pax_global_header00006660000000000000000000000064141153101220014500gustar00rootroot0000000000000052 comment=c4706aa764f3ae68258ba60be6325a5662900362 sparse-0.6.4/000077500000000000000000000000001411531012200130045ustar00rootroot00000000000000sparse-0.6.4/.gitignore000066400000000000000000000006511411531012200147760ustar00rootroot00000000000000# generic *.a *.o *.o.d *.pyc *.so *~ .*.swp # generated /version.h # programs /c2xml /compile /ctags /example /graph /obfuscate /scheck /semind /sparse /sparse-llvm /test-dissect /test-inspect /test-lexing /test-linearize /test-parsing /test-show-type /test-unssa # tags tags TAGS # stgit generated dirs patches-* # quilt's files patches series # local makefile local.mk .*.mk # cscope and Qt files cscope.out *.pro* sparse-0.6.4/Documentation/000077500000000000000000000000001411531012200156155ustar00rootroot00000000000000sparse-0.6.4/Documentation/.gitignore000066400000000000000000000000241411531012200176010ustar00rootroot00000000000000build dev-options.1 sparse-0.6.4/Documentation/IR.rst000066400000000000000000000251301411531012200166620ustar00rootroot00000000000000.. default-domain:: ir Intermediate Representation =========================== Instructions ~~~~~~~~~~~~ This document briefly describes which field of struct instruction is used by which operation. Some of those fields are used by almost all instructions, some others are specific to only one or a few instructions. The common ones are: * .src1, .src2, .src3: (pseudo_t) operands of binops or ternary ops. * .src: (pseudo_t) operand of unary ops (alias for .src1). * .target: (pseudo_t) result of unary, binary & ternary ops, is sometimes used otherwise by some others instructions. * .cond: (pseudo_t) input operands for condition (alias .src/.src1) * .type: (symbol*) usually the type of .result, sometimes of the operands Terminators ----------- .. op:: OP_RET Return from subroutine. * .src : returned value (NULL if void) * .type: type of .src .. op:: OP_BR Unconditional branch * .bb_true: destination basic block .. op:: OP_CBR Conditional branch * .cond: condition * .type: type of .cond, must be an integral type * .bb_true, .bb_false: destination basic blocks .. op:: OP_SWITCH Switch / multi-branch * .cond: condition * .type: type of .cond, must be an integral type * .multijmp_list: pairs of case-value - destination basic block .. op:: OP_UNREACH Mark code as unreachable .. op:: OP_COMPUTEDGOTO Computed goto / branch to register * .src: address to branch to (void*) * .multijmp_list: list of possible destination basic blocks Arithmetic binops ----------------- They all follow the same signature: * .src1, .src2: operands (types must be compatible with .target) * .target: result of the operation (must be an integral type) * .type: type of .target .. op:: OP_ADD Integer addition. .. op:: OP_SUB Integer subtraction. .. op:: OP_MUL Integer multiplication. .. op:: OP_DIVU Integer unsigned division. .. op:: OP_DIVS Integer signed division. .. op:: OP_MODU Integer unsigned remainder. .. op:: OP_MODS Integer signed remainder. .. op:: OP_SHL Shift left (integer only) .. op:: OP_LSR Logical Shift right (integer only) .. op:: OP_ASR Arithmetic Shift right (integer only) Floating-point binops --------------------- They all follow the same signature: * .src1, .src2: operands (types must be compatible with .target) * .target: result of the operation (must be a floating-point type) * .type: type of .target .. op:: OP_FADD Floating-point addition. .. op:: OP_FSUB Floating-point subtraction. .. op:: OP_FMUL Floating-point multiplication. .. op:: OP_FDIV Floating-point division. Logical ops ----------- They all follow the same signature: * .src1, .src2: operands (types must be compatible with .target) * .target: result of the operation * .type: type of .target, must be an integral type .. op:: OP_AND Logical AND .. op:: OP_OR Logical OR .. op:: OP_XOR Logical XOR Integer compares ---------------- They all have the following signature: * .src1, .src2: operands (types must be compatible) * .target: result of the operation (0/1 valued integer) * .type: type of .target, must be an integral type * .itype: type of the input operands .. op:: OP_SET_EQ Compare equal. .. op:: OP_SET_NE Compare not-equal. .. op:: OP_SET_LE Compare less-than-or-equal (signed). .. op:: OP_SET_GE Compare greater-than-or-equal (signed). .. op:: OP_SET_LT Compare less-than (signed). .. op:: OP_SET_GT Compare greater-than (signed). .. op:: OP_SET_B Compare less-than (unsigned). .. op:: OP_SET_A Compare greater-than (unsigned). .. op:: OP_SET_BE Compare less-than-or-equal (unsigned). .. op:: OP_SET_AE Compare greater-than-or-equal (unsigned). Floating-point compares ----------------------- They all have the same signature as the integer compares. The usual 6 operations exist in two versions: 'ordered' and 'unordered'. These operations first check if any operand is a NaN and if it is the case the ordered compares return false and then unordered return true, otherwise the result of the comparison, now guaranteed to be done on non-NaNs, is returned. .. op:: OP_FCMP_OEQ Floating-point compare ordered equal .. op:: OP_FCMP_ONE Floating-point compare ordered not-equal .. op:: OP_FCMP_OLE Floating-point compare ordered less-than-or-equal .. op:: OP_FCMP_OGE Floating-point compare ordered greater-or-equal .. op:: OP_FCMP_OLT Floating-point compare ordered less-than .. op:: OP_FCMP_OGT Floating-point compare ordered greater-than .. op:: OP_FCMP_UEQ Floating-point compare unordered equal .. op:: OP_FCMP_UNE Floating-point compare unordered not-equal .. op:: OP_FCMP_ULE Floating-point compare unordered less-than-or-equal .. op:: OP_FCMP_UGE Floating-point compare unordered greater-or-equal .. op:: OP_FCMP_ULT Floating-point compare unordered less-than .. op:: OP_FCMP_UGT Floating-point compare unordered greater-than .. op:: OP_FCMP_ORD Floating-point compare ordered: return true if both operands are ordered (none of the operands are a NaN) and false otherwise. .. op:: OP_FCMP_UNO Floating-point compare unordered: return false if no operands is ordered and true otherwise. Unary ops --------- .. op:: OP_NOT Logical not. * .src: operand (type must be compatible with .target) * .target: result of the operation * .type: type of .target, must be an integral type .. op:: OP_NEG Integer negation. * .src: operand (type must be compatible with .target) * .target: result of the operation (must be an integral type) * .type: type of .target .. op:: OP_FNEG Floating-point negation. * .src: operand (type must be compatible with .target) * .target: result of the operation (must be a floating-point type) * .type: type of .target .. op:: OP_SYMADDR Create a pseudo corresponding to the address of a symbol. * .src: input symbol (must be a PSEUDO_SYM) * .target: symbol's address .. op:: OP_COPY Copy (only needed after out-of-SSA). * .src: operand (type must be compatible with .target) * .target: result of the operation * .type: type of .target Type conversions ---------------- They all have the following signature: * .src: source value * .orig_type: type of .src * .target: result value * .type: type of .target Currently, a cast to a void pointer is treated like a cast to an unsigned integer of the same size. .. op:: OP_TRUNC Cast from integer to an integer of a smaller size. .. op:: OP_SEXT Cast from integer to an integer of a bigger size with sign extension. .. op:: OP_ZEXT Cast from integer to an integer of a bigger size with zero extension. .. op:: OP_UTPTR Cast from pointer-sized unsigned integer to pointer type. .. op:: OP_PTRTU Cast from pointer type to pointer-sized unsigned integer. .. op:: OP_PTRCAST Cast between pointers. .. op:: OP_FCVTU Conversion from float type to unsigned integer. .. op:: OP_FCVTS Conversion from float type to signed integer. .. op:: OP_UCVTF Conversion from unsigned integer to float type. .. op:: OP_SCVTF Conversion from signed integer to float type. .. op:: OP_FCVTF Conversion between float types. Ternary ops ----------- .. op:: OP_SEL * .src1: condition, must be of integral type * .src2, .src3: operands (types must be compatible with .target) * .target: result of the operation * .type: type of .target .. op:: OP_FMADD Fused multiply-add. * .src1, .src2, .src3: operands (types must be compatible with .target) * .target: result of the operation (must be a floating-point type) * .type: type of .target .. op:: OP_RANGE Range/bounds checking (only used for an unused sparse extension). * .src1: value to be checked * .src2, src3: bound of the value (must be constants?) * .type: type of .src[123]? Memory ops ---------- .. op:: OP_LOAD Load. * .src: base address to load from * .offset: address offset * .target: loaded value * .type: type of .target .. op:: OP_STORE Store. * .src: base address to store to * .offset: address offset * .target: value to be stored * .type: type of .target Others ------ .. op:: OP_SETFVAL Create a pseudo corresponding to a floating-point literal. * .fvalue: the literal's value (long double) * .target: the corresponding pseudo * .type: type of the literal & .target .. op:: OP_SETVAL Create a pseudo corresponding to a string literal. The value is given as an expression EXPR_STRING. * .val: (expression) input expression * .target: the resulting value * .type: type of .target, the value .. op:: OP_LABEL Create a pseudo corresponding to a label-as-value. * .bb_true: the BB corresponding to the label * .target: the resulting value * .type: type of .target (void \*) .. op:: OP_PHI Phi-node (for SSA form). * .phi_list: phi-operands (type must be compatible with .target) * .target: "result" * .type: type of .target .. op:: OP_PHISOURCE Phi-node source. Like OP_COPY but exclusively used to give a defining instructions (and thus also a type) to *all* OP_PHI operands. * .phi_src: operand (type must be compatible with .target, alias .src) * .target: the "result" PSEUDO_PHI * .type: type of .target * .phi_node: the unique phi instruction using the target pseudo .. op:: OP_CALL Function call. * .func: (pseudo_t) the function (can be a symbol or a "register", alias .src)) * .arguments: (pseudo_list) list of the associated arguments * .target: function return value (if any) * .type: type of .target * .fntypes: (symbol_list) list of the function's types: the first entry is the full function type, the next ones are the type of each arguments .. op:: OP_INLINED_CALL Only used as an annotation to show that the instructions just above correspond to a function that have been inlined. * .func: (pseudo_t) the function (must be a symbol, alias .src)) * .arguments: list of pseudos that where the function's arguments * .target: function return value (if any) * .type: type of .target .. op:: OP_SLICE Extract a "slice" from an aggregate. * .base: (pseudo_t) aggregate (alias .src) * .from: offset of the "slice" within the aggregate * .target: result * .type: type of .target .. op:: OP_ASM Inlined assembly code. * .string: asm template * .asm_rules: asm constraints, rules Sparse tagging (line numbers, context, whatever) ------------------------------------------------ .. op:: OP_CONTEXT Currently only used for lock/unlock tracking. * .context_expr: unused * .increment: (1 for locking, -1 for unlocking) * .check: (ignore the instruction if 0) Misc ops -------- .. op:: OP_ENTRY Function entry point (no associated semantic). .. op:: OP_BADOP Invalid operation (should never be generated). .. op:: OP_NOP No-op (should never be generated). .. op:: OP_DEATHNOTE Annotation telling the pseudo will be death after the next instruction (other than some other annotation, that is). .. # vim: tabstop=4 sparse-0.6.4/Documentation/Makefile000066400000000000000000000011261411531012200172550ustar00rootroot00000000000000# Minimal makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = -a SPHINXBUILD = sphinx-build SPHINXPROJ = sparse SOURCEDIR = . BUILDDIR = build targets := help targets += html targets += man # Put it first so that "make" without argument is like "make help". help: # route all targets to Sphinx using the new "make mode" option. $(targets): conf.py Makefile @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) %.1: %.rst man @mv build/man/$@ $@ .PHONY: Makefile # avoid circular deps with the catch-all rule sparse-0.6.4/Documentation/TODO.md000066400000000000000000000077351411531012200167200ustar00rootroot00000000000000TODO ==== Essential --------- * SSA is broken by simplify_loads() & branches rewriting/simplification * add support for bitwise enums (wip) Documentation ------------- * document the API * document the limitations of modifying ptrlists during list walking * document the data structures * document flow of data / architecture / code structure Core ---- * if a variable has its address taken but in an unreachable BB then its MOD_ADDRESSABLE may be wrong and it won't be SSA converted. - let kill_insn() check killing of SYMADDR, - add the sym into a list and - recalculate the addressability before memops's SSA conversion * bool_ctype should be split into internal 1-bit / external 8-bit Testsuite --------- * there are 60 failing tests. They should be fixed (but most are non-trivial to fix). Misc ---- * GCC's -Wenum-compare / clangs's -Wenum-conversion -Wassign-enum * parse __attribute_((fallthrough)) * add support for format(printf()) (WIP by Ben Dooks) * make use of UNDEFs (issues warnings, simplification, ... ?) * make memory accesses more explicit: add EXPR_ACCESS (wip) * it would be nice to do our own parsing of floating point (wip) * some header files needed for crypto/ need __vector or __fp16 * some even need __complex Optimization ------------ * a lot of small simplifications are waiting to be upstreamed * the domtree need to be rebuilt (or updated) * critical edges need to be split * the current way of doing CSE uses a lot of time * add SSA based DCE * add SSA based PRE * Add SSA based SCCP * add a pass to inline small functions during simplification. * use better/more systematic use of internal verification framework * tracking of operands size should be improved (WIP) * OP_INLINE is sometimes in the way * would be nice to strictly separate phases that don't changes the CFG and thus the dominance tree. IR -- * pseudos are untyped, it's usually OK but often it complicates things: - PSEUDO_REGs are defined by instructions and their type is normally retrievable via this defining instruction but in some cases they're not: for example, pseudos defined by ASM output. - PSEUDO_ARGs are considered as defined by OP_ENTRY and are used like this for liveness trackability but their type can't simply be retrieved via this instruction like PSEUDO_REGs are (with ->def->type). - PSEUDO_VALs are completely typeless. Maybe a few bits should be used to store some kind of low-level type. * OP_SET should return a bool, always * add IR instructions for va_arg() & friends * add a possibility to import of file in "IR assembly" * dump the symtable * dump the CFG LLVM ---- * fix ... Internal backends ----------------- * it would be nice the upstream the code generator * add a pass to transform 3-addresses code to 2-addresses * add some basic register allocation * add a pass to order the BBs and changes 2-ways CBR into one-way branches * what can be done for x86? * add support to add constraints in the MD rules Longer term/to investigate -------------------------- * attributes are represented as ctypes's alignment, modifiers & contexts but plenty of attributes doesn't fit, for example they need arguments. * format(printf, ...), * section("...") * assume_aligned(alignment[, offsert]) * error("message"), warning("message") * ... * should support "-Werror=..." ? * All warning messages should include the option how to disable it. For example: "warning: Variable length array is used." should be something like: "warning: Variable length array is used. (-Wno-vla)" * ptrlists must not have elements removed while being iterated; this should somehow be enforced. * having 'struct symbol' used to represent symbols *and* types is quite handy but it also creates lots of problems and complications * Possible mixup of symbol for a function designator being not a pointer? This seems to make evaluation of function pointers much more complex than needed. * extend test-inspect to inspect more AST fields. * extend test-inspect to inspect instructions. sparse-0.6.4/Documentation/annotations.rst000066400000000000000000000073531411531012200207140ustar00rootroot00000000000000Annotations =========== Sparse extends C's type system with a number of extra type qualifiers which add restrictions on what you can do on objects annotated with them. These qualifiers are specified with GCC's ``__attribute__`` syntax. address_space(*name*) --------------------- This attribute is to be used on pointers to specify that its target is in address space *name* (an identifier or a constant integer). Sparse treats pointers with different address spaces as distinct types and will warn on casts (implicit or explicit) mixing the address spaces. An exception to this is when the destination type is ``uintptr_t`` or ``unsigned long`` since the resulting integer value is independent of the address space and can't be dereferenced without first casting it back to a pointer type. bitwise ------- This attribute is to be used to define new, unique integer types that cannot be mixed with other types. In particular, you can't mix a "bitwise" integer with a normal integer expression, and you can't even mix it with another bitwise expression of a different type. The integer 0 is special, though, and can be mixed with any bitwise type since it's safe for all bitwise operations. Since this qualifier defines new types, it only makes sense to use it in typedefs, which effectively makes each of these typedefs a single "bitwise class", incompatible with any other types. context(*ctxt*, *entry*, *exit*) -------------------------------- This attribute is to be used on function declarations to specify the function's entry and exit count for a given context. This context can be pretty much anything that can be counted. Sparse will check that the function's entry and exit contexts match, and that no path through a function is ever entered with conflicting contexts. In particular, it is designed for doing things like matching up a "lock" with the pairing "unlock". For example, a function doing a lock should be annotated with an entry value of 0 and an exit value of 1, the corresponding unlock function should use the values 1 and 0, and a function that should only be called on some locked data, release the lock but which doesn't exit without reacquiring the lock being, should use entry and exit values of 1. The first argument, *ctxt*, is an expression only used as documentation to identify the context. Usually, what is used is a pointer to the structure containing the context, for example, the structure protected by the lock. See also https://lwn.net/Articles/109066/. noderef ------- This attribute is to be used on a r-value to specify it cannot be dereferenced. A pointer so annotated is in all other aspects exactly like a pointer but trying to actually access anything through it will cause a warning. nocast ------ This attribute is similar to ``bitwise`` but in a much weaker form. It warns about explicit or implicit casting to different types. However, it doesn't warn about the mixing with other types and it easily gets lost: you can add plain integers to __nocast integer types and the result will be plain integers. So, it ends 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. For example, a 64-bit integer that you don't want to mistakenly/silently be returned as int. See also `Linus' e-mail about __nocast vs __bitwise `_. safe ---- This attribute specifies that the object, which should be a pointer, is defined to never be NULL or nontrapping. It causes a warning if the object is tested in a conditional. force ----- This attribute is to be used in casts to suppress warnings that would otherwise be caused by the presence of one of these extra qualifiers. sparse-0.6.4/Documentation/api.rst000066400000000000000000000005301411531012200171160ustar00rootroot00000000000000Sparse API ========== .. contents:: :local: :depth: 2 Utilities ~~~~~~~~~ .. c:autodoc:: ptrlist.c .. c:autodoc:: utils.h .. c:autodoc:: flowgraph.h Parsing ~~~~~~~ .. c:autodoc:: expression.h Typing ~~~~~~ .. c:autodoc:: evaluate.h Optimization ~~~~~~~~~~~~ .. c:autodoc:: optimize.c .. c:autodoc:: flow.c .. c:autodoc:: simplify.c sparse-0.6.4/Documentation/conf.py000066400000000000000000000125051411531012200171170ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. # If extensions (or modules to document with autodoc) are in another # directory, add these directories to sys.path here. If the directory # is relative to the documentation root, use os.path.abspath to make # it absolute, like shown here. # sys.path.insert(0, os.path.abspath('.')) import os import sys import datetime # -- General configuration ------------------------------------------------ needs_sphinx = '1.7' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. sys.path.insert(0, os.path.abspath('sphinx')) extensions = [ 'cdoc' , 'ir' ] # support .md with python2 & python3 if sys.version_info[0] > 2: from recommonmark.parser import CommonMarkParser source_parsers = { '.md': CommonMarkParser, } else: source_parsers = { '.md': 'recommonmark.parser.CommonMarkParser', } # Add any paths that contain templates here, relative to this directory. templates_path = ['templates'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # source_suffix = ['.rst', '.md'] # The master toctree document. master_doc = 'index' # General information about the project. project = 'sparse' copyright = '2003 - ' + str(datetime.datetime.now().year) author = "sparse's development community" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The full version, including alpha/beta/rc tags. release = next(open('../Makefile', 'r')).split('=')[1].rstrip() # The short X.Y version. version = release.split('-')[0] # it's a C project, so: primary_domain = 'c' # disable syntax highlight in non-code sections highlight_language = 'none' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = None # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path exclude_patterns = ['build'] # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = True # -- Options for cdoc extension ------------------------------------------- cdoc_srcdir = '..' # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # try: html_theme = 'sphinx_rtd_theme' import sphinx_rtd_theme html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] except: sys.stderr.write("Warning: theme '%s' not found\n" % html_theme) html_theme = 'classic' # html_theme_options = {} # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['sphinx/static'] html_context = { 'css_files': [ '_static/theme_overrides.css', ], } # Custom sidebar templates, must be a dictionary that maps document names # to template names. # # This is required for the alabaster theme # refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars html_sidebars = { } html_logo = 'logo.svg' # -- Options for HTMLHelp output ------------------------------------------ # Output file base name for HTML help builder. htmlhelp_basename = 'sparsedoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # 'papersize': 'a4paper', # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # # 'preamble': '', # Latex figure (float) alignment # # 'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ ('index', 'sparse.tex', u'sparse Documentation', author, 'manual'), ] # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('dev-options', 'dev-options', u'options for development', [author], 1), ] # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'sparse', u'sparse Documentation', author, 'sparse', 'C semantic parser & checker', 'Software development'), ] # vim: tabstop=4 sparse-0.6.4/Documentation/data-structures.txt000066400000000000000000000103431411531012200215110ustar00rootroot00000000000000- A slightly edited irc discussion with Josh Triplett. - Describes most data structures used in sparse. 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.6.4/Documentation/dev-options.rst000066400000000000000000000022061411531012200206160ustar00rootroot00000000000000Extra options for developers ============================ SYNOPSIS -------- ``tools`` [`options`]... `file.c`` DESCRIPTION ----------- This file is a complement of sparse's man page meant to document options only useful for development on sparse itself. OPTIONS ------- .. option:: -fdump-ir=pass,[pass] Dump the IR at each of the given passes. The passes currently understood are: * ``linearize`` * ``mem2reg`` * ``final`` The default pass is ``linearize``. .. option:: -f[-disable|-enable|=last] If ``=last`` is used, all passes after the specified one are disabled. By default all passes are enabled. The passes currently understood are: * ``linearize`` (can't be disabled) * ``mem2reg`` * ``optim`` .. option:: -vcompound Print all compound global data symbols with their sizes and alignment. .. option:: -vdead Add ``OP_DEATHNOTE`` annotations to dead pseudos. .. option:: -vdomtree Dump the dominance tree after its calculation. .. option:: -ventry Dump the IR after all optimization passes. .. option:: -vpostorder Dump the reverse postorder traversal of the CFG. sparse-0.6.4/Documentation/doc-guide.rst000066400000000000000000000064521411531012200202160ustar00rootroot00000000000000Documentation guide =================== Introduction ------------ The documentation for Sparse is written in plain text augmented with either `reStructuredText`_ (.rst) or `MarkDown`_ (.md) markup. These files can be organized hierarchically, allowing a good structuring of the documentation. Sparse uses `Sphinx`_ to format this documentation in several formats, like HTML or PDF. All documentation related files are in the ``Documentation/`` directory. In this directory you can use ``make html`` or ``make man`` to generate the documentation in HTML or manpage format. The generated files can then be found in the ``build/`` sub-directory. The root of the documentation is the file ``index.rst`` which mainly lists the names of the files with real documentation. .. _Sphinx: http://www.sphinx-doc.org/ .. _reStructuredText: http://docutils.sourceforge.net/rst.html .. _MarkDown: https://en.wikipedia.org/wiki/Markdown .. _rest-markup: Minimal reST cheatsheet ----------------------- Basic inline markup is: * ``*italic*`` gives *italic* * ``**bold**`` gives **bold** * ````mono```` gives ``mono`` Headings are created by underlining the title with a punctuation character; it can also be optionally overlined:: ############# Major heading ############# Minor heading ------------- Any punctuation character can be used and the levels are automatically determined from their nesting. However, the convention is to use: * ``#`` with overline for parts * ``*`` with overline for chapters * ``=`` for sections * ``-`` for subsections * ``^`` for subsubsections Lists can be created like this:: * this is a bulleted list * with the second item on two lines * nested lists are supported * subitem * another subitem * and here is the fourth item #. this is an auto-numbered list #. with two items 1. this is an explicitly numbered list 2. with two items Definition lists are created with a simple indentation, like:: term, concept, whatever Definition, must be indented and continue here. It can also have several paragraphs. Literal blocks are introduced with ``::``, either at the end of the preceding paragraph or on its own line, and indented text:: This is a paragraph introducing a literal block:: This is the literal block. It can span several lines. It can also consist of several paragraphs. Code examples with syntax highlighting use the *code-block* directive. For example:: .. code-block:: c int foo(int a) { return a + 1; } will give: .. code-block:: c int foo(int a) { return a + 1; } Autodoc ------- .. highlight:: none .. c:autodoc:: Documentation/sphinx/cdoc.py For example, a doc-block like:: /// // increment a value // // @val: the value to increment // @return: the incremented value // // This function is to be used to increment a // value. // // It's strongly encouraged to use this // function instead of open coding a simple // ``++``. int inc(int val) will be displayed like this: .. c:function:: int inc(int val) :noindexentry: :param val: the value to increment :return: the incremented value This function is to be used to increment a value. It's strongly encouraged to use this function instead of open coding a simple ``++``. Intermediate Representation --------------------------- .. c:autodoc:: Documentation/sphinx/ir.py sparse-0.6.4/Documentation/index.rst000066400000000000000000000054011411531012200174560ustar00rootroot00000000000000.. sparse documentation master file. Welcome to sparse's documentation ================================= .. toctree:: :maxdepth: 1 About Sparse ------------ Sparse, the semantic parser, provides a compiler frontend capable of parsing most of ANSI C as well as many GCC extensions, and a collection of sample compiler backends, including a static analyzer also called `sparse`. Sparse provides a set of annotations designed to convey semantic information about types, such as what address space pointers point to, or what locks function acquires or releases. Linus Torvalds started writing Sparse in 2003, initially targeting issues such as mixing pointers to user address space and pointers to kernel address space. Josh Triplett was Sparse's first maintainer in 2006. This role was taken over by Christopher Li in 2009 and by Luc Van Oostenryck in late 2018. Getting Sparse -------------- The most recent version can be obtained directly from the Git repository with the command:: git clone git://git.kernel.org/pub/scm/devel/sparse/sparse.git You can also `browse the Git repository `_ or use the mirror at https://github.com/lucvoo/sparse. The tarballs of released versions of Sparse and their signatures can be found at https://www.kernel.org/pub/software/devel/sparse/dist/. Once you have the sources, to build Sparse and install it in your ~/bin directory, just do:: cd sparse make make install To install it in another directory, use:: make PREFIX= install Contributing and reporting bugs ------------------------------- Submission of patches and reporting of bugs, as well as discussions related to Sparse, should be done via the mailing list: linux-sparse@vger.kernel.org. You do not have to be subscribed to the list to send a message there. Previous discussions and bug reports are available on the list archives at https://marc.info/?l=linux-sparse. To subscribe to the list, send an email with ``subscribe linux-sparse`` in the body to ``majordomo@vger.kernel.org``. Bugs can also be reported and tracked via the `Linux kernel's bugzilla for sparse `_. .. toctree:: :caption: User Documentation :maxdepth: 1 annotations Some interesting external documentation: * `Sparse: a look under the hood `_ * `Sparse: a short overview `_ .. toctree:: :caption: Development :maxdepth: 1 submitting-patches types api dev-options IR test-suite doc-guide TODO .. toctree:: :caption: Release Notes :maxdepth: 1 release-notes/index Indices and tables ================== * :ref:`genindex` sparse-0.6.4/Documentation/logo.svg000066400000000000000000000133731411531012200173050ustar00rootroot00000000000000 image/svg+xml Layer 1 sparse-0.6.4/Documentation/release-notes/000077500000000000000000000000001411531012200203635ustar00rootroot00000000000000sparse-0.6.4/Documentation/release-notes/index.rst000066400000000000000000000003571411531012200222310ustar00rootroot00000000000000************* Release Notes ************* .. toctree:: :maxdepth: 1 v0.6.4 v0.6.3 v0.6.2 v0.6.1 v0.6.0 v0.5.2 v0.5.1 v0.5.0 v0.4.5-rc1 v0.4.4 v0.4.3 v0.4.2 v0.4.1 v0.4 v0.3 v0.2 v0.1 sparse-0.6.4/Documentation/release-notes/v0.1.rst000066400000000000000000000046521411531012200216100ustar00rootroot00000000000000v0.1 (2006-11-06) ================= I have tagged and tarballed a 0.1 release of Sparse, now available from http://ftp.be.debian.org/pub/software/devel/sparse/dist/, with sha1sum 9e0a4d5abb8e8a4be4cf8d9fe632c69dbec3e242. As discussed in [http://marc.info/?l=linux-sparse&m=116231992212971 Re: Official releases?], I've taken maintainership of sparse. Thanks to Linus Torvalds for his previous maintainership. As a result, this release comes from my sparse Git repository. You can find more information about obtaining sparse via Git at the [https://www.kernel.org/pub/linux/kernel/people/josh/sparse/ new sparse homepage]. In addition to all the work in the [https://www.kernel.org/pub/scm/devel/sparse/sparse.git/ previous Sparse repository], this release includes the following changes: Adam DiCarlo (1): * Add type information to enum mismatch warning Al Viro (2): * added a bunch of gcc builtins * switch to hash-based get_one_special() Josh Triplett (15): * "Initializer entry defined twice" should not trigger with zero-size fields * Fix incorrect symbol in comment on #endif for multiple-inclusion guard * Add -Wno-uninitialized * graph: Show position in basic block nodes * bb_terminated: Use boundary values rather than specific opcodes * Turn on -Wcontext by default * Merge branch 'fix-defined-twice-error-on-empty-struct' into staging * Merge branch 'graph' into staging * merge branch 'more-warning-flags' into staging and fix conflicts * merge branch 'no-semantic-h' into staging and fix conflicts * Merge branch 'Wcontext-default' into staging * Add test cases to validation/context.c for the Linux __cond_lock macro * Merge branch 'context-test-cases-for-cond-lock' into josh * Rename test case bad-assignement.c to bad-assignment.c, fixing the typo. * Stop building and installing libsparse.so Josh Triplett and Pavel Roskin (1): * Recognize and ignore __alias__ and __visibility__ Pavel Roskin (4): * Compile sparse executable under it's own name, not as "check" * Add support for __builtin_strpbrk() * Typo fixes * Install cgcc on "make install", refactor installation code Known issue with this release: * Sparse does not produce the expected set of warnings for several of the validation programs, included in the sparse source in the directory 'validation/'. Some scripts should provoke warnings but don't, and others provoke warnings they shouldn't. -- Josh Triplett sparse-0.6.4/Documentation/release-notes/v0.2.rst000066400000000000000000000031121411531012200215770ustar00rootroot00000000000000v0.2 (2006-12-05) ================= I have tagged and tarballed a 0.2 release of Sparse, now available from http://ftp.be.debian.org/pub/software/devel/sparse/dist/, with sha1sum 1762fc609fe436e74b87356a52690b5f7bb40c81. In addition to plenty of bug fixes, this release includes several notable new features: * -Wall, thanks to Pavel Roskin * '#strong_define' and '#strong_undef', thanks to Oleg Nesterov * Argument parsing functions no longer mangle the argv passed to them, thanks to Christopher Li * static library and header files now installed, along with a pkg-config file to find them * Makefile now supports DESTDIR, useful for packagers Full changelog: Christopher Li (4): * trivial fix for seg fault. * Fix warning on self check. * delay removing file scope * cleanup write to argument array hack Damien Lespiau (1): * trivial: more .gitignore stuff Josh Triplett (5): * Update the FAQ: add sparse website and gitweb, update git URL, remove old BK url * Rename "check.c" to "sparse.c" to match program name; update .gitignore * Install static library and header files * Generate and install a pkg-config file. Add DESTDIR support to Makefile. * Remove old SCCS target from Makefile. Nicolas Kaiser (1): * double inclusions Oleg Nesterov (7): * use lookup_macro() in handle_undef() * kill NS_INVISIBLEMACRO, introduce NS_UNDEF * fix redefine of #weak_define * fix 'weak' attribute loss * prepare for #strong_{define,undef} * implement #strong_define * implement #strong_undef Pavel Roskin (1): * Support -Wall flag -- Josh Triplett sparse-0.6.4/Documentation/release-notes/v0.3.rst000066400000000000000000000124051411531012200216050ustar00rootroot00000000000000v0.3 (2007-05-01) ================= I have tagged and tarballed a 0.3 release of Sparse, now available from http://ftp.be.debian.org/pub/software/devel/sparse/dist/ with sha1sum 1d868b29234176abd5f3f5463aad1f11d5268dc2. Note that the Sparse Git repository has moved to: https://git.kernel.org/cgit/devel/sparse/sparse.git The old repository location will continue to work for now, but please update any references you have to the old location. Thanks to Christopher Li for contributing heavily to this release, including several notable new features. See the full changelog for details. In addition to numerous bug fixes, cleanups, and new test cases, this release includes several new visible features: * A Sparse-based implementation of ctags, thanks to Christopher Li. Now you can have a ctags that 'actually parses C'. * Sparse now correctly supports annotations on inline functions. * The new '-ventry' option will make Sparse dump its internal linearized bytecode format. * Sparse now parses the '__regparm__' attribute just like 'regparm', fixing a warning with pthreads from glibc 2.5. * Sparse now interprets the integer size attributes 'QI', 'SI', 'HI', and 'DI', and their double-underscore variants. * Sparse now supports attributes on labels, sometimes used to specify 'unused' labels. * Sparse now handles structure attributes between the structure keyword and the name, fixing many parse errors. * Sparse now supports more than one command-line include file. * The pkg-config file gets installed to its correct location under 'lib', rather than under 'share'. Notable internal changes: * Fully table-driven attribute parsing. This simplifies the C parser and makes it far easier to add new attributes to Sparse. * Many internal cleanups, more information preserved for backends, more useful formats for that information, and lower memory usage. * Fixed Sparse warnings about itself. Full changelog: Christopher Li (24): * Sparse-based Ctags implementation * Change the symbol access list to a pseudo list * Add instruction to pseudo user tracking. * Update usage chain for dead instructions * Update usage chain for dead branch instruction. * Allow more than one command line include file. * Enhance debug information. * Another attempt to fix the attribute parsing. * Marking anonymous string. * Bug fix in pointer modifier inheritance at function degeneration. * Handle structure attributes between the structure keyword and the name * Fix the segfault when initializer has unknown symbol * Fix double semicolon in struct declaration * Make the ptrlist using the sparse allocator. * Fix core dump on anonymous symbol. * Fix a bug that match_idents forget to end with NULL * Adding debug option for showing the linearized instruction. * Fix core dump on huge switch * Introduce expression_error * Disable liveness "dead" instruction by default. * Add annotation for inline function call. * Introduce keyword driven attribute parsing * Fix the annotated inline call position * handle label attributes Christopher Li and Josh Triplett (4): * Introduce top level parsing for asm parsing. * Introducing statement keywords * Free up some special bits in modifiers. * Moving statement parsing into smaller functions. James Westby (2): * Fix mistaken comparison that becomes a no-op. * Update the information in README about using the library. Josh Triplett (30): * Add ctags to .gitignore * Add a return in the last case of a switch; redundant but less error-prone. * Coding style fix: in a pointer type, * goes with the name, not the type. * Add missing #include "allocate.h" in linearize.h for DECLARE_ALLOCATOR. * Add test case for function pointer modifier inheritance * Add test case for structure attribute placement. * Add test case for double semicolon in structure declaration. * Coding style fix: use parentheses with sizeof * Move pkg-config file to lib, rather than share * Add static to declarations in test cases, to remove unrelated warnings. * Fix typo in symbol.h: s/keywrods/keywords/ * Fix typos in comments * Use GCC format and sentinel attributes on appropriate functions * Fix two potential NULL pointer dereferences in dissect.c * Avoid returning an uninitialized pointer from dup_list of an empty list * Remove stray space from expand_compare in expand.c * Prevent potential NULL pointer dereference in expand_compare * Add test case for basic address_space annotations. * Use noreturn on die() and error_die() * Parse and ignore the __regparm__ attribute, just like regparm. * Fix comment to reference #weak_define rather than #ifndef, matching code * Teach cgcc about -Wtransparent-union and -Wno-transparent-union * Declare die_if_error extern in lib.h * Remove unused variable "include" from lib.c * Declare do_error static * Declare gcc_patchlevel extern in lib.h * compile-i386.c: Declare regs_in_use static * simplify.c: Declare delete_pseudo_user_list_entry static * linearize: DECLARE_ALLOCATOR for asm_constraint and asm_rules * Fix most -Wshadow warnings in Sparse. Oleg Nesterov (3): * dissect: cleanup report_implicit() * dissect: fix multidimensional array initializer * dissect: simplify lookup_member() -- Josh Triplett sparse-0.6.4/Documentation/release-notes/v0.4.1.rst000066400000000000000000000014751411531012200217520ustar00rootroot00000000000000v0.4.1 (2007-11-13) =================== I have tagged and tarballed Sparse 0.4.1, now available from http://ftp.be.debian.org/pub/software/devel/sparse/dist/ with sha1sum 14085c5317cd7f2c8392fb762969906fa91888ef. This bugfix release fixes a Sparse assertion which recent Linux kernels started triggering, along with a few other fixes. Full changelog: Christopher Li (1): * Perform local label lookup Emil Medve (1): * Handle ignored attribute malloc Josh Triplett (4): * Add comment on taint flags enum referencing expr->taint * Add test-suite metadata to validation/local-label.c * Add known-to-fail test case for a static forward declaration * Makefile: VERSION=0.4.1 Mike Frysinger (1): * fix install perms of manpages Tilman Sauerbeck (1): * Added a prototype for mempcpy(). -- Josh Triplett sparse-0.6.4/Documentation/release-notes/v0.4.2.rst000066400000000000000000000146351411531012200217550ustar00rootroot00000000000000v0.4.2 (2009-10-16) =================== I have tagged and released the sparse version 0.4.2 at http://ftp.be.debian.org/pub/software/devel/sparse/dist/ with sha1sum a2adef3f78c7409e8c0bb80941f473d775afcac4 As previously discussed on the sparse mailing list, I am the new maintainer of the sparse project. This is my first release for sparse. Thanks Josh Triplett for the previous maintenance of the project. I also created a new sparse wiki, it will replace the current sparse home page. https://sparse.wiki.kernel.org/ A lot of bug fixes and enhancements have gone into this release. Special thanks to Al Viro for overhauling the parser. Now sparse has better ctype and attribute handling. The detailed changes follow. Al Viro (39): * saner warnings for restricted types * fix show_typename() * catch !x & y brainos * fun with declarations and definitions * Fix type_info_expression() * fun with declarations and definitions * Fix handling of ident-less declarations * Separate parsing of identifier-list (in K&R-style declarations) * More nested declarator fixes * Fix attribute/asm handling * more direct_declarator() sanitizing * Warn about non-empty identifier list outside of definition * Apply attributes after ( to the right place * Leave applying attributes until we know whether it's a nested declarator * Don't mess with passing symbol to declarator/direct_declarator * Fix braino in which_kind() * Sanitize direct_declarator logics * Separating ctype and parser state, part 1 * Propagate decl_state to declaration_specifiers() * Fix regression created by commit af30c6df74f01db10fa78ac0cbdb5c3c40b5c73f * Take the rest of storage class keywords to parse.c * Fix handling of typedefs with several declarators * preparations to ->declarator() cleanup - separate typedef handling * Take the rest of specifiers to parse.c * Saner type for __builtin_va_list * Rewrite and fix specifiers handling * Have ->declarator() act directly on ctype being affected * Clean up and split declaration_specifiers() * Pass decl_state down to ->declarator() and handle_attributes() * Pass decl_state down to ->attribute() * Restore __attribute__((mode)) handling * Fix enumeration constants' scope beginning * Fix declaration_specifiers() handling of typedef name shadowed by NS_SYMBOL * Fix __label__ handling * Simplify get_number_value() and ctype_integer() * Don't mix storage class bits with ctype->modifiers while parsing type * Sanitize pointer() * Segfault at evaluate.c:341 * warn directive in argument list Alberto Bertogli (1): * Support the __thread storage class Alexander Shishkin (1): * don't call sparse when called to generate dependencies Alexey Zaytsev (16): * Remove symbol.id_list * Replace the -specs cgcc option with -target * Make show_symbol newline-consistent * Handle a terminal -o option properly. * Looks more evident this way. * Mark handle_switch as static and don't export it from lib.h * Handle missing argument to -D. * Gdb macros to get a better look at some sparse data structures. * A slightly edited irc discussion with Josh Triplett. * Warning should be enough for an unhandled transparent union * Set gcc include path at runtime. * Let cgcc pass -gcc-base-dir to sparse. * Document -gcc-base-dir in sparse.1 * Rename dirafter to idirafter. * Let void have sizeof 1 * Also warn about sizeof(function) Blue Swirl (6): * Sparc64 (Sparc V9, LP64) support * OpenBSD support * Ignore attribute __bounded__, used by OpenBSD headers. * Add c{l,t}z{,l,ll}, ffsl{,l}, popcountll and floating point comparison builtins. * Add support for TImode type (__int128_t) * Define __LP64__ for x86_64 unless in 32 bit mode Christopher Li (11): * Evaluate iterator symbols * Remove pre_buffer * Add enum member list to the parent * Teach classify_type to handle typeof * Warn about explicit usage of sizeof(void) * Makefile automatic header dependency * Clean up Makefile long lines * Update the validation check for ftabstop= * Add validation for restrict and attribute warning * move extern inline function to file scope * Sparse 0.4.2 David Given (2): * Unhardcode byte size being 8 bits. * Add type information to struct instruction. Geoff Johnstone (4): * Add support for GCC's -std=... and -ansi command line options. * Add builtin functions for use with __FORTIFY_SOURCE * Fix type mismatches with incomplete types * Add -Wno-declaration-after-statement Hannes Eder (4): * Add -ftabstop=WIDTH * refactor handle_switch_f * test-suite: be more verbose on 'unhandled' and 'known to fail' tests * test-suite: integrate unhandled proprocessor tests Johannes Berg (8): * cgcc: handle ppc arch * make sparse keep its promise about context tracking * sparse test suite: add test mixing __context__ and __attribute__((context(...))) * sparse: simple conditional context tracking * inlined call bugfix & test * improve -Wcontext code and messages * fix bug in context tracking code * Revert the context tracking code Josh Triplett (2): * Add test case for new warning about !x & y * Expand "dubious !x & y" handling to other combinations of !, &, and \|. Kamil Dudka (4): * compile-i386: do not generate an infinite loop * linearize.h: sanitize header * unssa: track uses when replacing a phi node * make sparse headers self-compilable... Linus Torvalds (5): * Fix cast instruction generation * Simplify (and warn about) right shifts that result in zero * Allow array declarators to have 'restrict' in them * Turn off '-Wtransparent-union' by default * Avoid "attribute 'warning': unknown attribute" warning Martin Nagy (3): * .gitignore: Ignore dependencies and Vim swap files * Add missing checks for Waddress-space * Print an error if typeof() lacks an argument Pavel Roskin (1): * Ignore "cold" and "hot" attributes, which appeared in gcc 4.3 Pekka Enberg (1): * sparse: Add GCC pre-defined macros for user-space Ramsay Jones (1): * Makefile: suppress error message from pkg-config Reinhard Tartler (1): * show_token: handle TOKEN_UNTAINT and TOKEN_ARG_COUNT types Samuel Bronson (1): * Have Makefile import local.mk if it exists. Thomas Schmid (1): * Fix implicit cast to float Vegard Nossum (2): * Fix use of invalid file descriptor * Set \*tree to NULL on error -- Chris Li sparse-0.6.4/Documentation/release-notes/v0.4.3.rst000066400000000000000000000045031411531012200217470ustar00rootroot00000000000000v0.4.3 (2010-11-02) =================== Hi, It is final there. The sparse version 0.4.3 is released. Mostly small fix up. It can parse the recent kernel better, less noise. For people interested in the sparse internals, there is a sparse inspecting tools now. Currently it has limited knowledge of AST. It is very easy to extent though. Thanks every one for the contribution. Chris -- Bernd Petrovitsch (1): * Fix a typo - "typdef" is neither C nor plain English Christopher (3): * evaluate: check for NULL type inside typeof * Add test case for builtin_unreachable() * inspect: add some expression inspection Christopher Li (15): * Make MOD_NORETURN fits into 32 bit * Move noreturn attribute out of ignore attr area * Declare ignored attributres into a list of string. * Simplify Makefile using static pattern rules * Adding test case for "x && y && z" . * Pointer don't inherent the alignment from base type * Allow parsing L'\0' * Parsing wide char string * Adding asm goto label test case * inspect: add custom ast treeview model * inspect: add some example inspect for symbol and statement * inspect: Add test-inspect program * inspect: cast expression * Fixup and cleanup modifier_string() function. * sparse 0.4.3 finial Damien Lespiau (1): * Ignore the may_alias GCC attribute Dan Carpenter (1): * add test-inspect to .gitignore Dan McGee (1): * Makefile: fix permissions mixup on install Daniel De Graaf (1): * Fix incorrect linearization of "x && y && z" Jiri Slaby (3): * parser: add support for asm goto * parser: fix and simplify support of asm goto * parser: define __builtin_unreachable Joel Soete (1): * possible fix to cgcc issue in sparse 0.4.2: Josh Triplett (2): * Rename -Wall to Wsparse-all, so it doesn't get turned on unintentionally * New attribute designated_init: mark a struct as requiring designated init Kamil Dudka (1): * do not ignore attribute 'noreturn'... Michael Buesch (2): * ignore attributes "externally_visible" and "signal" * Ignore "naked" attribute Michael Stefaniuc (3): * Ignore the ms_abi/sysv_abi attributes. * Ignore the alloc_size attribute. * Handle __builtin_ms_va_list. Mike Frysinger (1): * parser: add Blackfin gcc info Morten Welinder (1): * skip may_alias and declare builtin_fabs sparse-0.6.4/Documentation/release-notes/v0.4.4.rst000066400000000000000000000053631411531012200217550ustar00rootroot00000000000000v0.4.4 (2011-11-25) =================== This is a long due release. The sparse 0.4.4 can be downloaded from: https://www.kernel.org/pub/software/devel/sparse/dist/ The new sparse has a lot of bugs fixes. It will report less noise while checking the new kernel tree. It compiles better with the new gcc as well. The sparse project is in the process of moving to the MIT license. Dan is coordinating the efforts. Most sparse developers sign off the MIT license already. If you haven't done so, please contact Dan off the list regarding the new license. Here is the short summery of the changes in this release. Have a nice long week end. Chris Ben Pfaff (1): * evaluate: Allow sizeof(_Bool) to succeed. Christopher Li (13): * inspect: adding function arugument list * Allow overwrite CFLAGS from command line * Ignore attribute vector_size * Remove set but not used variable * inspect: Add switch statement and more * validation: inline switch statement * Fix inlining switch statement. * Sparse 0.4.4-rc1 * Add test case for empty asm clobbers * Fix parsing empty asm clobber * Sparse 0.4.4-rc2 * Add test case for binary constants * sparse 0.4.4 Dan Carpenter (1): * recognize binary constants Diego Elio Pettenò (3): * build: allow easy override of GCC_BASE * build: add an all-installable target that builds the targets to install. * Fix build with GCC 4.6 series. Florian Fainelli (1): * Makefile: warn user when libxml and/or libgtk2 are not available Jan Pokorný (4): * remove unused "container" macro * flow.c: make comment for 'dominates' reflect code * use ARRAY_SIZE() when possible (continued) * parse.c: "if(" -> "if (" adjustment Jonathan Neuschäfer (3): * fix a memory leak in compile-i386.c * FAQ: fix a typo ("because or") * fix common misspellings with codespell Kamil Dudka (2): * cse: treat PHI-nodes as other instructions * cse: update PHI users when throwing away an instruction Linus Torvalds (5): * Add new streams to a hash-list based on their names * Teach 'already_tokenized()' to use the stream name hash table * Make 'linearize_iterator()' helper function * Make 'linearize_switch()' helper function * Make 'linearize_return()' helper function Michael Stefaniuc (1): * Ignore the ms_hook_prologue attribute. Namhyung Kim (3): * use ARRAY_SIZE() when possible * Fix tokenizer for octal escape sequences * Update the validation check for escape sequences Nicolas Kaiser (1): * memops.c: always true expression Pekka Enberg (4): * sparse: Add 'artifical' to ignore attributes * sparse: Enable unhandled validation tests * Show expected vs. actual output on test failure * sparse: Fix __builtin_safe_p for pure and const functions sparse-0.6.4/Documentation/release-notes/v0.4.5-rc1.rst000066400000000000000000000004101411531012200224250ustar00rootroot00000000000000v0.4.5-rc1 (2013-05-09) ======================= I just push the tag for sparse 0.4.5-rc1 It is about time to cut a new release. There have been a lot of small improvements. It produces less warning on the recent kernel check. Please give it a good test. Chris sparse-0.6.4/Documentation/release-notes/v0.4.rst000066400000000000000000000342351411531012200216130ustar00rootroot00000000000000v0.4 (2007-09-15) ================= I have tagged and tarballed Sparse 0.4, now available from http://kernel.org/pub/software/devel/sparse/dist/sparse-0.4.tar.gz, with sha1sum a77a10174c8cdb5314eb5000c1e4f24458848b91. Highlights and visible changes in this release: * The Sparse validation files have become an automated test suite, invoked via 'make check'. Thanks to Damien Lespiau for the initial 'test-suite' script. Also thanks to Pavel Roskin for ideas, discussion, and prototyping, and to the contributors of new test cases and test-suite metadata for existing test cases. * New backend 'c2xml' by Rob Taylor, which outputs an XML representation of the variable declarations, function declarations, and data structures in a C file. * 'sparse' and 'cgcc' now have manpages. * Warning changes: * Warn on 'return ;' if given '-Wreturn-void'; off by default because the C99 standard permits this. * Sparse now defaults to -Wno-do-while. * Sparse now defaults to -Wdecl. * -Wno-old-initializer turns off warnings about non-C99 struct initializers * -Wno-non-pointer-null turns off warnings about using a plain integer as a NULL pointer * Initializer entries defined twice and bitfields without explicit signs have changed from errors to warnings. * 'cgcc' no longer passes '-Wall' to 'sparse' if given '-Wall' on the command line. '-Wall' turns on all Sparse warnings, including experimental and noisy ones. Don't include it just because a project wants to pass '-Wall' to 'cc'. If you really want 'cgcc' to run 'sparse' with '-Wall', set 'CHECK='sparse -Wall'' * Support for more builtin functions: '__builtin_labs', '__builtin_offsetof', '__builtin_strcat', '__builtin_strncat', and '__builtin_strlen'. * Support for more attributes: 'constructor', 'destructor', '__always_inline__', '__noinline__', 'used', '__syscall_linkage__', 'format_arg', 'cdecl', 'stdcall', 'fastcall', 'dllimport', and 'dllexport'. * Support for the '__DATE__' and '__TIME__' preprocessor symbols. * Support for '__alignof' (treated like the existing '__alignof__'). * typeof(bitwise_type) now produces the same type, not an incompatible type; thanks to Al Viro. * Various profile-driven optimizations and changes to compiler optimization flags, making Sparse significantly faster. * Numerous fixes to C standards compliance, thanks to Al Viro. In particular: * Sparse now requires integer constant expressions in various places, not arbitrary expressions. * Sparse now handles NULL pointer constants more correctly. * Sparse now resolves pointer types more correctly in conditional expressions * The 'graph' backend now outputs significantly better program graphs, and the Sparse source code includes some 'gvpr' scripts to modify these graphs in various ways, such as only showing the callers or callees of particular functions. Thanks to Dan Sheridan. * Linux-style 'make' output; for a more verbose make with full command lines, 'make V=1'. Thanks to Damien Lespiau. * Numerous bugfixes and internal cleanups; thanks to the many Sparse contributors. Full changelog: Al Viro (51): * handle __alignof as equivalent of __alignof__ * saner reporting of overlaps in initializers * check for whitespace before object-like macro body * fix alignment for _Bool * fix interaction of typeof with bitwise types * better recovery from bad operations on bitwise * make copying of EXPR_INDEX non-lazy * tie the fields of struct in simple list * rewrite of initializer handling * fix handling of typeof on structs * missing NULL checks in initializer handling * take cast_to() out of usual_conversions(), do it in callers * mechanically split compatible_assignment_types() * null pointer constants have no special meaning for pointer subtraction * remove long-dead variable in evaluate_ptr_add() * remove useless argument in evaluate_ptr_sub() * cleanup of evaluate_assign_op() * clean up the typechecking in arithmetics * clean up usual_conversions(), kill evaluate_shift() * fix index conversions in evaluate_ptr_add() * fix default argument promotion * move degenerate() down into compatible_assignment_types() * in case of compound literal we want to delay examining type * warn on return ; * deal with enum members without excessive PITA * implement __builtin_offsetof() * fix handling of integer constant expressions * fix the comma handling in integer constant expressions * first pass at null pointer constants * make size_t better approximate the reality * fix handling of address_space in casts and assignments * fix handling of pointers in ?: * saner show_type() * clean up evaluate_sign() * integer_promotions() can't get SYM_NODE or SYM_ENUM * start cleaning type_difference() * get compatible_assignment_types() deal with all cases * fix the sanity check in evaluate_ptr_sub() * rewrite type_difference() * deal correctly with qualifiers on arrays * add __builtin_strlen() * no such thing as array of functions * new helper: unfoul() * handling of typeof in evaluate_member_dereference() * file and global scopes are the same for purposes of struct redefining * ...,array should degenerate * sanitize evaluate_ptr_add(), start checking for pointers to functions * fix evaluate_compare() * sanitize evaluate_postop() * saner -Wtypesign * braino in conditional_expression() Alberto Bertogli (1): * Implement x86-64 support in cgcc. Alexey Dobriyan (2): * Fix infinite loop in free_preprocessor_line() * Fix -E handling Christopher Li (2): * combinations string clean up * Pass a bitmask of keywords to handle_attributes Damien Lespiau (6): * Change sparse homepage in ctags headers. * __DATE__ & __TIME expansion * Beautify all & install Makefile targets * test-suite: a tiny test automation script * test-suite documentation * Sample test-suite test cases Dan Sheridan (2): * Improved graph generation using subgraph clusters for functions * Add gvpr-based post-processing for graphs Josh Triplett (118): * Fix website and repository references in FAQ * Fix the version number * Remove old version note. * gitweb lives at git.kernel.org now. * Add a "make dist" that requires $(VERSION) to match 'git describe' * Add test case for __asm__ __volatile__(...) * Make cgcc not pass -Wall to sparse even if passing it to cc * Teach cgcc about all currently existing sparse warning options * Teach cgcc about -ventry and -vdead * Parse asm after a label as a statement, not an attribute * Add test case for stdcall and cdecl attributes. * Add -Wno-old-initializer to turn off warnings about non-C99 struct initializers * Add test case for -Wno-old-initializer * Revert unintentional inclusion of warning fix in previous commit. * Use %td when printing a ptrdiff_t to avoid problems on 64-bit platforms * Remove extra space. * Add shebang to gvpr scripts, make them executable, and change usage accordingly * Fix an __attribute__() parsing error * Expand calling convention test case to cover fastcall * Add -Wno-non-pointer-null to turn off warning about using a plain integer as a NULL pointer * Add __builtin_strcat and __builtin_strncat. * Ignore the GCC constructor and destructor attributes * Remove inaccurate comment designating some attributes as windows-specific. * Move the ident for defined() into the preprocessor section. * Reorganize attribute list for readability. * Add double-underscore variant __always_inline__. * Add double-underscore variant __noinline__. * Add no-double-underscore variant "used", ignored like "__used__". * Add double-underscore variant __syscall_linkage__. * Add no-double-underscore variant format_arg. * Add explanatory comment about direct use of __IDENT for preprocessor idents. * Sparse always defines __STDC__ 1, so cgcc does not need to do so * Fix old typo: s/wierd/weird/ * Canonicalize URL in FAQ: add www., add trailing slash * Change "LD" to "LINK" in Makefile prettyprinting. * Makefile prettyprinting: make INSTALL and other output line up correctly * Add test case for infinite loop in free_preprocessor_line() * Turn on -Wdecl by default. * ctags: Use const as appropriate in cmp_sym() * validation/old-initializer.c: Make the_s static to avoid extraneous warning. * validation/restricted-typeof.c: Make globals static to avoid extraneous warnings. * validation/escapes.c: Make globals static to avoid extraneous warnings. * validation/non-pointer-null.c: Make global static to avoid extraneous warning. * Merge commit 'viro/integer-constant' * Move all the preprocessor tests into validation/preprocessor/ * Move test-suite output files to validation/.gitignore * .gitignore: Stop ignoring all dotfiles * validation: Update comments for current Sparse behavior and test-suite. * Add test-suite comments to all the obvious preprocessor tests * Make preprocessor-loop a normal numbered preprocessor test * Add test-suite comment to preprocessor21. * Add test-suite comment to address_space.c * Make clean depend on clean-check * Rename asm-volatile to better describe what it tests * Add test-suite comment to label-asm.c * Remove "check-exit-value: 0" and rely on default; remove extra blank line. * Add test-suite comment to bad-array-designated-initializer.c * Add c2xml to .gitignore * Split c2xml build rule into compile and link stages, and add the quiet prefixes * expression.h needs lib.h for struct position and symbol.h for int_ctype * Fix GCC warnings in c2xml * Fix sparse warnings in c2xml: mark globals static and remove unused globals * Fix test-suite to handle stdout and stderr separately, and fix up tests * Add test-suite metadata to bad-cast.c * Add test-suite metadata to bad-ternary-cond.c, and remove now-redundant comment * Add test-suite metadata to initializer-entry-defined-twice.c * Add test-suite metadata to context.c * Add test-suite metadata to escapes.c * Add test-suite metadata to calling-convention-attributes.c * Fix typos in test-suite documentation * Makefile: stop cleaning files we didn't make and have no business cleaning * Add test-suite metadata to old-initializer.c; also test with -Wno-initializer * allocate.h: Stop needlessly returning a void value in __DO_ALLOCATOR * Turn off -Wdo-while by default. * Add test-suite metadata to label-attr.c * validation/builtin_safe1.c: Show the unsafe macro argument * Make "Initializer entry defined twice" a warning, not an error * Remove explicit restatements of defaults in metadata for member_of_typeof test * Remove explicit restatements of defaults in metadata for outer-scope test * Remove explicit restatements of defaults in metadata for comma test * Add test case for comparing null pointer constant to int. * Makefile: Use -O2 -finline-functions, not just -O * cse: Size insn_hash_table more realistically, speeding up CSE significantly * Add some missing dependencies in the Makefile * Drop -fpic; it hurts performance and we don't build libsparse.so by default * Add another test case to validation/comma.c * ctags: Handle some new namespaces and symbol types. * is_zero_constant: declare saved const * Add test case for -Wtypesign * Sort warning options in lib.c and lib.h * Rename Wcast_to_address_space to Wcast_to_as to match the command-line argument * Add a manpage for sparse * Install the Sparse manpage * cgcc: Sparse accepts -Wcast-to-as, not -Wcast-to-address-space * Rename Wundefined_preprocessor to Wundef to match the command-line argument * cgcc: Sparse accepts -Wundef, not -Wundefined-preprocessor * Use -fno-strict-aliasing, as the ptrlist code seems to violate C99 strict aliasing rules * Add test-suite annotations to restricted-typeof.c * Add test-suite annotations to double-semicolon.c * Add test-suite annotations to check_byte_count-ice.c * Add test-suite annotations to badtype4.c * Add test-suite annotations to varargs1.c * Add test-suite annotations to struct-attribute-placement.c * Add test-suite annotations to non-pointer-null.c * Add test-suite annotations to struct-ns1.c * Add test-suite annotations to noderef.c * Makefile: Use ?= to allow overriding OS or AR on the Make command line * FAQ: Point to URL on vger for subscription instructions and archives * README: recode from ISO-8859-1 to UTF-8 * validation: Rename typeconvert.c to integer-promotions.c to match its purpose * Add test-suite annotations to integer-promotions.c * Add test-suite annotations to cond_expr.c * Add test-suite annotations to function-pointer-modifier-inheritance.c * validation: Update comment in type1.c to reflect current state of Sparse * Add test-suite annotations to init-char-array.c * Add a manpage for cgcc * Add SEE ALSO for cgcc in sparse manpage * Makefile: VERSION=0.4 Kovarththanan Rajaratnam (1): * libxml compile fix on Cygwin Michael Stefaniuc (3): * Ignore the cdecl and stdcall attributes for now. * Add test for typedef on pointer to function with stdcall attribute. * '\?' is a valid escape character defined by ANSI C. Its value is '?'. Mike Frysinger (1): * Makefile: improve flag handling Pavel Roskin (5): * Improve error message if using a member of an incomplete struct or union * Bitfield without explicit sign should be a warning, not an error * cgcc: preserve sparse exit code if -no-compile is used * Avoid use of libc headers in the validation suite * Fix warnings about undeclared globals, they are irrelevant to the test Ramsay Jones (2): * Add (more) support for WIN32 attribute names * Add cygwin support to cgcc Randy Dunlap (1): * add __builtin_labs() Rob Taylor (4): * add end position to symbols * add sparse_keep_tokens api to lib.h * new get_type_name function * add c2xml program Yura Pakhuchiy (1): * Make cgcc filter out all sparse warning related options ricknu-0@student.ltu.se (4): * tokenize.c: Replace handwritten strncmp with existing function. * expression.c: Clean up match_oplist() and add missing va_end() * parse.c: Adding va_end(). * tokenize.c: Simplify drop_stream_eoln(). -- Josh Triplett sparse-0.6.4/Documentation/release-notes/v0.5.0.rst000066400000000000000000000237441411531012200217550ustar00rootroot00000000000000v0.5.0 (2014-04-01) =================== As a lot of you have already noticed. The sparse 0.5.0 has been tagged and upload to the kernel.org for a while. One of the most noticeable change of the sparse 0.5.0 is the license. The sparse project has finally move to the MIT license. Thanks for the hard work of Dan Carpenter and Franz Schrober, who contact and collect permissions from the sparse developers. We actually manage to get *all* developer's blessing on the MIT license. This release also has some enhancement matching the latest kernel source annotations. It will reduce the noise level of the sparse warnings. So there you have it. The official announcement of the sparse 0.5.0. Chris --- Al Viro (9): * Fix handling of __func__ * Fix tab handling in nextchar_slow() * Fix ,##__VA_ARGS__ kludge * Gentler handling of bitwise warnings in unary operations * simplify handling of newline/whitespace flags in expand() * fix handling of -include * massage handling of wide string literals/character constants in tokenizer * switch to delayed handling of escape sequences * L ## 'a' is valid; so's L ## "a" Benjamin Herrenschmidt (1): * sparse, llvm: Fix varargs functions Christopher Li (19): * Limit usage of g++ to llvm related programs. * Merge branch 'sparse-llvm' of git://github.com/penberg/sparse-llvm.git * Adding default for m64/m32 handle * Merge branch 'for-chris' of git://github.com/penberg/sparse-llvm * Merge branch 'llvm/core' of github.com:penberg/sparse-llvm * remove weak define x86_64 * Merge git://git.kernel.org/pub/scm/linux/kernel/git/viro/sparse into marge * Clean up some test case error. * Fix segfault cause by fucntion without ident. * Get rid of gcc warning about enum values * Larger buffer size for token concatenation * Proper variable length array warning * Allow forced attribute in function argument * Trivial: Remove redundant Makefile variable * Merge branch 'llvmcore' * Merge branch 'Novafora' of git://git.zytor.com/users/hpa/sparse/sparse into license * Sparse 0.5.0 rc1 * Fix make dist failure * Sparse 0.5.0 Emilio G. Cota (1): * gitignore: add 'version.h' Ethan Jackson (1): * sparse: Add 'leaf' to ignored attributes. Franz Schrober (5): * Revert "Update the information in README about using the library." * Revert "Fix mistaken comparison that becomes a no-op." * sparse: Relicense under the MIT license * FAQ: Remove outdated sections about the license * sparse: Also check bit_offset when checking implicit casts Frederic Crozat (1): * Add __builtin_stpcpy, __sync_synchronize, __sync_bool_compare_and_swap to declare_builtin_functions James Westby (1): * Update the information in README about using the library. Jan Pokorný (2): * unssa: track use of newly added pseudo * simplify: conservative handling of casts with pointers Jeff Garzik (15): * sparse, llvm: if-else code generation * sparse-llvm: OP_SEL * sparse-llvm: OP_SWITCH * sparse-llvm: OP_LOAD * sparse-llvm OP_PHISOURCE: replace copy with target=src pointer operation * sparse, llvm: replace FIXME comment with assert(), following existing style * sparse, llvm: implement OP_CALL * sparse, llvm: move OP_PHI code from switch statement to separate function * sparse, llvm: move OP_CAST code to separate func. support FP casts. * sparse, llvm: create helper for obtaining instruction's type * sparse, llvm: store module-local functions on function reference list * sparse, llvm: move OP_COPY support to separate function. Add FP support. * sparse, llvm: support OP_STORE * sparse, llvm: Fix loops, by properly handling OP_PHI forward references * sparse, llvm: add loop testcase Jeff Layton (1): * sparse: add __builtin_va_arg_pack() and __builtin_va_arg_pack_len() Joe Perches (1): * There's no current way to know the version of sparse. Add --version to see it. Jonathan Neuschäfer (9): * FAQ: update the website address and call it Wiki * ptrlist.c: fix a typo in a comment * sparse, llvm: 'Verify' the LLVM module before writing it * sparse, llvm: convert the condition of branch/select to bool * sparse, llvm: Fix type of loaded values * sparse, llvm: Fix resulting type of store address calculations * sparse, llvm: de-duplicate load/store address calculation code * sparse, llvm: base load/store address type on insn_symbol_type() * sparse, llvm: add a struct access test case Josh Triplett (2): * Define __SIZEOF_POINTER__ * Support #pragma once KOSAKI Motohiro (2): * sparse: Add '__vector_size__' to ignored attributes * sparse: Add 'error' to ignored attributes Kamil Dudka (2): * cse: treat PHI-nodes as other instructions * cse: update PHI users when throwing away an instruction Kim Phillips (2): * sparse: add built-in byte swap identifiers * sparse: add built-in atomic memory access identifiers Linus Torvalds (1): * sparse, llvm: Make function declaration accessible to backend Masatake YAMATO (3): * Warn about initialization of a char array with a too long constant C string. * Test case for -Winit-cstring option * Add description for -Winit-cstring option Mauro Dreissig (1): * Fix wrong array size expression Pekka Enberg (69): * sparse, llvm: Initial commit * sparse, llvm: Fix assert() in sparse code * sparse, llvm: Fix global variable initialization * sparse, llvm: Fix 'sparsec' when it's not in PATH * llvm, sparse: Separate entry and exit basic blocks * sparse, llvm: Add switch statement to output_insn() * sparse, llvm: OP_RET/PSEUDO_VAL code generation * sparse, llvm: Add support for OP_RET/PSEUDO_ARG * sparse, llvm: Introduce 'struct function' to clean up code * sparse, llvm: Add output_op_binary() stub * sparse, llvm: Implement OP_ADD * sparse, llvm: Add support for more binary ops * sparse, llvm: Implement some binary comparison ops * sparse, llvm: Move binop tests to validation/backend * sparse, llvm: Implement OP_CAST * sparse, llvm: Floating point support for binops * sparse, llvm: Reorganize code generation tests * sparse, llvm: Bitwise not operator codegen * sparse, llvm: Kill ifdef'd unssa() call * sparse, llvm: Kill debugging code * Merge pull request #1 from jgarzik/hacks * Merge pull request #2 from jgarzik/hacks * sparse, llvm: Warn the user when we fall back to GCC * sparse, llvm: Code generation for string constants * sparse, llvm: Cleanup output_data() * sparse, llvm: Fix OP_CAST to use zero-extend * sparse, llvm: Improve sparsec front-end * sparse, llvm: Fix PSEUDO_OP code generation * sparse, llvm: Don't redefine module local functions * Revert "sparse, llvm: Don't redefine module local functions" * sparse, llvm: Fix code generation for casts * sparse, llvm: Fix pseudo_type() for PSEUDO_ARG * Merge pull request #3 from jgarzik/hacks * Merge branch 'master' of github.com:penberg/sparse-llvm * llvm, sparse: Fix symbol_is_fp_type() goof * Merge pull request #4 from jgarzik/hacks * sparse, llvm: Fix code generation for 'long double' data type * sparse, llvm: Add support for struct types * sparse, llvm: Add support for symbol initializers * sparse: Bump up sizeof(_Bool) to 8 bits * sparse, llvm: Add support for logical ops * sparse, llvm: Fix 'void \*' pointer code generation * sparse, llvm: Use new LLVM type system API for structs * sparse, llvm: Fix struct code generation * sparse, llvm: Fix symbol_type() for bitfields and enums * sparse, llvm: Add support for array types * sparse, llvm: Add support for union types * sparse, llvm: Make 'sparsec' error handling more robust * sparse, llvm: Function pointer code generation * sparse, llvm: Fix symbol initializer code generation * sparse, llvm: Fix 'extern' symbol code generation * sparse, llvm: Make llc output to stdout in sparsec * sparse, llvm: Pointer cast code generation * sparse, llvm: OP_SET_B and OP_SET_A code generation * sparse, llvm: More comparison ops code generation * sparse, llvm: Simplify comparison op code generation * sparse, llvm: FP comparison op code generation * Merge pull request #6 from jgarzik/hacks * sparse, llvm: Don't fail the build if LLVM is too old * sparse, llvm: Use LLVMInt1Type() in sym_basetype_type() * sparse, llvm: Add test case for type * Revert "sparse: Bump up sizeof(_Bool) to 8 bits" * sparse, llvm: Add _Bool to cast validation test * sparse, llvm: Simplify output_data() type logic * sparse, llvm: Fix string initializer code generation * sparse, llvm: Fix global string access code generation * sparse, llvm: Fix SIGSEGV for extern symbols * sparse, llvm: Fix 'void' return type code generation * sparse, llvm: Use LLVM_HOSTTRIPLE Ramsay Jones (2): * char.c: Fix parsing of escapes * symbol.c: Set correct size of array from parenthesized string initializer Randy Dunlap (1): * sparse patch v2: add noclone as an ignored attribute Robert Bedichek (1): * Novafora license grant using MIT license. Shakthi Kannan (1): * I have updated the sparse.1 man page including the __bitwise relevant content, and created Documentation/sparse.txt with the complete comparison between __nocast vs __bitwise. Xi Wang (17): * compile-i386: fix use-after-free in func_cleanup() * check missing or duplicate goto labels * fix casting constant to _Bool * fix SIGFPE caused by signed division overflow * sparse, llvm: fix link errors * sparse, llvm: fix phi generation * sparse, llvm: simplify function generation * sparse, llvm: improve pointer arithmetic handling * sparse, llvm: set target specification * sparse, llvm: use LLVM_DEFAULT_TARGET_TRIPLE * sparse, llvm: fix array size * sparse, llvm: cache symbol_type() result * sparse, llvm: fix struct name generation * sparse, llvm: set more data attributes * sparse, llvm: die if error * Fix result type of relational and logical operators * Fix expression type for floating point negation ('!') sparse-0.6.4/Documentation/release-notes/v0.5.1.rst000066400000000000000000000421341411531012200217500ustar00rootroot00000000000000v0.5.1 (2017-08-18) =================== It is finally there. Sparse 0.5.1 is released. I consider this the best quality of release of sparse I ever experienced so far. There are lots of enhancement and bug fixes incorporate into this release. I would like to thank every one that contributes to this release, people who submit patches, perform testing and send bug reports. I want to specially thank Luc Van Oostenryck who makes this release possible. He has 242 commits in this release, far more than any one else. The development effort for the next release has already began. Finally, I would like to call for developers joining the sparse project. If you are interested in modern compilers, reading compiler books and want some excise. Sparse is a good project to start. Sparse is very small, the project compiles under 10 seconds. It can digest the full kernel source file, generate internal byte code representation and perform check on it. All this happens in 1/10 of the time it took gcc to compile the same source file. Here is some project ideas, https://git.kernel.org/pub/scm/devel/sparse/sparse.git/tree/Documentation/project-ideas.md?h=v0.5.1 Thanks Chris Aaro Koskinen (1): * build: allow use of PKG_CONFIG to override pkg-config Andy Shevchenko (1): * lib.c: skip --param parameters Ard Biesheuvel (2): * sparse: treat function pointers as pointers to const data * Ignore pure attribute in assignement Azat Khuzhin (2): * sparse, llvm: compile: skip function prototypes to avoid SIGSEGV * validation/prototype: regression for skipping prototypes Christian Borntraeger (1): * s390x: add the proper defines for data types Christopher Li (24): * Minor clean up for option handling * round up the array element size to byte align * Make same_symbol list share the same scope * rename -Werror to -Wsparse-error * teach next_designators() use array_element_offset() * Ptr list sorting should use memmove instead of memcpy * Make macro expanded string immutable * Fix warning compiling sparse-llvm * Adding ignored attribute optimize * Let create_symbol check for previous same symbol * Add full list of gcc attribute * bump sparse's version to 0.5.1-rc4 * Adding gcc attribute no_gccisr * Add test case for the wine dead loop bug * Makefile: clean up and simplify * Makefile: add selfcheck target * Adding _Pragma() * fix warnings report by selfcheck * Adding gcc attribute noipa etc * Adding document for sparse patch submit process * Documents: project ideas * test-inspect: handle special case iter==NULL * test-inspect: Detect gtk3 then gtk2 package * Sparse 0.5.1 Cody P Schafer (3): * build: allow use of LLVM_CONFIG to override llvm-config config script * sparse{i,c}: use LLVM_CONFIG to find llc and lli * parse: support c99 [static ...] in abstract array declarators Dan Carpenter (1): * ptrlist: reading deleted items in NEXT_PTR_LIST() Daniel Wagner (1): * parse: Add comment to struct statement Edward Cree (1): * Allow casting to a restricted type if !restricted_value Emilio G. Cota (1): * Define __CHAR_BIT__ Emily Maier (2): * linearize: Emit C99 declarations correctly * validation: Check C99 for loop variables Hans Verkuil (3): * Add test case for extern array * Add test case for anonymous union initializer * Add test case for the ioc type check Heiko Carstens (1): * sparse/parse.c: ignore hotpatch attribute Jeff Layton (2): * sparse: make bits_to_bytes round up instead of down * Handle SForced in storage_modifiers Joe Perches (1): * sparse: Allow override of sizeof(bool) warning Johannes Berg (1): * implement constant-folding in __builtin_bswap*() John Keeping (3): * validation/sizeof-bool: fix broken test case * evaluate: split out implementation of compatible_assignment_types * Support GCC's transparent unions Lance Richardson (3): * sparse: ignore __assume_aligned__ attribute * sparse: update __builtin_object_size() prototype * sparse: add support for _Static_assert Linus Torvalds (5): * Add warning about duplicate initializers * Use any previous initializer to size a symbol * Fix error at anoymous unions * Fix scoping of extern symbols in block scope * Fix initializers in anonymous structs and unions Luc Van Oostenryck (242): * Teach sparse about the __COUNTER__ predefined macro * Fix size calculation of unsized bool array * Do not drop 'nocast' modifier when taking the address. * fix mixup in "Handle SForced in storage_modifiers" * Fix type checking of variadic functions * add missing #include "char.h" to char.c * make 'ignored_attributes[]' static * cleanup: remove evaluate_arguments()'s unused argument * Warn on unknown attributes instead of throwing errors * Remove unneeded variable in integer_promotion() * fix discarded label statement * add test case for builtin bswap with constant args * make ptrlist walking against robust against empty blocks * let "compile" not crash on bools * give comparable label's names to basic blocks * OP_SWITCH should use 'insn->cond' instead of 'insn->target' * remove unused field 'multijump' in struct instruction * storage should not be inherited by pointers * testsuite: simplify test function-pointer-inheritance * use a shorter name for function-pointer-modifier-inheritance.c * testsuite: test modifiers preserved by '&' operator * testsuite: test modifiers preserved by 'typeof()' * some modifiers need to be preserved by 'typeof()' * Update maintainers in the manpage * cgcc should not define non-reserved identifiers * recursive phi_defines cannot happen * fix missing element in types declaration * add support for __int128 * fix typing error in compound assignment * llvm: fix typing when comparing to a constant * llvm: remove unneeded OP_COPY support * fix cast to bool * unssa: do not try to update liveness * unssa: simplify rewrite of OP_PHISOURCE * unssa: try to avoid some OP_PHI copies * unssa: eliminate trivial phisrc copies * unssa: update comment about the unneeded copies * volatile loads must not be simplified * fix superfluous phisrc * fix phisrc mixup * missing load simplification * fix value of label statement * C11: teach sparse about '_Thread_local' * C11: teach sparse about '_Noreturn' * C11: teach sparse about '_Alignof()' * C11: teach sparse about '_Alignas()' * C11: teach sparse about '--std={c11,gnu11}' * fix cast's target type info * fix crash while testing between conditional & unconditional OP_BR * kill uses of replaced instructions * fix killing OP_PHI instructions * fix killing OP_CAST & friends * fix killing OP_SELECT * fix killing OP_COMPUTEDGOTO * explicitely ignore killing OP_ENTRY * cleanup kill_instruction() * fix conditional context test case with void * add helper: is_scalar_type() * validate expression's type in conditionals * remove unused arg in uses/defs functions * add testcase for wrong early escape conversion * warn on unknown escapes after preprocessing * remove 'Escape' from token character class * fix killing OP_SETVAL instructions * define __LP64__ & _LP64 if arch_m64 is enabled * add an helper for common predefined macros * define __LONG_MAX__ & __SIZEOF_POINTER__ * move OP_MUL simplification in a separate function * simplify '(x / 1)' to 'x' * simplify '(x * -1)' to '-x' * simplify '(x / -1)' to '-x' (but only for signed division) * simplify '(x % 1)' into '0' * simplify '~(~x)' and '-(-x)' to 'x' * simplify '(x || 1)' to '1' * simplify '(x op x)' to '0', '1' or 'x' * add warning option '-Wtautological-compare' * simplify comparisons followed by an equality test against 0 or 1 * simplify '(x || x)' and '(x && x)' * add support for LLP64 arch * move evaluation & expansion of builtins in a separate file * let identical symbols share their evaluate/expand methods * expand __builtin_bswap*() with constant args * testsuite: give a proper name to the 'binary-constant' test * testsuite: make tests known to fail effectively fail * testsuite: simplify the ioc-typecheck case * testsuite: add a simple test for -Wenum-mismatch * testsuite: add tag to ignore the output/error * testsuite: report as error tests known to fail but which succeed * allow to launch the test suite from the project root dir * testsuite: check patterns presence or absence in output * testsuite: add some selfchecking * testsuite: check the nbr of times a pattern should be present * testsuite: use 'error' instead of 'info' for successful tests known to fail * testsuite: get 'check-known-to-fail' earlier * testsuite: allow quieter error reporting * testsuite: quieter error reporting for 'known-to-fail' * cleanup: there is no 'struct phi' to allocate * remove unused field 'multijmp' in struct statement * remove unused field 'goto_bb' in struct statement * fix show-parse()'s labels * add killing of OP_SLICEs * add killing of OP_PHISOURCEs * add helper kill_use_list() * fix killing of OP_PHIs * fix clear_phi(), replace it by kill_instruction() * remove unused clear_phi() * fix killing of otherwise not-handled instructions * kill_instruction() may need to be forced or not * add killing of pure calls * fix killing OP_CALL via pointers * add killing of non-volatile loads * add killing of stores * fix killing of rewritten loads * use kill_instruction() when killing an OP_PHI during CSE * use kill_instruction() when killing any instructions during CSE * fix OP_PHI usage in try_to_simplify_bb() * simplify float-to-float casts that doesn't change size * CSE: add test cases for comparisons duality * CSE: use commutativity to identify equivalent instructions * CSE: avoid hashing removed instructions * fix expansion cost of pure functions * add missing braces around FOR_EACH_PTR loop * make -Wbitwise operational again * use option: '-Woverride-init' * add test case for warnings about overlapping initializers * allow to warn on all overlapping initializers * fix checking of overlapping initializer * ignore whole-range overlapping initializer * fix usage in simplify_seteq_setne() * fix size of loaded bitfields * split OP_BR between unconditional & conditional: OP_CBR * remove unused helper is_branch_goto() * replace test for c99 for-loop initializers * add test case for scope of C99 for-loop declarations * add test cases for storage of c99 for-loop declarations * add an optional validation method to external_declaration() * check the storage of C99 for-loop initializers * move 'extern with initializer' validation after the validate method * use VOID instead of directly using &void_pseudo * teach sparse about -Waddress * add is_func_type() * warn if testing the address of a function * add is_array_type() * warn if testing the address of an array * fix evaluation of a function or array symbol in conditionals * fix is_scalar_type() * fix test for cast to bool on 32bit machines * predefine __INT_MAX__ and friends * predefine __SIZEOF_INT__ & friends * fix test validation/div.c * fix cast to pointer to floating-point * do not depends on limits.h to test __CHAR_BIT__ * fix expansion of integers to floats * avoid crash with test-linearize -vv * fix OP_PHI usage in try_to_simplify_bb(), correctly * be more careful with concat_user_list() * avoid useless warning for 'bool <- restricted type' conversion * introduce REPEAT_CFG_CLEANUP * let kill_unreachable_bbs() clear REPEAT_CFG_CLEANUP * fix: kill unreachable BBs after killing a child * ignore VOID when trying to if-convert phi-nodes * fix boolean context for OP_AND_BOOL & OP_OR_BOOL * fix missing reload * keyword: add test case for reserved '_Static_assert' * keyword: regroup the [reserved] keywords * keyword: explicitly add C99 & C11 keywords * keyword: add more reserved keywords to the test case * keyword: add a comment about NS_TYPEDEF & reserved keywords * keyword: no pre-declaration needed for attribute names * add get__stats() * add show_allocation_stats() * add helper handle_simple_switch() * teach sparse how to handle '-fmem-report' * use -fmem-report to report allocation stats * testsuite: cleanup result files * fix: kill old branch in insert_branch() * returns the correct type when evaluating NULL * remove bit_size & bit_offset from struct access_data * add test case for linearize_initializer() of bitfields * fix implicit zero initializer. * remove alignment from struct access_data * remove origval from struct access_data * add support for a new flag: -fdump-linearize[=only] * more tests for implicit 'bool <- restricted' casts * avoid warning on explicit 'bool <- restricted' casts * define ident_list * teach sparse how to dump macro definitions * fix hardcoded size of wide chars * avoid to redefine __INT_MAX__ and friends * fix definition of __SCHAR_MAX__ & friends * teach sparse how to handle -dD flag * let -dD report macro definitions * testsuite: get all tags in once * testsuite: grep the expected output only when needed * testsuite: grep the output patterns only when needed * testsuite: use shell arithmetic instead of fork-execing expr * testsuite: remove unneeded './' before commands * testsuite: avoid fork+execing basename * teach cgcc about OSX aka darwin * ret-void: add test case for toplevel asm * ret-void: warn for implicit type * use NULL instead of 0 in testcases. * finer control over error vs. warnings * Add more declarations for more builtin functions * keep the warnings table alphabetically sorted * cgcc: alphasort warning names in check_only_option() * cgcc: add missing warning names to check_only_option() * cgcc: filter-out '-fdump-linearize[=...]' * memcpy()'s byte count is unsigned * add support for -Wmemcpy-max-count * add support for -fmemcpy-max-count * fix: add missing examine in evaluate_dereference() * fix OP_PHI usage in try_to_simplify_bb() only when non-bogus * fix: try_to_simplify_bb eargerness * add fallback for missing __builtin_bswapXX() * fix: __builtin_bswap{16,32,64}() constantness * dissect: use built_in_ident() instead of MK_IDENT() * teach sparse about -m{big,little}-endian * teach sparse about __{BIG,LITTLE}_ENDIAN__ * teach sparse about __BYTE_ORDER__ & __ORDER_{BIG,LITTLE}_ENDIAN__ * cgcc: teach cgcc about arm64 * cgcc: teach cgcc about ppc64[le] * cgcc: teach cgcc about arm * bump sparse's version to -rc3 * fix ptrlist corruption while killing unreachable BBs * fix infinite simplification loops * fix BB dependencies on phi-nodes * fix crash when ep->active is NULL * fix crash in rewrite_branch() * fix some crashes in add_dominators() * fix crash with sym->bb_target == NULL * take comma expr in account for constant value * fix: give a type to bad cond expr with known condition * ptrlist: add a counter for the number of removed elemnets * ptrlist: adjust ptr_list_size for the new ->rm field * ptrlist: add MARK_CURRENT_DELETED * ptrlist: avoid iteration on NULL entries * mark pseudo users as deleted instead of removing them * testsuite: add support for commands with timeout * Remove single-store shortcut * Bump sparse's version to -rc5 * Sparse v0.5.1 Michael Stefaniuc (3): * Add the __builtin functions needed for INFINITY and nan(). * Add a define for __builtin_ms_va_copy() * Add tests for the builtin INF and nan() functions. Oleg Nesterov (3): * dissect: teach do_expression() to handle EXPR_OFFSETOF * dissect: teach do_initializer() to handle the nested EXPR_IDENTIFIER's * dissect: s/mode_t/usage_t/ in report_member() Omar Sandoval (1): * sparse-llvm: Fix LLVM 3.5 linker errors Pavel Roskin (1): * Use LLVM_CONFIG instead of llvm-config in Makefile Ramsay Jones (15): * Add the __restrict__ keyword * sparse: add 'gnu_inline' to the ignored attributes * don't call isdigit/tolower with a char argument * Makefile: suppress error message from shell * don't run sparse{c,i} tests when sparse-llvm is disabled * Add support for multiarch system header files * cgcc: use only the cc command to determine $gcc_base_dir * cgcc: use $ccom to set $multiarch_dir if not specified * test-suite: remove bashism to avoid test failures * cgcc: avoid passing a sparse-only option to cc * parse.c: remove duplicate 'may_alias' ignored_attributes * compile-i386.c: don't ignore return value of write(2) * sparse: add 'alloc_align' to the ignored attributes * lib: workaround the 'redeclared with different type' errors * Makefile: pass -Wno-vla to sparse while checking pre-process.c Randy Dunlap (1): * documentation: update email reference link Rui Teng (1): * sparse: add no_sanitize_address as an ignored attribute Thomas Graf (1): * sparse: Make -Werror turn warnigns into errors Tony Camuso (2): * .gitignore: add cscope and Qt project files * Add default case to switches on enum variables sparse-0.6.4/Documentation/release-notes/v0.5.2.rst000066400000000000000000000077301411531012200217540ustar00rootroot00000000000000v0.5.2 (2018-04-30) =================== The latest release of sparse have been pushed to the official repository. It's a smaller release than the previous one but it contains some important changes to not be flooded by unimportant warnings while compiling the kernel. The most notable changes are: * better tracking and handling of expression constness * fix bug with variadic macros * less warnings on unknown attributes (none by default now) * teach sparse about __builtin_{isinf_sign,isfinite,isnan} * various update to the documentation * do selfcheck with the locally built sparse * some fixes or improvements for build (armhf, GNU/kfreebsd, ...) * also evaluate files included via -include Many thanks to everyone involved. Luc Van Oostenryck --- Al Viro (1): * Sparse preprocessing bug with zero-arg variadic macros Christopher Li (8): * gcc attr: add nonstring warn_if_not_aligned * Makefile: provide CFLAGS for command line override. * Give the constant pseudo value a size * sparse-llvm: use pseudo->size to select llvm integer type * Update gcc attribute list * Fix crash cause by previous pseudo size change Jacob Keller (1): * sparse: document that -Wbitwise is default Logan Gunthorpe (1): * add __builtin functions for isinf_sign, isfinite and isnan Luc Van Oostenryck (13): * constexpr: rename handle_simple_initializer() to handle_initializer() * constexpr: collect storage modifiers of initializers * return an error if too few args * give default return type in evaluate_call() * constexpr: flag __builtin_bswap() as constexpr * build: disable sparse-llvm on non-x86 * fix cgcc ELF version for ppc64/pcc64le * fix: missing evaluate with '-include' : add testcase * fix: missing evaluate with '-include' * Revert "Give the constant pseudo value a size" * By default disable the warning flag '-Wunknown-attribute' * bump up version to 0.5.2-RC1 * Sparse v0.5.2 Martin Kepplinger (2): * compile-i386.c: fix a memory leak in sort_array() * compile-i386: make use of expression_list_size() Nicolai Stange (20): * constexpr: introduce additional expression constness tracking flags * constexpr: init flags at expression allocation * constexpr: examine constness of casts at evaluation only * constexpr: examine constness of binops and alike at evaluation only * constexpr: examine constness of preops at evaluation only * constexpr: examine constness of conditionals at evaluation only * constexpr: add support for tagging arithmetic constant expressions * constexpr: add support for tagging address constants * constexpr: check static storage duration objects' intializers' constness * constexpr: recognize static objects as address constants * constexpr: recognize address constants created through casts * constexpr: recognize address constants created through pointer arithmetic * constexpr: recognize members of static compound objects as address constants * constexpr: recognize string literals as address constants * constexpr: recognize references to labels as address constants * constexpr: examine constness of __builtin_offsetof at evaluation only * constexpr: flag builtins constant_p, safe_p and warning as constexprs * constexpr: relax some constant expression rules for pointer expressions * constexpr: support compound literals as address constants * constexpr: treat comparisons between types as integer constexpr Ramsay Jones (1): * Makefile: use locally built sparse in the selfcheck target Randy Dunlap (5): * sparse: minor manpage corrections * Documentation: make data-structures.txt easier to read * Documentation: editing fixes in test-suite * test-suite: handle format with filename.c not existing * sparse: ignore indirect_branch attribute Uwe Kleine-König (4): * build: remove version.h in clean target * cgcc: teach cgcc about GNU/kFreeBSD * compile-i386: Use SPARSE_VERSION instead of __DATE__ * cgcc: provide __ARM_PCS_VFP for armhf sparse-0.6.4/Documentation/release-notes/v0.6.0.rst000066400000000000000000001344371411531012200217600ustar00rootroot00000000000000v0.6.0 (2018-12-26) =================== The source code can be found at its usual repository: git://git.kernel.org/pub/scm/devel/sparse/sparse.git v0.6.0 The tarballs are found at: https://www.kernel.org/pub/software/devel/sparse/dist/ Many thanks to people who have contributed to the 888 non-merge patches of this release: Ramsay Jones, Randy Dunlap, Uwe Kleine-König, Joey Pabalinas, John Levon, Ben Dooks, Jann Horn, Logan Gunthorpe, Vincenzo Frascino and Tycho Andersen. Special thanks to Ramsay Jones who has reviewed numerous of my patches, tested many of my series and found many of my typos. Best wishes for 2019 -- Luc Van Oostenryck The changes since the pre-release (v0.6.0-rc1) are: * add -Wbitwise-pointer to warn on casts to/from bitwise pointers * beautify how types are displayed: * no more * no more redundant type specifiers * remove double space * some cleanup of the build system: * only need includedir from llvm-config * check if sparse-llvm needs libc++ * remove -finline-functions from CFLAGS * some random cleanup: * remove unneeded declarations in "compat.h" * remove unused arg in add_branch() * allocate BBs after the guards * remove redundant check of _Bool bitsize * remove unused regno() * remove self-assignment of base_type * some documentation: * update comment for struct-expression::cast_expression * fix list formatting in inline doc * document that identifiers are now OK for address spaces * add TODO list. The main changes since the previous release (v0.5.2) are: * by default, disable warnings about unknown attributes * no more warnings about sizeof(void) unless -Wpointer-arith is given * add support for __has_attribute() & __has_builtin() * many fixes for type evaluation/checking * the build should be more friendly for distros * a huge number of testcases have been added to the testsuite * the handling of cast instructions has been completely revamped * the SSA conversion has been is now corrected and has been rewritten with a variant of the classical algorithm * documentation is now handled with Sphinx and inline doc is extracted from the code. * online documentation can be found at https://sparse-doc.readthedocs.io/en/master/ A more complete list of changes is: * add predefined macros for __INTMAX_TYPE__, __INT_MAX__, ... * prepare to identify & display the address spaces by name * fix linearization of non-constant switch-cases * manpage: update maintainer info * manpage: add AUTHORS section * fixes for -dD * add support for -dM * remove more complex phi-nodes * fix linearization/SSA when missing a return * fix linearization/SSA of (nested) logical expressions * fix linearization of unreachable switch + label * add support for __has_attribute() * consolidate instruction's properties into an opcode table * fix: do not optimize away accesses to volatile bitfields * support mode(__pointer__) and mode(__byte__) * do 'classical' SSA conversion (via the iterated dominance frontier). * fix buggy recursion in kill_dead_stores() * kill dead stores again after memops simplification is done. * simplify TRUNC((x & M') | y, N) * simplify AND(SHIFT(a | b, S), M) * simplify TRUNC(SHIFT(a | b, S), N) * add simplification of TRUNC(TRUNC((x)) * add simplification of SHL(LSR(x), S), S) * generate integer-wide OP_ADD & OP_SUB in linearize_inc_dec() * simplify mask instructions & bitfield accesses * fix a few bugs related to the linearization of logical expressions * simplify those logical expressions. * add optimized version of some list operations * some simplifications of OP_SETNE & OP_SETEQ with 0 and 1 * several simplifications involving casts and/or bitfields * give a correct & sensible warning on negative or over-sized shifts. * add conservative simplification of such shifts. * do not optimize the meaningless shift: * any shift with a negative count * OP_ASRs with an over-sized shift count. * try to give a correct negative/too-big error message during simplification. * simplify chains of shifts. * simplify ZEXT + ASR into ZEXT + LSR * cse: try the next pair instead of keeping the first instruction * cse: compare casts only by kind a size, not by C type. * optimize away OP_UTPTR & OP_PTRTU which are nops. * cleanup of list walking macros: * make untagged pointers the normal case * use structurally equivalent struct for all pointer lists to avoid needless casting to and fro struct ptrlist * simplify PREPARE/NEXT/RESET logic by using common PTR_NEXT() * add validation of the IR. * improve expansion of builtin dynamic macros (__FILE__, ...) * add support for __INCLUDE_LEVEL__ & __BASE_FILE__ * improve generation of predefined macros * add support for builtins doing overflow checking. * add support for the __has_builtin() macro. * improve Sphinx doc for IR instructions * have those instructions in the index * have a nicer presentation of the generated doc thanks to not having to use level-4 headings anymore * fixes & improvements to the testsuite; mainly: * allow to run the testsuite on all the tests of a subdir * teach 'format' to directly append to the testcase * validate the 'check-...' tags Shortlog -------- Ben Dooks (1): * tokenize: check show_string() for NULL pointer Jann Horn (1): * fix accesses through incorrect union members Joey Pabalinas (2): * doc: copy-edit text related to applying sizeof to a _Bool * sparse: add -Wpointer-arith flag to toggle sizeof(void) warnings John Levon (2): * Ignore #ident directives * Conditionalize 'warning: non-ANSI function ...' Logan Gunthorpe (1): * add __builtin functions for isinf_sign, isfinite and isnan Luc Van Oostenryck (857): * use long for all mem stats * diet: use smaller LIST_NODE_NR (29 -> 13) * diet: remove unused struct scope::token * diet: remove unused struct symbol::arg_count * option: add helper to parse/match command line options * option: rename 'struct warning' to 'struct flag' * option: let handle_simple_switch() handle an array of flags * option: extract OPTION_NUMERIC() from handle_switch_fmemcpy_max_count() * option: add support for options with 'zero is infinity' * option: add support for '-=unlimited' * option: use OPTION_NUMERIC() for handle_switch_fmemcpy_max_count() * option: constify match_option() * option: handle switches by table * dump-ir: add defines for the compilation passes * testsuite: 'echo -n' may not be interpreted as '-n' * testsuite: allow to test a few cases at once * testsuite: move verbose() & error() * testsuite: better message for pattern nbr checking * testsuite: better message for pattern absence/presence * use shorter name for constexpr tests * testsuite: new eq/min/max syntax for pattern checking * testsuite: obsolete old pattern checking syntax * testsuite: convert to the new pattern syntax * use a specific struct for asm operands * fix: missing evaluate with '-include' : add testcase * fix: missing evaluate with '-include' * fix test case kill-phi-ttsb * add test case for incomplete type * add test case for bad return type * diet: remove unused struct symbol::value * cclass: char is wide enough * cclass: cleanup * remove prototype extern int is_ptr_type() * remove prototype for nonexistent examine_simple_symbol_type() * graph: do not scan removed instructions * build: fix effectiveness of generated dependencies * build: remove unused support for pkgconfig * cgcc: teach cgcc about freebsd & netbsd * testsuite: clearer result summary * testsuite: check error messages first * testsuite: saner handling of 'must_fail' * testsuite: allow to parse several options * testsuite: add support for -q|--quiet * testsuite: add support for -a|--abort * testsuite: get options from env too * testsuite: allow --format & --single * testsuite: remove useless selftest * testsuite: remove useless test-be.c * testsuite: extract disable() * testsuite: simplify documentation * testsuite: allow arch-specific tests * testsuite: save screen real estate * testsuite: add a blank line before format * testsuite: 'quiet' must be initialized earlier * testsuite: move up arg_file() * testsuite: make do_format() more self-contained * testsuite: format: saner defaults handling * testsuite: format: strip .c from default name * testsuite: add support for 'format -f' * testsuite: add support for 'format -l' * remove never-used MOD_TYPEDEF * MOD_ACCESSED is not a type modifier ... * reorganize the definition of the modifiers * remove redundancy in MOD_STORAGE * define MOD_QUALIFIER for (MOD_CONST | MOD_VOLATILE) * associate MOD_RESTRICT with restrict-qualified variables * add support for C11's _Atomic as type qualifier * build: use '-objs' instead of '_EXTRA_DEPS' * build: use '-ldlibs' instead of '_EXTRA_OBJS' * build: allow target-specific CFLAGS, CPPFLAGS, LDFLAGS & LDLIBS * build: allow CFLAGS & friends from command line * build: avoid rule-specific CFLAGS * build: use $LIBS directly in the dependency list * build: no need to use wildcards for generated dependencies * build: reuse rule for ALL_OBJS * build: CHECKER_FLAGS=-Wno-vla for all targets * build: move tests near their use * build: add note about overwritable vars * build: remove references to nonexistent pre-process.h * build: move clean & clean-check together * build: make clean targets quieter * build: remove rule for shared lib, it's unused * build: normalize rules * build: remove the dist rule since unused * build: use one line per item * build: allow the name 'local.mk' to be configurable via the environment * build: use standard rules for install * build: remove unused QUIET_INST_SH * build: let quiet commands use less indentation * build: simplify quiet commands * build: simplify clean pattern * build: add \*.o to clean-check pattern * build: avoid foreach * build: reorg & add comment * build: use a single space before assignments * build: add rule to run a single test * build: let -fno-strict-aliasing be a mandatory flag * volatile loads are side-effects too * define MOD_ACCESS for (MOD_ASSIGNED | MOD_ADDRESSABLE) * fix 'simplification' of float-to-int casts * fix description setval & symaddr * flush stdout when warning * add test case for bogus volatile simplification * fix: volatile stores must not be simplified * dump-ir: add testcase for option parsing corner case * dump-ir: allow to specify the passes to execute via cli's options * dump-ir: activate/deactivate pass 'mem2reg' * dump-ir: allow to skip the optimization pass(es) * dump-ir: saner use of fdump_linearize * dump-ir: rename -fdump-linearize to -fdump-ir * dump-ir: make it more flexible * dump-ir: activate -fdump-ir=mem2reg * add test case for using multiple input files * add test case for VLA sizeof * add test case for memory to register problem * add test case for conditionally undefined var * add test case for incomplete type * add test case bitfields in K&R decl * add test case storage specifier in struct member * add test case using sizeof on incomplete type * add test case for bad layout of bool in bitfields * add test case for missed overflow detection * add test cases for canonicalization of add/sub chains * add test case for compound literals * add testcase for __builtin_unreachable() * add test cases for canonicalization of mul chains * add test case for pre-processor extra tokens warning * add testcase for return & inline * add test cases for simplification of equivalent to 'x == 0' or 'x != 0' * add test case for superfluous cast with volatiles * add testcase for mem2reg/SSA conversion * add test cases for canonicalization of boolean expressions * add test case for missing conversion to select * show OP_PHI without VOID * don't output value of anonymous symbol's pointer * add table to "negate" some opcode * use opcode table for compare_opcode() * canonicalize binops before simplification * canonicalize compare instructions * add is_signed_type() * fix usage of inlined calls * inlined calls should not block BB packing * give a type to all function arguments * give a type to OP_PHISOURCEs * give a type to OP_SELs, always * give a type to OP_SWITCHs * add doc about sparse's instructions/IR * add support for wider type in switch-case * llvm: remove unneeded arg 'module' * llvm: remove unneeded 'generation' * llvm: remove unneeded function::type * llvm: reduce scope of 'bb_nr' * llvm: use pseudo_list_size() instead of open coding it * llvm: give arguments a name * llvm: give a name to call's return values * llvm: avoid useless temp variable * llvm: extract get_sym_value() from pseudo_to_value() * llvm: fix test of floating-point type * llvm: fix translation of PSEUDO_VALs into a ValueRefs * llvm: fix output_op_store() which modify its operand * llvm: fix output_op_[ptr]cast() * llvm: add test cases for symbol's address * llvm: add test cases for pointers passed as argument * llvm: add test cases for arrays passed as argument * llvm: add test cases for degenerated pointers * llvm: add support for OP_NEG * llvm: add support for OP_SETVAL with floats * llvm: add support for OP_SETVAL with labels * llvm: ignore OP_INLINED_CALL * llvm: fix pointer/float mixup in comparisons * llvm: fix type in comparison with an address constant * llvm: give correct type to binops * llvm: adjust OP_RET's type * llvm: variadic functions are not being marked as such * llvm: fix type of switch constants * llvm: make pseudo_name() more flexible * llvm: give a name to all values * llvm: add support for OP_SWITCH with a range * llvm: fix OP_SWITCH has no target * llvm: make value_to_pvalue() more flexible * llvm: make value_to_ivalue() more flexible * llvm: add test case pointer compare with cast * llvm: let pseudo_to_value() directly use the type * llvm: add small script to test LLVM generated bytecode * llvm: add testcase for calling variadic functions * llvm: fix variadic calls with constants * llvm: take care of degenerated rvalues * llvm: fix mutating function pointer * llvm: fix mutated OP_RET * llvm: fix mutated OP_SEL * llvm: fix mutated OP_SWITCH * llvm: fix mutated OP_PHISOURCE * llvm: fix mutated OP_[PTR]CAST * llvm: add support for restricted types * llvm: fix get value from initialized symbol * llvm: fix get value from non-anonymous symbol * llvm: fix type of bitfields * llvm: add support for OP_FPCAST * llvm: add support for cast from floats * llvm: cleanup of output_[ptr]cast() * llvm: fix creation of sparsec's tmp files * llvm: give names easier to debug * llvm: gracefully catch impossible type/value * llvm: warn instead of assert on global inits * llvm: add support for float initializer * llvm: only compare void pointers * fix linearize_inc_dec() with floats * add test case for boolean negation on float * fix support of floating-point compare * add support of floating-point specific arithmetic ops * testsuite: fix: remove unneeded './' before commands * fix: build sparse-llvm on i686s too. * add more testcases for using addresses in conditionals * add testcases linearization of degenerated arrays/functions * fix: add missing degenerate() for logical not * testsuite: make the '%.t' rule depends on PROGRAMS too * testsuite: fix a few more incorrect check-commands * testsuite: convert to the new pattern syntax * testsuite: remove old ugly pattern syntax * testsuite: move verbose/error() before get_tag_value() * testsuite: add & use warning() * testsuite: reset 'quiet' at the start of each testcase * testsuite: fix invalid 'check-...' tags * testsuite: validate the 'check-...' tags * testsuite: early return in getopt loop * testsuite: move do_test_suite out of the getopt loop * testsuite: move no-arg out of the getopt loop * testsuite: change do_usage text * testsuite: allow to test only a subdir * testsuite: default to shift in the getopt loop * testsuite: add support for 'format -a' * add testcase for 'sparse -D M...' * fix: accept 'sparse -D M...' * testsuite: add test case for quoting of command's arguments * testsuite: process extra options without exec * testsuite: respect command line's quotes & whitespaces * testsuite: allow default args from environment for test commands * add test case for space within command line * fix: spaces in macro definition on the command line * add testcases for unexamined base type * fix: evaluate_dereference() unexamined base type * add testcases for the linearization of calls * simplify linearize_call_expression() * fix linearize (\*fun)() * add testcases for multiple deref of calls * avoid unneeded alloc on error path * dereference of a function is a no-op * add testcase for constant bitfield dereference * fix expansion of constant bitfield dereference * add testcase for CSE of floating-point compares * fix: restore CSE on floating-point compares * llvm: fix: previous function ref MUST be reused * llvm: use LLVMModuleRef for get_sym_value() * llvm: add declares for function prototypes * testcases: add missing return statements * warn on empty parenthesized expressions * fix crash on bad expression in linearize_switch() * llvm: simplify emit of null pointers * llvm: default init of arrays & structs * add more testcases for function designator dereference * add testcases for type comparison * fix implicit size of unsized arrays * let handle_switches() also handle reverse logic * add support for '-f[no-][un]signed-char' * fix: dereference null-type * teach sparse about '-fmax-warnings' * give a type to builtin functions * ctags: avoid null deref * cleanup: make some functions static * cleanup: remove unused & obsolete symbol_is_typename() * cleanup: remove unused delete_last_basic_block() * cleanup: remove declaration of unused merge_phi_sources * add OP_SETFVAL * CSE: support CSE of floating-point literal * doc: fix manpage formatting * report type & size on non-power-of-2 pointer subtraction * remove warning "call with no type" * add testcases for duplicated warning about invalid types * fix error in bad conditional * early return if null ctype in evaluate_conditional() * add helper: valid_type() * use valid_type to avoid to warn twice on conditionals * add helpers: valid_expr_type() & valid_subexpr_type() * do not report bad types twice * always evaluate both operands * fix examination of bad typeof * extract extract eval_insn() from simplify_constant_binop() * add testcase of dead dominator * fix dead dominator * fix missing checks for deleted instructions * add testcase for bad killing of dominated stores * add helper for pseudo's user-list's size * add helper: has_users() * use has_users() in dead_insn() too * let kill_instruction() report if changes were made * add testcases for converted loads * fix killing of converted loads * fix usage of deadborn loads * kill dead loads * kill dead stores when simplifying symbols * By default disable the warning flag '-Wunknown-attribute' * no repetition in unknown attribute warning message * cleanup: remove unused 'struct pseudo_ptr_list' * llvm: use list_size() to count the numbers of arguments * llvm: initialize at declaration time * show_pseudo(): protect against NULL ->sym * use show_pseudo() for OP_SYMADDR's symbol * let struct access_data use a single type * rename base_type() to bitfield_base_type() * builtin: add ctype for const {void,char} * * builtin: make builtins more builtin * builtin: extract eval_args() from arguments_choose() * builtin: add typechecking of isnan(), isinf(), ... * builtin: add testcases for expansion of special FP constants * builtin: add testcases for expansion of FP classification * unsigned multiplication is also associative * no need for signed & unsigned multiplication * use '%lld' for printing long longs * build: add -MP for generated dependencies * build: use -MMD for generated dependencies * ban use of 'true' or 'false' * 'amd64' is also ok for sparse-llvm * testsuite: fix typo with 'test-suite format -a' * rename variable 'optimize' to 'optimize_level' * move the optimization loop in its own file * cse: untangle simplification & hashing * extract cse_eliminate() from cleanup_and_cse() * expose interface to CSE * move liveness interface to its own header * move inner optimization loop into optimize.c * move the inner optimization loop into the main loop * remove unneeded cast in calls to free_ptr_list() * testsuite: add testcase for some random crash * testsuite: add testcase about CSE problem * IR: fix typo in IR doc * IR: remove now unused OP_LNOP & OP_SNOP * IR: remove never-generated instructions * IR: let .cond unionize with .src and not .target * IR: let OP_COMPUTEGOTO use .src instead of .target * llvm: normalize sparse-llvm-dis' output * llvm: fix typo for constant addresses * testsuite: fix problem with double-escaping in patterns * add a field 'tainted' to struct instruction * taint: let check_access() warn just once * fix address_taken() * fix symbol cleanup * cleanup deadborn phi-sources * optim: add some more optimization tests * optim: add testcase for internal infinite loop * optim: add timeout for infinite optim loop tests * optim: kill unreachable BBS after CFG simplification * optim: no need to kill_unreachable_bbs() after main loop * optim: fix optimization loop's condition * optim: pack bb must set REPEAT_CFG * optim: load simplification should repeat optimization * optim: fix REPEAT_CFG_CLEANUP * add an helper to test the value of a pseudo against zero * optim: simplify null select * make remove_usage() more generic * add remove_use() * show_label: add (and use) show_label() * extract alloc_phisrc() from alloc_phi() * small code reorg of add_store() * alloc: add missing #include "compat.h" * defer initialization of bb::context * fix-return: remove special case for single return * avoid deadborn loads & stores * doc: options.md is for development * doc: document the debug flags * fix missing handling of OP_FNEG * graph: do not use insn->symbol for memops * use -Wpointer-arith for tests * default to LP64 for all and only for 64 bit ABIs * fix alignment of 64 bit integers on LLP64 * add testcases for verifying ABI's integer size & align * use an enum for ARCH_LP32 & friends * add a flag -mx32 ILP32 env on 64 bit archs * add testcase for enum / int type difference * add testcase for array size type difference * add testcase for typedef redefinition * export check_duplicates() * fix: warn on typedef redefinition * do not to ignore old preprocessor testcases * use also __x86_64 when __x86_64__ is used * build: use a variable for $(shell uname -m) * build: use 'filter' to do pattern matching inside the Makefile * build: disable LLVM on x86-64-x32 * let cgcc use sparse's predefines for i386 & x86-64 * build: use --dirty with 'git describe' * fix build on Hurd which doesn't define PATH_MAX * testsuite: add check-cp-if * testsuite: add check-assert * teach sparse about _Floatn and _Floatnx * add test case bug expand union * alloc: check if size is too big * fix: don't dump pointer value in error message * fix missing checks for deleted instructions * fix comment about PSEUDO_SYM usage * fix: remove usage when killing symaddr (part 1) * fix: remove usage when killing symaddr (part 2) * OP_SYMADDR is simply an unop * use function-like syntax for __range__ * increment the version number suffix it with -dev * doc: fix markdown syntax * doc: fix headings * doc: add minimal support for sphinx-doc * doc: add logo * doc: automatically set the copyright date * doc: automatically get the version * doc: set primary domain to C * doc: allow .md with py3-sphinx * doc: move sparse.txt to markdown and rename it * doc: the testsuite doc in reST * doc: format dev-options.md as a man page * doc: use reST for manpages * api: move evaluate interface to its own header file * doc: add structured doc to ptrlist.c * autodoc: extract doc from the C files * autodoc: convert extracted doc to reST * autodoc: add a sphinx c:autodoc directive for the extracted doc * autodoc: add doc from ptrlist.c * autodoc: add markup to argument's references * autodoc: doc the doc * autodoc: by default disable syntax highlighting * autodoc: add a small cheatsheet for reST markup * autodoc: support muti-line param & return descriptions * autodoc: document a few more APIs to test multiline * autodoc: add autodoc tests in the testsuite * doc: convert IR.md to reST * doc: add sphinx domain for IR instruction indexation * add helper for new parsing errors: unexpected() * context: fix parsing of attribute 'context' * context: __context__(...) expect a constant expression * context: fix crashes while parsing '__context__;' or '__context__(;' * context: stricter syntax for __context__ statement * context: extra warning for __context__() & friends * label: add testcase for label redefinition * label: avoid multiple definitions * vla-sizeof: add test cases * vla-sizeof: add support for sizeof of VLAs * fix typing of __builtin_expect() * fix crash on 'goto ' * give a position to end-of-input * avoid multiple error message after parsing error * add test for integer-const-expr-ness * dyn-macro: add testcase for __LINE__ & friends * dyn-macro: use a table to expand __DATE__, __FILE__, ... * dyn-macro: add support for __INCLUDE_LEVEL__ * dyn-macro: add real support for __BASE_FILE__ * utils: add xmemdup() & xstrdup() * utils: convert alloc + copy to {mem,str}dup_alloc() * builtin: add testcase for builtin macro expansion * builtin: extract do_define() from do_handle_define() * builtin: add builtin types {u,}{int,long,long}_ptr_ctype * builtin: declare __builtin_[us]{add,sub,mul}{,l,ll}_overflow() * builtin: rename arguments_choose() to args_triadic() * builtin: add support for __builtin_{add,sub,mul}_overflow(), ... * extract replace_with_bool() from replace_with_defined() * builtin: add support for __has_builtin() * builtin: add predefine() * builtin: directly predefine builtin macros * builtin: consolidate predefined_macros() * doc: API before IR * builtin: switch calling order of predefined_macros() & friends * builtin: merge declare_builtin_function() with declare_builtins() * teach sparse about -m16 * ptrlist: specialize __add_ptr_list() for tag/notag * ptrlist: remove now unneeded add_ptr_list_notag() * ptrlist: add helper PTR_UNTAG() * ptrlist: rename PTR_ENTRY() to PTR_ENTRY_UNTAG() * ptrlist: make explicit when tagged pointers are used. * ptrlist: let FOR_EACH_PTR() ignore tags * utils: add xasprintf() & xvasprintf() * add support for -fdiagnostic-prefix[=prefix] * doc: add doc for the -vcompound flag * testsuite: fix missing return * keep the debug flags alphabetically sorted * testsuite: allow extra/default options to test commands * ir-validate: add framework for IR validation * ir-validate: validate pseudo's defining instruction * ir-validate: add validation of (nbr of) phi operands * ir-validate: add more validation points * sparsec: simplify & portable use of mktemp * add predefines for __INT_WIDTH__ & friends * ptrlist: remove the now unneeded FOR_EACH_PTR_NOTAG() * ptrlist: let {first,last}_ptr_list() return the raw pointer * ptrlist: let sort_list() use the raw pointer * ptrlist: let all pointer lists have the same parameterized structure * ptrlist: when possible use the real type of the list * ptrlist: remove now unneeded CHECK_TYPE() * ptrlist: make add_ptr_list() more readable * ptrlist: make free_ptr_list() more readable * ptrlist: remove some unneeded arg from internal macros. * ptrlist: remove extra ident level * ptrlist: simplify loop nesting * ptrlist: simplify DO_NEXT * ptrlist: simplify PREPARE/NEXT * ptrlist: shorter continuated lines * ptrlist: remove ptr_list_empty() * ptrlist: make {first,last}_ptr_list() out-of-line functions * ptrlist: move semi-private prototypes close to their user * ptrlist: use VRFY_PTR_LIST() for sanity check * ptrlist: keep declaration of head-list-nr together * ptrlist: make clear what is API and what is implementation. * ptrlist: move DO_SPLIT() into DO_INSERT_CURRENT() * ptrlist: add missing doc for some functions * add testcase for bad fpcast simplification * fix bad fpcast simplification * avoid useless deref in simplify_cond_branch() * new helper: replace_pseudo() * remove unused arg in simplify_cond_branch() * add_uniop() should take a type, not an expression * rename add_uniop() to add_unop() * add missing entry for OP_FNEG in kill_insn() & validate_insn() * ir: define an \OP_.. range for unops * ir: case OP_UNOP ... OP_UNOP_END * cast: reorg testcases related to casts * cast: add testcase for bad implicit casts to struct/union * cast: add testcase for cast to bad typeof * cast: add tests for warnings issued by sparse -v * cast: rename evaluate_cast()'s vars with slightly more meaningful names * cast: force_cast are OK on non-scalar values * cast: prepare finer grained cast instructions * cast: specialize FPCAST into [USF]CVTF * cast: handle NO-OP casts * cast: specialize floats to integer conversion * cast: specialize casts from unsigned to pointers * cast: make [u]intptr_ctype alias of [s]size_t_ctype * cast: make pointer casts always size preserving * cast: temporary simplify handling cast to/from void* * cast: specialize cast from pointers * cast: add support for -Wpointer-to-int-cast * cast: make casts from pointer always size preserving * cast: specialize integer casts * cast: accept null casts * cast: do not try to linearize illegal casts * cse: add testcase for missed opportunity * new helper: def_opcode() * cast: simplify simplify_cast() * cast: merge simplification of constant casts with constant unops * cast: prepare for more cast simplifications * cast: keep instruction sizes consistent * cse: move to next comparable instruction * cast: simplify TRUNC + ZEXT to AND * add simple testcases for internal infinite loops * simplify 'x | ~0' to '~0' * simplify 'x & ~0' to 'x' * simplify 'x ^ ~0' to '~x' * bool: add testcase for bool simplification * bool: fix add missing check in simplify_seteq_setne() * bool: simplify ZEXT in bool -> int -> bool * bool: fix missing boolean context for floats * bool: generate plain OP_{AND,OR} instead of OP_{AND,OR}_BOOL * bool: remove OP_{AND,OR}_BOOL instructions * cast: reorganize testcases for cast optimization * cast: optimize away casts to/from pointers * cse: let equivalent casts hash & compare identically * fix killing OP_SWITCH * fix: remove dead OP_{SETVAL,SETFVAL,SLICE} * kds: add testcases for kill_dead_stores() * kds: add explanation to kill_dead_stores() * kds: rename kill_dead_stores() to kill_dead_stores_bb() * kds: add interface for kill_dead_stores() * kds: kill dead stores after memops simplification * kds: shortcut for kill_dead_stores() * kds: fix recursion in kill_dead_stores_bb() * kds: clarify kill_dead_stores_bb() * testsuite: reorganize tests for compound literals * testsuite: add a few more tests catching quadratic behaviour * testsuite: improve mem2reg testcases * testsuite: remove useless test for loop-linearization * graph: build the CFG reverse postorder traversal * graph: add debugging for (reverse) postorder traversal * dom: calculate the dominance tree * dom: add some debugging for the dominance tree * dom: add support for dominance queries * dom: build the domtree before optimization * dom: use domtree for bb_dominates() * sset: add implementation of sparse sets * idf: compute the iterated dominance frontier * idf: add test/debug/example * add new helper: is_integral_type() * add PSEUDO_UNDEF & undef_pseudo() * add insert_phi_node() * ptrmap: core implementation * ptrmap: add type-safe interface * ssa: phase 1: phi-nodes placement * ssa: phase 2: rename load & stores * ssa: phase 3: rename phi-nodes * ssa: activate the new SSA conversion * ssa: remove unused simplify_symbol_usage() * ssa: phi worklist * remove unused finish_address_gen() * remove unused struct access_data::pos * no need to assign ad->type for EXPR_POS * remove obsolete comment: /\* Dummy pseudo allocator \*/ * big-shift: add test for shifts with bad count * big-shift: mark out-of-range OP_{ASR,LSR,SHL} as tainted * big-shift: do not evaluate negative or over-sized shifts * big-shift: don't take the modulo at expand time * big-shift: move the check into check_shift_count() * big-shift: use the base type for shift-too-big warning * big-shift: also check shift count of shift-assignment * big-shift: do not simplify over-sized OP_ASR to zero * big-shift: reorder the tests in simplify_asr() * big-shift: reuse simplify_asr() for LSR & SHL * big-shift: simplify over-sized OP_LSRs * big-shift: simplify over-sized OP_SHLs * big-shift: use the type width for too big shift * big-shift: fix warning message for negative shift count * big-shift: fix evaluation of shift-assign * big-shift: do not truncate the count when checking it * big-shift: add -Wshift-count-{negative,overflow} * extract nbr_users() from unssa.c * add testcases for casts & bitfield insertion/extraction * bitfield: extract linearize_bitfield_extract() * bitfield: extract linearize_bitfield_insert() * cast: simplify [SZ]EXT + TRUNC to original size * cast: simplify [SZ]EXT + TRUNC to a smaller/greater size * cast: fix shift signedness in cast simplification * cast: do not compare sizes, test the opcode * cast: use a switch to handle TRUNC(AND(x,M),N) in simplify_cast() * cast: preserve the sizes of TRUNC(AND(x,M),N) * cast: simplify [ZS]EXT(AND(x,M),N) * cast: simplify AND(ZEXT(x,M),N) * cast: simplify SEXT(SEXT(x,N),N') * cast: simplify ZEXT(ZEXT(x,N),N') * cast: simplify SEXT(ZEXT(x,N),N') * bits: add helpers for zero & sign-extension * big-shift: add testcases for simplification of over-sized shifts * big-shift: add testcases for simplification of negative shifts * big-shift: move shift count check in a separate function * big-shift: fix warning message for negative or over-sized shifts * big-shift: do not optimize negative shifts * big-shift: do not optimize over-sized ASRs * use "%Le" to display floats * add copy_ptr_list() * testcases: add testcase for missing detection of out-of-bound stores * testcases: missing evaluation of side effects in typeof(VLA) * kill dead OP_FADD & friends * add ptr_list_empty() * add ptr_list_multiple() * add lookup_ptr_list_entry() * shift: simplify LSR(LSR(x,N),N') & friends * shift: simplify ASR(LSR(x,N),N') * shift: avoid simplification of ASR(LSR(x,0),N) * shift: simplify ASR(ZEXT(X, N), C) * testcase for SET{EQ,NE}([SZ]EXT(x, N),{0,1})'s simplification * cleanup of simplify_seteq_setne(): remove tmp vars * simplify SET{EQ,NE}(ZEXT(x, N),{0,1}) * simplify SET{EQ,NE}(SEXT(x, N),{0,1}) * simplify 'x != 0' or 'x == 1' to 'x' * add testcase for linearize_logical() * fix size corruption when simplifying 'x != 0' to 'x' * protect add_convert_to_bool() against bad types / invalid expressions * conditional branches can't accept arbitrary expressions * fix linearize_conditional() for logical ops * expand linearize_conditional() into linearize_logical() * simplify linearize_logical() * simplify SETNE(AND(X,1),0) to AND(X,1) * simplify SETNE(TRUNC(x,N),{0,1}) * simplify ZEXT(SETCC(x,y), N) * simplify SEXT(SETCC(x,y), N) * simplify TRUNC(SETCC(x,y), N) * simplify AND(SETCC(x,y), M) * boolean conversion of boolean value is a no-op * cast: fix warning position in cast_pseudo() * limit the mask used for bitfield insertion * expand linearize_position() into linearize_initializer() * put back the bitfield base type into struct access_data * fix instruction size & type in linearize_inc_dec() * fix bad indentation in linearize_inc_dec() * avoid infinite simplification loops of the second kind * optim: add a few more testcases for shift & mask * use multi_users() instead on nbr_users() * reorg code for shift-shift simplification * simplify ((x & M') | y ) & M into (y & M) when (M' & M) == 0 * simplify ((x & M) | y) >> S to (y >> S) when (M >> S) == 0 * simplify (x << S) >> S into x & (-1 >> S) * simplify (x & M) >> S to (x >> S) & (M >> S) * rename testcase for ((x << S) >> S) simplification * add testcase for ((x >> S) << S) simplification * add testcase for TRUNC(TRUNC(x)) simplification * simpler guard in LSR-SHL simplification * reorganize shift-shift simplification * simplify ((x >> S) << S) * reorganize simplification of ZEXT(TRUNC(x)) * simplify TRUNC(TRUNC(x)) * doc: simplify the creation of the viewlist * doc: automatically insert the blank line for lists * doc: convert existing simplify.c doc into ReST autodoc * doc: reword doc for replace_pseudo() * doc: add doc for simplification notation * add testcase for (((x & M') | (y & M'')) & M) * add testcases for bitfield & AND/OR simplification * unify simplify_lsr_or() & simplify_and_or_mask() * add simplify_mask_or() * use better names for simplify_mask_or_and() vars * document simplify_mask_or() & simplify_mask_or_and() * switch return order in simplify_mask_or_and() * allow simplification of OP(((x & y) | (a & M')), K) * move opcode test inside simplify_mask_or_and() * simplify OP(((x & M') | y), K) when (M' & M) == M * simplify OP(((x & M') | y), K) when (M' & M) != M' * simplify OP((x | C), K) when (C & M) == 0 * simplify OP((x | C), K) when (C & M) == M * simplify OP((x | C), K) when (C & M) != C * simplify SHL((x & M') | y, S) * add testcases for {LSR,SHL}(AND(x, M), S) with shared AND(x, M) * use an intermediate mask in simplify_shift() * simplify ((x & M) >> S) when (M >> S) == 0 * simplify ((x & M) >> S) when (M >> S) == (-1 >> S) * simplify ((x & M) << S) when (M << S) == 0 * simplify ((x & M) << S) when (M << S) == (-1 << S) * simplify TRUNC((x & M') | y, N) * doc: extend simplification notation * prepare simplification of MASK(SHIFT(a | b, S), M) * simplify AND(SHIFT(a | b, S), M) * simplify TRUNC(SHIFT(a | b, S), N) * mode keywords don't need MOD_{CHAR,LONG,...} * add support for mode __pointer__ * add support for mode __byte__ * add a testcase for enum using a mode * remove superfluous newline in 'unknown mode attribute' error message * testsuite: remove useless test for loop-linearization * symaddr: s/insn->symbol/insn->src/ * add testcase for accesses to volatile bitfields * split memops from unops * add a flag for volatile memops * fix: do not optimize away accesses to volatile bitfields * opcode: centralize opcode definition * opcode: add arity info * opcode: add OPF_TARGET * add a function to remove deadborn instructions * fix missing declarations * has-attr: add testcase for __has_attribute() * has-attr: move 'mode' next to '__mode__' * has-attr: add __designated_init__ & transparent_union * has-attr: add support for __has_attribute() * ir-validate: add validation branch to dead BB * ir-validate: ignore dead phis * ir-validate: validate return value * add testcase for unreachable label in switch * fix linearization of unreachable switch (with reachable label). * move DEF_OPCODE() to header file * trivial-phi: add testcase for unneeded trivial phi-nodes * trivial-phi: make clean_up_phi() more sequential * trivial-phi: extract trivial_phi() from clean_up_phi() * trivial-phi: early return * trivial-phi: use a temp var for the real source * trivial-phi: directly return the unique value * trivial-phi: remove more complex trivial phi-nodes * stricter warning for explicit cast to ulong * add linearization as a pass * add testcases for missing return in last block * use a temp var for function's upper-level statement * topasm: top-level asm is special * specialize linearize_compound_statement() * there is always an active BB after linearize_fn_statement() * the return BB is never terminated * extract add_return() from linearize_return() * use UNDEF for missing returns * use a temp var for the return type/symbol * return nothing only in void functions * add testcases for wrong ordering in phi-nodes * fix ordering of phi-node operand * add tests for nested logical expr * fix linearization of nested logical expr * add testcase for non-constant switch-case * fix linearization of non-constant switch-cases * test: make test Waddress-space-strict succeed on 32-bit * test: use integers of different sizes, even on 32-bit * test: make 32-bit version of failed test * ssa: relax what can be promoted * doc: is_int_type() returns false for SYM_RESTRICTs * enum: add testcase for UB in oversized shift * enum: fix UB when rshifting by full width * enum: add testcase for type of enum members * enum: add testcase for base & enumerator type * enum: fix cast_enum_list() * enum: use the smallest type that fit * enum: use the values to determine the base type * enum: only warn (once) when mixing bitwiseness * enum: warn when mixing different restricted types * enum: warn on bad enums * enum: rewrite bound checking * enum: keep enumerators as int if they fit * enum: default to unsigned * enum: more specific error message for empty enum * __attribute__((fallthrough)) can't simply be ignored * ptrlist: add ptr_list_nth_entry() * add testcase for missing function designator expansion * fix expansion of function designator * teach sparse about '-o ' * teach sparse about '-x ' * cgcc: add support to ignore argument(s) of options * cgcc: teach about '-o ' * cgcc: teach about '-x c' * dump-macro: break the loop at TOKEN_UNTAINT * dump-macro: simplify processing of whitespace * fix implicit K&R argument types * Use -Wimplicit-int when warning about missing K&R argument types * Accept comma-separated list for function declarations. * cgcc: use 'i386' for the arch instead of 'i86' * add testcase for missing deliminator ' or " * man: add section about reporting bugs * man: add AUTHORS section * man: update maintainer info * don't allow newlines inside string literals * multi-buffer for idents * as-name: add and use show_as() * as-name: use idents for address spaces * as-name: allow ident as address_space * as-name: check for multiple address spaces at parsing time * as-named: warn on bad address space * add detection of native platform * Consolidate 'machine detection' into "machine.h" * test endianness with __BYTE_ORDER__ * testsuite: test predef macros on LP32/LP64/LLP64 * fix '__SIZE_TYPE__' for LLP64 * allow optional "_T" suffix to __SIZEOF_XXX__ * use bits_mask() for predefined_max() * add builtin_type_suffix() * teach sparse about asm inline * remove duplicates from gcc-attr-list.h * show-parse: strip do_show_type()'s trailing space * make predefined_type_size() more generic * give a type to wchar * use the type for predefined_max() * add predefined macros for wint_t * add predefined macros for [u]intptr * add predefined macros for [u]intmax * add predefined macros for [u]int{8,16}_t * add predefined macros for [u]int64_t * add predefined macros for [u]int32_t * add predefined macros for char{16,32}_t * fix the size of long double * add predefine for __CHAR_UNSIGNED__ * add predefine_min() and use it for __{WCHAR,WINT}_MIN__ * add a flag to warn on casts to/from bitwise pointers * show-parse: don't display null ident in show_typename() * show-parse: do not display base type's redundant specifiers * show-parse: remove string_ctype from typenames * VERSION=0.6.0-rc1 * build: only need includedir from llvm-config * build: check if sparse-llvm needs libc++ * remove unneeded declarations in "compat.h" * remove unused arg in add_branch() * allocate BBs after the guards * remove redundant check of _Bool bitsize * remove unused regno() * remove -finline-functions from CFLAGS * remove self-assignment of base_type * doc: fix list formatting * as-name: document that identifiers are OK for address spaces * add TODO list. * Sparse v0.6.0 Ramsay Jones (9): * Makefile: use locally built sparse in the selfcheck target * sparsec: use a compatible exception model on cygwin * sparsei: add the --[no-]jit options * constant: add -Wconstant-suffix warning * pre-process: suppress trailing space when dumping macros * pre-process: print macros containing # and ## correctly * pre-process: don't put spaces in macro parameter list * pre-process: print variable argument macros correctly * pre-process: add the -dM option to dump macro definitions Randy Dunlap (6): * sparse: minor manpage corrections * Documentation: make data-structures.txt easier to read * Documentation: editing fixes in test-suite * lib.c: early return from handle_onoff_switch() * sparse: ignore indirect_branch attribute * sparse: option to print compound global data symbol info Thiebaud Weksteen (1): * Add testcases for bitwise cast on pointer Tycho Andersen (1): * expression.h: update comment to include other cast types Uwe Kleine-König (6): * build: make PREFIX overwritable from the environment * build: put comment about local.mk to the place where it is included * build: drop BASIC_CFLAGS and ALL_CFLAGS * build: drop -g from LDFLAGS * build: pass CPPFLAGS to compiler * build: only generate version.h when needed Vincenzo Frascino (1): * print address space number for cast-from-AS warnings sparse-0.6.4/Documentation/release-notes/v0.6.1.rst000066400000000000000000000024341411531012200217500ustar00rootroot00000000000000v0.6.1 (2019-10-14) =================== It's a small, 74 patches, release containing mainly small fixes and improvements: * improve build & test support for distros, mainly Debian * stop warning on externally_visible functions without a prototype * accept casts of __user/__iomem/... pointers to/from uintptr_t * fix the underlying type of some enumeration values * fix a build problem for sparse-llvm by using 'llvm-config --cppflags' * conditionals (?:) may now be considered as constants if the condition is * some error messages are now clearer or more coherent * add missing expansion of compound literals * improve parsing & checking of asm operands * add missing expansion of asm operands * expand some more builtins with constant operands (ffs, clz, ...) * fix sparsec with recent version of cygwin * fix crashes with some tools on toplevel asm. Many thanks to people who have contributed to this release: Uwe Kleine-König, Ramsay Jones, Randy Dunlap, Thomas Weißschuh, Dan Carpenter, Jann Horn, Ben Dooks, Vegard Nossum, Aurelien Aptel, Oliver Hartkopp, Linus Torvalds and Ilya Maximets. The source code can be found at its usual repository: git://git.kernel.org/pub/scm/devel/sparse/sparse.git v0.6.1 The tarballs are found at: https://www.kernel.org/pub/software/devel/sparse/dist/ sparse-0.6.4/Documentation/release-notes/v0.6.2.rst000066400000000000000000000077611411531012200217610ustar00rootroot00000000000000v0.6.2 (2020-06-21) =================== * add a new tool: sindex - the semantic utility Sindex is a simple to use cscope-like tool but understanding how symbols are used and which can track struct members. * add support for GCC's __auto_type * add support for _Generic * fully propagate declarations downward. For example, it means that code like:: static int foo(void); int foo(void) { return 0; } now behaves as expected: foo() is effectively static. * multi-arch: * allow a single sparse executable to be used for multiple architectures * add support for -mcmodel & -f{pic,PIC,pie,PIE}, mainly for RISC-V * add new option, --arch=$ARCH, to specify the target architecture * move all arch-specific code into separate files (target-$ARCH.c) * try to support the various floating-point ABIs on ARM * fix wchar_t & wint_t for openbsd * add missing predefines for PPC * add missing predefines: __amd64 & __amd64__ * sparc32 on SunOS/Solaris uses 128-bit long double * fix wchar_t & wint_t on SunOS/Solaris * teach sparse about -fshort-wchar * keep cygwin specifics with i386/x86-64 specifics * keep BSD & Darwin specifics with i386/x86-64 specifics * fix the signedness of plain chars * add support for s390 (ILP32) * add predefine for __mips__ * predefine "i386" if needed * pre-define __unix__ and friends * add necessary defined for sunos-derived systems * improved detection of the native OS * warnings: * improve diagnostic message about wrong redeclaration * conditionally accept { 0 } without warnings * add -Wexternal-function-has-definition * display the bitfield name in error messages * oversized bitfields are now errors * add an option to suppress warning 'no newline at EOF' * warn when jumping into statement expressions * warn when using undefined labels * warn on defined but unused labels * attributes: * allows '____' for all attributes. * improve handling of function attributes * separate modifiers into type/declaration * add support for attributes 'unused' & 'gnu_inline' * simplify parsing of inline/__tls/__visible * better handle function-only attributes * teach sparse about gnu_inline * parse enum attributes and, for now, ignore them * cgcc: * use -fshort-char for Cygwin * add support for riscv32 & riscv64 * don't define __CYGWIN32__ on 64-bit * filter-out sparse-specific -msize-long & -msize-llp64 * use -mfloat-abi=hard for armhf * define _BIG_ENDIAN when needed * remove definition of _STRING_ARCH_unaligned (defined by glibc) * removed unneeded predefines for integers (now defined by sparse) * better multi-arch support by using --arch=$ARCH * testsuite: * avoid standard includes in the tests * fix testcase with non-constant initializer * IR * add support for the linearization of builtins * generate OP_UNREACH from __builtin_unreachable() * add OP_UNREACH after calls to __noreturn functions * doc: * do not use obsolete sphinx's AutodocReporter * Sphinx's minimal version is now 1.7 * add basic doc about the type system * doc is now accessible as: https://sparse.docs.kernel.org * release notes (old and current ones) have been added to the doc * now using the sphinx_rtd_theme instead of the classic theme * misc: * add support for '-std=c17/c18' * simplify testing of which version of the standard is used * ensure that typeofs are evaluated before using show_typename() * use a single way to expand typeofs * various improvements to the 'dissect' tool * simplify the parsing of type specifiers * improve diagnostic messages concerning bitfields * fix premature examination of dereferenced object * various fixes for the expansion of constant symbols * fix type compatibility of _Atomic types * add support for builtin macros with argument * add support for __has_feature() & __has_extension() sparse-0.6.4/Documentation/release-notes/v0.6.3.rst000066400000000000000000000050321411531012200217470ustar00rootroot00000000000000v0.6.3 (2020-10-17) =================== Bug fixes: * fix missing inlining of _Generic expression * fix evaluation error with assignment of qualified arrays * delay 'empty character constant' warning to phase 5 * simplify & fix parsing of array declarators * accept whitespace after option -U * teach dissect about _Generic * reset locale after gtk_init() to workaround problems with strtold() * fix linearization of shift-assign * force to 0 expressions which are erroneously non-constant * fix evaluate_ptr_add() when sizeof(offset) != sizeof(pointer) * fix access to defining instruction in simplify_unop() * fix evaluation of pointer to bool conversions * fix usual conversion of integers * fix null pointer deref on return expression with invalid type New features: * add support for arch specific asm constraints * add memory asm constraint for PPC & S390 * prepend diagnostics with source's path and include chain * add support for h8300, microblaze, nds32, openrisc, sh & xtensa * add support for '-march=....' and use it for riscv * add an option to specify the OS: --os=$OS * add predefines for OS identification * add predefines for __INT_LEAST${N}_TYPE__ & __INT_FAST${N}_TYPE__ * document the sparse's extensions * sindex/semind: allow indexing outside the project tree * rename tool 'sindex' to 'semind' * add builtin support for __sync_{bool,val}_compare_and_swap() * add support for wide strings * union-cast: teach sparse about union casts * add support for a new instruction: OP_FMADD * add various warnings for dangerous usage of flexible array members * add builtin support for __builtin_ia32_pause() Misc changes: * cleanup the handling of options flags * avoid multiple warnings when inlining undeclared calls * small fixes for alpha, arm, nios2, ppc, sparc & x86 * add missing predefines for endianness on arm, arm64, mips * add various missing arch-specific predefines * add the predefines '__cdecl', ... on cygwin * warn on empty assignments & initializations * reorganize the keyword parsing table * the message in _Static_assert() is now optional (C2x) * small fixes & improvement to the [online] documentation * allow [*] in array declarators * do not accept comma expressions in array declarator * simplify parsing of attributes & storage class * bad-shift: wait dead code elimination to warn about bad shifts * fix is_scalar_type(): fouled types are scalars too * better support for linearization of builtins * remove definition of removed OP_{AND,OR}_BOOL sparse-0.6.4/Documentation/release-notes/v0.6.4.rst000066400000000000000000000111021411531012200217430ustar00rootroot00000000000000v0.6.4 (2020-09-06) =================== Fixes: * build: fix version.h dependencies * fix and complete the evaluation of atomic builtins * fix some testcases related to bitfield manipulation * llvm: fix crash with llvm-11 / use real phi-nodes * fix: OP_INLINE should not use the function symbol * fix testing if a OP_CALL's function is pure * warn on all missing parameter types * fix init_linearized_builtins() * fix usage count in linearize_fma() * linearize: fix a couple of 'selfcheck' warnings * cfg: remove phi-sources when merging BBs * cfg: remove phi-nodes when merging BBs * cfg: add missing REPEAT_CFG_CLEANUP * fix: rebuild dominance tree during CFG cleanup * fix: drop qualifiers of casts and comma or statement expressions * fix kill_insn(OP_SETVAL) * fix trivial_phi() when the target is before the single value * memops: fix wrong killing of stores partially dominated by a load * memops: kill dead loads before phi-node conversion * memops: kill more dead stores * fix rem_usage() when the pseudo has a use list but is not PSEUDO_REG * shut up a silly -Wmaybe-uninitialized warning * fix add_join_conditional() when one of the alternative is VOID * asm: fix killing OP_ASM * asm: fix a test failure on 32-bit systems * asm: output *memory* operands need their address as *input* * asm: teach dominates() about OP_ASM * fix the type in the assignment of 0 to a restricted variable * fix SSA conversion of mismatched memops * fix and improve the check that protects try_to_simplify_bb() * fix remove_merging_phisrc() with duplicated CFG edges. * fix null-pointer crash with with ident same as one of the attributes New: * improve CFG simplification * teach sparse about -funsigned-bitfields * add a symbolic checker * expand __builtin_object_size() * let plain bitfields default to signed * add support for __packed struct * handle qualified anonymous structures * move check_access() to late_warnings() * let phi-sources to directly access to their phi-node * small improvements to the ptrlist API * warn when taking the address of a built-in function * handle more graciously labels with no statement * give an explicit type to compare's operands * give a type to OP_SYMADDR * add some notes about pseudos being typeless * shrink struct basic_block * pre-proc: strip leading "./" from include paths * pre-proc: do some path normalization * linearize __builtin_isdigit() IR Simplifications: * simplify: essential OP_ADD & OP_SUB simplifications * simplify and canonicalize unsigned compares * simplify: basic unop simplifications * simplify SEL(SEL(...), ...) * simplify SEL(x == y, x, y) and friends * simplify SEL(x, x, x) and SEL(x, 0, x) * simplify & canonicalize compares * simplify CBR-CBR on the same condition * simplify unrestricted postop * simplification of computed gotos with 1 or 2 targets * simplify kill_insn() of unops and unop-ish instructions * simplify: put PSEUDO_ARGS and PSEUDO_REGs in canonical order too * simplify (~x {&,|,^} x) --> {0,~0,~0} * simplify ((x cmp y) {&,|,^} (x !cmp y)) --> {0,1,1} * simplify LSR + SEXT into ASR * simplify and canonicalize signed compares * simplify CMP(AND(x,M), C) and CMP(OR(x,M), C) * simplify AND(x >= 0, x < C) --> (unsigned)x < C * simplify TRUNC(x) {==,!=} C --> AND(x,M) {==,!=} C * simplify of TRUNC(NOT(x)) --> NOT(TRUNC(x)) * factorize (x OP1 z) OP2 (y OP1 z) into (x OP2 y) OP1 z * factorize SHIFT(x, s) OP SHIFT(y, s) into SHIFT((x OP y), s) * factorize SEL(x, OP(y,z), y) into OP(SEL(x, z, 0), y) * convert SEL(x & BIT1, BIT2, 0) into SHIFT(x & BIT1, S) * canonicalize ((x & M) == M) --> ((x & M) != 0) when M is a power-of-2 Testsuite: * testsuite: add new tags: check-output-{match,returns} * testsuite: fix parsing of tags used in the testcases * testsuite: add option '-r' to 'test-suite format' Documentation: * doc: fix: Sphinx's option ':noindex:' renamed into ':noindexentry:' * doc: fix extracted autodoc when short description ends with a '?' * doc: add some doc about using NULL or VOID in pointer lists * doc: add some doc to flowgraph.h * doc: extract doc related to simplification Cleanups: * slice: small reorg of OP_SLICE in preparation for some incoming changes * cleanup: removed an unused parameter for show_symbol_list() * cleanup linearize_cond_branch() * cleanup: remove unneeded REPEAT_SYMBOL_CLEANUP * cleanup: no needs to use MARK_CURRENT_DELETED() for multi-jumps * linearize: remove unneeded forward declarations * linearize: only allocate call instructions when needed sparse-0.6.4/Documentation/sphinx/000077500000000000000000000000001411531012200171265ustar00rootroot00000000000000sparse-0.6.4/Documentation/sphinx/cdoc.py000077500000000000000000000170001411531012200204110ustar00rootroot00000000000000#!/usr/bin/env python # SPDX_License-Identifier: MIT # # Copyright (C) 2018 Luc Van Oostenryck # """ /// // Sparse source files may contain documentation inside block-comments // specifically formatted:: // // /// // // Here is some doc // // and here is some more. // // More precisely, a doc-block begins with a line containing only ``///`` // and continues with lines beginning by ``//`` followed by either a space, // a tab or nothing, the first space after ``//`` is ignored. // // For functions, some additional syntax must be respected inside the // block-comment:: // // /// // // // // // // @<1st parameter's name>: // // @<2nd parameter's name>: which needs multiple lines> // // @return: (absent for void functions) // // // // // int somefunction(void *ptr, int count); // // Inside the description fields, parameter's names can be referenced // by using ``@``. A function doc-block must directly precede // the function it documents. This function can span multiple lines and // can either be a function prototype (ending with ``;``) or a // function definition. // // Some future versions will also allow to document structures, unions, // enums, typedefs and variables. // // This documentation can be extracted into a .rst document by using // the *autodoc* directive:: // // .. c:autodoc:: file.c // """ import re class Lines: def __init__(self, lines): # type: (Iterable[str]) -> None self.index = 0 self.lines = lines self.last = None self.back = False def __iter__(self): # type: () -> Lines return self def memo(self): # type: () -> Tuple[int, str] return (self.index, self.last) def __next__(self): # type: () -> Tuple[int, str] if not self.back: self.last = next(self.lines).rstrip() self.index += 1 else: self.back = False return self.memo() def next(self): return self.__next__() def undo(self): # type: () -> None self.back = True def readline_multi(lines, line): # type: (Lines, str) -> str try: while True: (n, l) = next(lines) if not l.startswith('//\t'): raise StopIteration line += '\n' + l[3:] except: lines.undo() return line def readline_delim(lines, delim): # type: (Lines, Tuple[str, str]) -> Tuple[int, str] try: (lineno, line) = next(lines) if line == '': raise StopIteration while line[-1] not in delim: (n, l) = next(lines) line += ' ' + l.lstrip() except: line = '' return (lineno, line) def process_block(lines): # type: (Lines) -> Dict[str, Any] info = { } tags = [] desc = [] state = 'START' (n, l) = lines.memo() #print('processing line ' + str(n) + ': ' + l) ## is it a single line comment ? m = re.match(r"^///\s+(.+)$", l) # /// ... if m: info['type'] = 'single' info['desc'] = (n, m.group(1).rstrip()) return info ## read the multi line comment for (n, l) in lines: #print('state %d: %4d: %s' % (state, n, l)) if l.startswith('// '): l = l[3:] ## strip leading '// ' elif l.startswith('//\t') or l == '//': l = l[2:] ## strip leading '//' else: lines.undo() ## end of doc-block break if state == 'START': ## one-line short description info['short'] = (n ,l) state = 'PRE-TAGS' elif state == 'PRE-TAGS': ## ignore empty line if l != '': lines.undo() state = 'TAGS' elif state == 'TAGS': ## match the '@tagnames' m = re.match(r"^@([\w-]*)(:?\s*)(.*)", l) if m: tag = m.group(1) sep = m.group(2) ## FIXME/ warn if sep != ': ' l = m.group(3) l = readline_multi(lines, l) tags.append((n, tag, l)) else: lines.undo() state = 'PRE-DESC' elif state == 'PRE-DESC': ## ignore the first empty lines if l != '': ## or first line of description desc = [n, l] state = 'DESC' elif state == 'DESC': ## remaining lines -> description desc.append(l) else: pass ## fill the info if len(tags): info['tags'] = tags if len(desc): info['desc'] = desc ## read the item (function only for now) (n, line) = readline_delim(lines, (')', ';')) if len(line): line = line.rstrip(';') #print('function: %4d: %s' % (n, line)) info['type'] = 'func' info['func'] = (n, line) else: info['type'] = 'bloc' return info def process_file(f): # type: (TextIOWrapper) -> List[Dict[str, Any]] docs = [] lines = Lines(f) for (n, l) in lines: #print("%4d: %s" % (n, l)) if l.startswith('///'): info = process_block(lines) docs.append(info) return docs def decorate(l): # type: (str) -> str l = re.sub(r"@(\w+)", "**\\1**", l) return l def convert_to_rst(info): # type: (Dict[str, Any]) -> List[Tuple[int, str]] lst = [] #print('info= ' + str(info)) typ = info.get('type', '???') if typ == '???': ## uh ? pass elif typ == 'bloc': if 'short' in info: (n, l) = info['short'] lst.append((n, l)) if 'desc' in info: desc = info['desc'] n = desc[0] - 1 desc.append('') for i in range(1, len(desc)): l = desc[i] lst.append((n+i, l)) # auto add a blank line for a list if re.search(r":$", desc[i]) and re.search(r"\S", desc[i+1]): lst.append((n+i, '')) elif typ == 'func': (n, l) = info['func'] l = '.. c:function:: ' + l lst.append((n, l + '\n')) if 'short' in info: (n, l) = info['short'] l = l[0].capitalize() + l[1:].strip('.') if l[-1] != '?': l = l + '.' lst.append((n, '\t' + l + '\n')) if 'tags' in info: for (n, name, l) in info.get('tags', []): if name != 'return': name = 'param ' + name l = decorate(l) l = '\t:%s: %s' % (name, l) l = '\n\t\t'.join(l.split('\n')) lst.append((n, l)) lst.append((n+1, '')) if 'desc' in info: desc = info['desc'] n = desc[0] r = '' for l in desc[1:]: l = decorate(l) r += '\t' + l + '\n' lst.append((n, r)) return lst def extract(f, filename): # type: (TextIOWrapper, str) -> List[Tuple[int, str]] res = process_file(f) res = [ i for r in res for i in convert_to_rst(r) ] return res def dump_doc(lst): # type: (List[Tuple[int, str]]) -> None for (n, lines) in lst: for l in lines.split('\n'): print('%4d: %s' % (n, l)) n += 1 if __name__ == '__main__': """ extract the doc from stdin """ import sys dump_doc(extract(sys.stdin, '')) from sphinx.util.docutils import switch_source_input import docutils import os class CDocDirective(docutils.parsers.rst.Directive): required_argument = 1 optional_arguments = 1 has_content = False option_spec = { } def run(self): env = self.state.document.settings.env filename = os.path.join(env.config.cdoc_srcdir, self.arguments[0]) env.note_dependency(os.path.abspath(filename)) ## create a (view) list from the extracted doc lst = docutils.statemachine.ViewList() f = open(filename, 'r') for (lineno, lines) in extract(f, filename): for l in lines.split('\n'): lst.append(l.expandtabs(8), filename, lineno) lineno += 1 ## let parse this new reST content memo = self.state.memo save = memo.title_styles, memo.section_level node = docutils.nodes.section() try: with switch_source_input(self.state, lst): self.state.nested_parse(lst, 0, node, match_titles=1) finally: memo.title_styles, memo.section_level = save return node.children def setup(app): app.add_config_value('cdoc_srcdir', None, 'env') app.add_directive_to_domain('c', 'autodoc', CDocDirective) return { 'version': '1.0', 'parallel_read_safe': True, } # vim: tabstop=4 sparse-0.6.4/Documentation/sphinx/ir.py000077500000000000000000000033411411531012200201160ustar00rootroot00000000000000#!/usr/bin/env python # SPDX_License-Identifier: MIT # # Copyright (C) 2018 Luc Van Oostenryck # """ /// // To document the instructions used in the intermediate representation // a new domain is defined: 'ir' with a directive:: // // .. op: // // ... // // This is equivalent to using a definition list but with the name // also placed in the index (with 'IR instruction' as descriptions). """ import docutils import sphinx class IROpDirective(docutils.parsers.rst.Directive): # use the first line of content as the argument, this allow # to not have to write a blanck line after the directive final_argument_whitespace = True required_argument = 0 #optional_arguments = 0 has_content = True objtype = None def run(self): self.env = self.state.document.settings.env source = self.state.document lineno = self.lineno text = self.content name = text[0] node = docutils.nodes.section() node['ids'].append(name) node.document = source index = '.. index:: pair: %s; IR instruction' % name content = docutils.statemachine.ViewList() content.append(index, source, lineno) content.append('' , source, lineno) content.append(name , source, lineno) content.append('' , source, lineno) self.state.nested_parse(content, self.content_offset, node) defnode = docutils.nodes.definition() self.state.nested_parse(text[1:], self.content_offset, defnode) node.append(defnode) return [node] class IRDomain(sphinx.domains.Domain): """IR domain.""" name = 'ir' def setup(app): app.add_domain(IRDomain) app.add_directive_to_domain('ir', 'op', IROpDirective) return { 'version': '1.0', 'parallel_read_safe': True, } # vim: tabstop=4 sparse-0.6.4/Documentation/sphinx/static/000077500000000000000000000000001411531012200204155ustar00rootroot00000000000000sparse-0.6.4/Documentation/sphinx/static/theme_overrides.css000066400000000000000000000004671411531012200243220ustar00rootroot00000000000000p { margin-bottom: 0.6em; } ul.simple { margin-top: -0.5em; margin-bottom: 0.5em; } .rst-content .toctree-wrapper ul { margin-bottom: 0.5em; } .wy-menu-vertical a, .wy-menu-vertical li.current > a, .wy-menu-vertical p.caption { padding: 0.2em 1.2em; } .wy-side-nav-search > a img.logo { width: 60%; } sparse-0.6.4/Documentation/submitting-patches.md000066400000000000000000000014271411531012200217550ustar00rootroot00000000000000Submitting patches ================== 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.6.4/Documentation/templates/000077500000000000000000000000001411531012200176135ustar00rootroot00000000000000sparse-0.6.4/Documentation/templates/breadcrumbs.html000066400000000000000000000005611411531012200227740ustar00rootroot00000000000000{%- extends "sphinx_rtd_theme/breadcrumbs.html" %} {% block breadcrumbs_aside %} {% if hasdoc(pagename) %}
  • {% if show_source and has_source and sourcename %} {{ _('View page source') }} {% endif %}
  • {% endif %} {% endblock %} sparse-0.6.4/Documentation/templates/layout.html000066400000000000000000000004151411531012200220160ustar00rootroot00000000000000{% extends "!layout.html" %} {% block menu %} {{ super() }}

    Index

    {% endblock %} sparse-0.6.4/Documentation/test-suite.rst000066400000000000000000000116531411531012200204630ustar00rootroot00000000000000Test 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. Tag's syntax ============ ``check-name:`` *name* Name of the test. This is the only mandatory tag. ``check-description:`` *description ...* A description of what the test checks. ``check-command:`` *command arg ...* 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-arch-ignore:`` *arch[|...]* ``check-arch-only:`` *arch[|...]* Ignore the test if the current architecture (as returned by ``uname -m``) matches or not one of the archs given in the pattern. ``check-assert:`` *condition* Ignore the test if the given condition is false when evaluated as a static assertion (``_Static_assert``). ``check-cpp-if:`` *condition* Ignore the test if the given condition is false when evaluated by sparse's pre-processor. ``check-exit-value:`` *value* The expected exit value of check-command. It defaults to 0. ``check-timeout:`` *timeout* The maximum expected duration of check-command, in seconds. It defaults to 1. ``check-output-start`` / ``check-output-end`` 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`` Don't check the expected output (stdout or stderr) of check-command (useful 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`` Mark the test as being known to fail. ``check-output-contains:`` *pattern* 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:`` *pattern* 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(``\ *nbr*\ ``):`` *pattern* ``check-output-pattern(``\ *min*\ ``,``\ *max*\ ``):`` *pattern* Similar to the contains/excludes above, but with full control of the number of times the pattern should occur in the output. If *min* or *max* is ``-`` the corresponding check is ignored. ``check-output-match(``\ *start*\ ``):`` *pattern* Check that in the output (stdout) all lines starting with the first pattern also contains the second pattern. This should be reserved for matching IR instructions since the '.$size' suffix is ignored in the first pattern but is expected to be followed by a space character. ``check-output-returns:`` *value* Check that in the output (stdout) all IR return instructions have the given value. 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 ============== The test-suite comes with a format command to make a test easier to write:: test-suite format [-a] [-l] [-f] 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 formatted 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 */ The same effect without the redirection can be achieved by using the ``-a`` option. You can define the check-command you want to use for the test.:: $ ./test-suite format -a validation/preprocessor2.c "Preprocessor #2" \ "sparse -E \$file" $ 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.6.4/Documentation/types.rst000066400000000000000000000106271411531012200175210ustar00rootroot00000000000000*********** Type System *********** struct symbol is used to represent symbols & types but most parts pertaining to the types are in the field 'ctype'. For the purpose of this document, things can be simplified into: .. code-block:: c struct symbol { enum type type; // SYM_... struct ctype { struct symbol *base_type; unsigned long modifiers; unsigned long alignment; struct context_list *contexts; struct indent *as; }; }; Some bits, also related to the type, are in struct symbol itself: * type * size_bits * rank * variadic * string * designated_init * forced_arg * accessed * transparent_union * ``base_type`` is used for the associated base type. * ``modifiers`` is a bit mask for type specifiers (MOD_UNSIGNED, ...), type qualifiers (MOD_CONST, MOD_VOLATILE), storage classes (MOD_STATIC, MOD_EXTERN, ...), as well for various attributes. It's also used internally to keep track of some states (MOD_ACCESS or MOD_ADDRESSABLE). * ``alignment`` is used for the alignment, in bytes. * ``contexts`` is used to store the informations associated with the attribute ``context()``. * ``as`` is used to hold the identifier of the attribute ``address_space()``. Kind of types ============= SYM_BASETYPE ------------ Used by integer, floating-point, void, 'type', 'incomplete' & bad types. For integer types: * .ctype.base_type points to ``int_ctype``, the generic/abstract integer type * .ctype.modifiers has MOD_UNSIGNED/SIGNED/EXPLICITLY_SIGNED set accordingly. For floating-point types: * .ctype.base_type points to ``fp_ctype``, the generic/abstract float type * .ctype.modifiers is zero. For the other base types: * .ctype.base_type is NULL * .ctype.modifiers is zero. SYM_NODE -------- It's used to make variants of existing types. For example, it's used as a top node for all declarations which can then have their own modifiers, address_space, contexts or alignment as well as the declaration's identifier. Usage: * .ctype.base_type points to the unmodified type (which must not be a SYM_NODE itself) * .ctype.modifiers, .as, .alignment, .contexts will contains the 'variation' (MOD_CONST, the attributes, ...). SYM_PTR ------- For pointers: * .ctype.base_type points to the pointee type * .ctype.modifiers & .as are about the pointee too! SYM_FN ------ For functions: * .ctype.base_type points to the return type * .ctype.modifiers & .as should be about the function itself but some return type's modifiers creep here (for example, in int foo(void), MOD_SIGNED will be set for the function). SYM_ARRAY --------- For arrays: * .ctype.base_type points to the underlying type * .ctype.modifiers & .as are a copy of the parent type (and unused)? * for literal strings, the modifier also contains MOD_STATIC * sym->array_size is *expression* for the array size. SYM_STRUCT ---------- For structs: * .ctype.base_type is NULL * .ctype.modifiers & .as are not used? * .ident is the name tag. SYM_UNION --------- Same as for structs. SYM_ENUM -------- For enums: * .ctype.base_type points to the underlying type (integer) * .ctype.modifiers contains the enum signedness * .ident is the name tag. SYM_BITFIELD ------------ For bitfields: * .ctype.base_type points to the underlying type (integer) * .ctype.modifiers & .as are a copy of the parent type (and unused)? * .bit_size is the size of the bitfield. SYM_RESTRICT ------------ Used for bitwise types (aka 'restricted' types): * .ctype.base_type points to the underlying type (integer) * .ctype.modifiers & .as are like for SYM_NODE and the modifiers are inherited from the base type with MOD_SPECIFIER removed * .ident is the typedef name (if any). SYM_FOULED ---------- Used for bitwise types when the negation op (~) is used and the bit_size is smaller than an ``int``. There is a 1-to-1 mapping between a fouled type and its parent bitwise type. Usage: * .ctype.base_type points to the parent type * .ctype.modifiers & .as are the same as for the parent type * .bit_size is bits_in_int. SYM_TYPEOF ---------- Should not be present after evaluation: * .initializer points to the expression representing the type * .ctype is not used. Typeofs with a type as argument are directly evaluated during parsing. SYM_LABEL --------- Used for labels only. SYM_KEYWORD ----------- Used for parsing only. SYM_BAD ------- Should not be used. SYM_UNINTIALIZED ---------------- Should not be used. sparse-0.6.4/FAQ000066400000000000000000000064771411531012200133540ustar00rootroot00000000000000 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.6.4/LICENSE000066400000000000000000000027261411531012200140200ustar00rootroot00000000000000The '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.6.4/Makefile000066400000000000000000000202111411531012200144400ustar00rootroot00000000000000VERSION=0.6.4 ######################################################################## # The following variables can be overwritten from the command line OS = linux CC = gcc CXX = g++ LD = $(CC) AR = ar CFLAGS ?= -O2 -g DESTDIR ?= PREFIX ?= $(HOME) BINDIR ?= $(PREFIX)/bin MANDIR ?= $(PREFIX)/share/man PKG_CONFIG ?= pkg-config CHECKER_FLAGS ?= -Wno-vla # Allow users to override build settings without dirtying their trees # For debugging, put this in local.mk: # # CFLAGS += -O0 -DDEBUG -g3 -gdwarf-2 # SPARSE_LOCAL_CONFIG ?= local.mk -include ${SPARSE_LOCAL_CONFIG} ######################################################################## LIB_OBJS := LIB_OBJS += allocate.o LIB_OBJS += builtin.o LIB_OBJS += char.o LIB_OBJS += compat-$(OS).o LIB_OBJS += cse.o LIB_OBJS += dissect.o LIB_OBJS += dominate.o LIB_OBJS += evaluate.o LIB_OBJS += expand.o LIB_OBJS += expression.o LIB_OBJS += flow.o LIB_OBJS += flowgraph.o LIB_OBJS += inline.o LIB_OBJS += ir.o LIB_OBJS += lib.o LIB_OBJS += linearize.o LIB_OBJS += liveness.o LIB_OBJS += memops.o LIB_OBJS += opcode.o LIB_OBJS += optimize.o LIB_OBJS += options.o LIB_OBJS += parse.o LIB_OBJS += predefine.o LIB_OBJS += pre-process.o LIB_OBJS += ptrlist.o LIB_OBJS += ptrmap.o LIB_OBJS += scope.o LIB_OBJS += show-parse.o LIB_OBJS += simplify.o LIB_OBJS += sort.o LIB_OBJS += ssa.o LIB_OBJS += stats.o LIB_OBJS += storage.o LIB_OBJS += symbol.o LIB_OBJS += target.o LIB_OBJS += target-alpha.o LIB_OBJS += target-arm.o LIB_OBJS += target-arm64.o LIB_OBJS += target-bfin.o LIB_OBJS += target-default.o LIB_OBJS += target-h8300.o LIB_OBJS += target-m68k.o LIB_OBJS += target-microblaze.o LIB_OBJS += target-mips.o LIB_OBJS += target-nds32.o LIB_OBJS += target-nios2.o LIB_OBJS += target-openrisc.o LIB_OBJS += target-ppc.o LIB_OBJS += target-riscv.o LIB_OBJS += target-s390.o LIB_OBJS += target-sh.o LIB_OBJS += target-sparc.o LIB_OBJS += target-x86.o LIB_OBJS += target-xtensa.o LIB_OBJS += tokenize.o LIB_OBJS += unssa.o LIB_OBJS += utils.o LIB_OBJS += version.o PROGRAMS := PROGRAMS += compile PROGRAMS += ctags PROGRAMS += example PROGRAMS += graph PROGRAMS += obfuscate PROGRAMS += sparse PROGRAMS += test-dissect PROGRAMS += test-lexing PROGRAMS += test-linearize PROGRAMS += test-parsing PROGRAMS += test-show-type PROGRAMS += test-unssa INST_PROGRAMS=sparse cgcc INST_MAN1=sparse.1 cgcc.1 all: ######################################################################## # common flags/options/... cflags = -fno-strict-aliasing cflags += -Wall -Wwrite-strings GCC_BASE := $(shell $(CC) --print-file-name=) cflags += -DGCC_BASE=\"$(GCC_BASE)\" MULTIARCH_TRIPLET := $(shell $(CC) -print-multiarch 2>/dev/null) cflags += -DMULTIARCH_TRIPLET=\"$(MULTIARCH_TRIPLET)\" bindir := $(DESTDIR)$(BINDIR) man1dir := $(DESTDIR)$(MANDIR)/man1 ######################################################################## # target specificities compile: compile-i386.o EXTRA_OBJS += compile-i386.o # Can we use GCC's generated dependencies? HAVE_GCC_DEP:=$(shell touch .gcc-test.c && \ $(CC) -c -Wp,-MP,-MMD,.gcc-test.d .gcc-test.c 2>/dev/null && \ echo 'yes'; rm -f .gcc-test.d .gcc-test.o .gcc-test.c) ifeq ($(HAVE_GCC_DEP),yes) cflags += -Wp,-MP,-MMD,$(@D)/.$(@F).d endif # Can we use libxml (needed for c2xml)? HAVE_LIBXML:=$(shell $(PKG_CONFIG) --exists libxml-2.0 2>/dev/null && echo 'yes') ifeq ($(HAVE_LIBXML),yes) PROGRAMS+=c2xml INST_PROGRAMS+=c2xml c2xml-ldlibs := $(shell $(PKG_CONFIG) --libs libxml-2.0) c2xml-cflags := $(shell $(PKG_CONFIG) --cflags libxml-2.0) else $(warning Your system does not have libxml, disabling c2xml) endif HAVE_SQLITE := $(shell $(PKG_CONFIG) --exists sqlite3 2>/dev/null && echo 'yes') ifeq ($(HAVE_SQLITE),yes) SQLITE_VERSION:=$(shell $(PKG_CONFIG) --modversion sqlite3) SQLITE_VNUMBER:=$(shell printf '%d%02d%02d' $(subst ., ,$(SQLITE_VERSION))) ifeq ($(shell expr "$(SQLITE_VNUMBER)" '>=' 32400),1) PROGRAMS += semind INST_PROGRAMS += semind INST_MAN1 += semind.1 semind-ldlibs := $(shell $(PKG_CONFIG) --libs sqlite3) semind-cflags := $(shell $(PKG_CONFIG) --cflags sqlite3) semind-cflags += -std=gnu99 else $(warning Your SQLite3 version ($(SQLITE_VERSION)) is too old, 3.24.0 or later is required.) endif else $(warning Your system does not have sqlite3, disabling semind) endif # Can we use gtk (needed for test-inspect) 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 ifeq ($(HAVE_GTK),yes) GTK_CFLAGS := $(shell $(PKG_CONFIG) --cflags gtk+-$(GTK_VERSION)) ast-view-cflags := $(GTK_CFLAGS) ast-model-cflags := $(GTK_CFLAGS) ast-inspect-cflags := $(GTK_CFLAGS) test-inspect-cflags := $(GTK_CFLAGS) test-inspect-ldlibs := $(shell $(PKG_CONFIG) --libs gtk+-$(GTK_VERSION)) test-inspect: ast-model.o ast-view.o ast-inspect.o EXTRA_OBJS += ast-model.o ast-view.o ast-inspect.o PROGRAMS += test-inspect INST_PROGRAMS += test-inspect else $(warning Your system does not have gtk3/gtk2, disabling test-inspect) endif # Can we use LLVM (needed for ... sparse-llvm)? LLVM_CONFIG:=llvm-config HAVE_LLVM:=$(shell $(LLVM_CONFIG) --version >/dev/null 2>&1 && echo 'yes') ifeq ($(HAVE_LLVM),yes) arch := $(shell uname -m) ifeq (${MULTIARCH_TRIPLET},x86_64-linux-gnux32) arch := x32 endif ifneq ($(filter ${arch},i386 i486 i586 i686 x86_64 amd64),) LLVM_VERSION:=$(shell $(LLVM_CONFIG) --version) LLVM_VERSION_MAJOR:=$(firstword $(subst ., ,$(LLVM_VERSION))) ifeq ($(shell expr "$(LLVM_VERSION_MAJOR)" '>=' 3),1) LLVM_PROGS := sparse-llvm $(LLVM_PROGS): LD := $(CXX) LLVM_LDFLAGS := $(shell $(LLVM_CONFIG) --ldflags) LLVM_CFLAGS := $(shell $(LLVM_CONFIG) --cppflags) LLVM_LIBS := $(shell $(LLVM_CONFIG) --libs) LLVM_LIBS += $(shell $(LLVM_CONFIG) --system-libs 2>/dev/null) LLVM_LIBS += $(shell $(LLVM_CONFIG) --cxxflags | grep -F -q -e '-stdlib=libc++' && echo -lc++) PROGRAMS += $(LLVM_PROGS) INST_PROGRAMS += sparse-llvm sparsec sparse-llvm-cflags := $(LLVM_CFLAGS) sparse-llvm-ldflags := $(LLVM_LDFLAGS) sparse-llvm-ldlibs := $(LLVM_LIBS) else $(warning LLVM 3.0 or later required. Your system has version $(LLVM_VERSION) installed.) endif else $(warning sparse-llvm disabled on ${arch}) endif else $(warning Your system does not have llvm, disabling sparse-llvm) endif ifeq ($(HAVE_BOOLECTOR),yes) PROGRAMS += scheck scheck-cflags := -I${BOOLECTORDIR}/include/boolector scheck-ldflags := -L${BOOLECTORDIR}/lib scheck-ldlibs := -lboolector -llgl -lbtor2parser endif ######################################################################## LIBS := libsparse.a OBJS := $(LIB_OBJS) $(EXTRA_OBJS) $(PROGRAMS:%=%.o) # Pretty print V := @ Q := $(V:1=) ######################################################################## all: $(PROGRAMS) ldflags += $($(@)-ldflags) $(LDFLAGS) ldlibs += $($(@)-ldlibs) $(LDLIBS) $(PROGRAMS): % : %.o $(LIBS) @echo " LD $@" $(Q)$(LD) $(ldflags) $^ $(ldlibs) -o $@ libsparse.a: $(LIB_OBJS) @echo " AR $@" $(Q)$(AR) rcs $@ $^ cflags += $($(*)-cflags) $(CPPFLAGS) $(CFLAGS) %.o: %.c @echo " CC $@" $(Q)$(CC) $(cflags) -c -o $@ $< %.sc: %.c sparse @echo " CHECK $<" $(Q)CHECK=./sparse ./cgcc -no-compile $(CHECKER_FLAGS) $(cflags) -c $< selfcheck: $(OBJS:.o=.sc) SPARSE_VERSION:=$(shell git describe --dirty 2>/dev/null || echo '$(VERSION)') version.o: version.h version.h: FORCE @echo '#define SPARSE_VERSION "$(SPARSE_VERSION)"' > version.h.tmp @if cmp -s version.h version.h.tmp; then \ rm version.h.tmp; \ else \ echo " GEN $@"; \ mv version.h.tmp version.h; \ fi check: all $(Q)cd validation && ./test-suite validation/%: $(PROGRAMS) FORCE $(Q)validation/test-suite $* clean: clean-check @rm -f *.[oa] .*.d $(PROGRAMS) version.h clean-check: @echo " CLEAN" @find validation/ \( -name "*.c.output.*" \ -o -name "*.c.error.*" \ -o -name "*.o" \ \) -exec rm {} \; install: install-bin install-man install-bin: $(INST_PROGRAMS:%=$(bindir)/%) install-man: $(INST_MAN1:%=$(man1dir)/%) $(bindir)/%: % @echo " INSTALL $@" $(Q)install -D $< $@ || exit 1; $(man1dir)/%: % @echo " INSTALL $@" $(Q)install -D -m 644 $< $@ || exit 1; .PHONY: FORCE # GCC's dependencies -include $(OBJS:%.o=.%.o.d) sparse-0.6.4/README000066400000000000000000000056771411531012200137030ustar00rootroot00000000000000 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(filelist, file) { action(sparse(file)); } END_FOR_EACH_PTR(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.6.4/allocate.c000066400000000000000000000112501411531012200147330ustar00rootroot00000000000000/* * 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"); if (size > chunking) die("alloc too big"); 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, %lu bytes (%lu 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(asm_operand, "asmops"); 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.6.4/allocate.h000066400000000000000000000052571411531012200147520ustar00rootroot00000000000000#ifndef ALLOCATE_H #define ALLOCATE_H #include "compat.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; unsigned long 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(asm_operand); 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.6.4/ast-inspect.c000066400000000000000000000153141411531012200154060ustar00rootroot00000000000000 #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_TYPEOF] = "SYM_TYPEOF", [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.6.4/ast-inspect.h000066400000000000000000000005121411531012200154050ustar00rootroot00000000000000 #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.6.4/ast-model.c000066400000000000000000000346021411531012200150420ustar00rootroot00000000000000/* * 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.6.4/ast-model.h000066400000000000000000000043061411531012200150450ustar00rootroot00000000000000 /* * 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.6.4/ast-view.c000066400000000000000000000022151411531012200147070ustar00rootroot00000000000000 #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.6.4/ast-view.h000066400000000000000000000001371411531012200147150ustar00rootroot00000000000000 #include #include "lib.h" extern void treeview_main(struct symbol_list *syms); sparse-0.6.4/bitmap.h000066400000000000000000000026341411531012200144360ustar00rootroot00000000000000#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.6.4/bits.h000066400000000000000000000027011411531012200141160ustar00rootroot00000000000000/* SPDX-License-Identifier: MIT */ /* * Helper functions for manipulation & testing of integer values * like zero or sign-extensions. * * Copyright (C) 2017 Luc Van Oostenryck * */ #ifndef BITS_H #define BITS_H static inline unsigned long long sign_bit(unsigned size) { return 1ULL << (size - 1); } static inline unsigned long long sign_mask(unsigned size) { unsigned long long sbit = sign_bit(size); return sbit - 1; } static inline unsigned long long bits_mask(unsigned size) { unsigned long long sbit = sign_bit(size); return sbit | (sbit - 1); } static inline long long zero_extend(long long val, unsigned size) { return val & bits_mask(size); } static inline long long sign_extend(long long val, unsigned size) { if (val & sign_bit(size)) val |= ~sign_mask(size); return val; } /// // sign extend @val but only if exactly representable static inline long long sign_extend_safe(long long val, unsigned size) { unsigned long long mask = bits_mask(size); if (!(val & ~mask)) val = sign_extend(val, size); return val; } static inline long long bits_extend(long long val, unsigned size, int is_signed) { val = zero_extend(val, size); if (is_signed) val = sign_extend(val, size); return val; } static inline int is_power_of_2(long long val) { return val && !(val & (val - 1)); } /// // log base 2 of an exact power-of-2 static inline int log2_exact(unsigned long long val) { return 8 * sizeof(val) - __builtin_clzl(val) - 1; } #endif sparse-0.6.4/builtin.c000066400000000000000000000740051411531012200146240ustar00rootroot00000000000000/* * 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 "builtin.h" #include "expression.h" #include "evaluate.h" #include "expand.h" #include "symbol.h" #include "compat/bswap.h" #include #define dyntype incomplete_ctype static bool is_dynamic_type(struct symbol *t) { if (t->type == SYM_NODE) t = t->ctype.base_type; return t == &dyntype; } static int evaluate_to_int_const_expr(struct expression *expr) { expr->ctype = &int_ctype; expr->flags |= CEF_SET_ICE; return 1; } static int evaluate_pure_unop(struct expression *expr) { struct expression *arg = first_expression(expr->args); int flags = arg->flags; /* * Allow such functions with a constant integer expression * argument to be treated as a *constant* integer. * This allow us to use them in switch() { case ...: */ flags |= (flags & CEF_ICE) ? CEF_SET_INT : 0; expr->flags = flags; return 1; } /* * eval_args - check the number of arguments and evaluate them. */ static int eval_args(struct expression *expr, int n) { struct expression *arg; struct symbol *sym; const char *msg; int rc = 1; FOR_EACH_PTR(expr->args, arg) { if (n-- == 0) { msg = "too many arguments"; goto error; } if (!evaluate_expression(arg)) rc = 0; } END_FOR_EACH_PTR(arg); if (n > 0) { msg = "not enough arguments"; goto error; } return rc; error: sym = expr->fn->ctype; expression_error(expr, "%s for %s", msg, show_ident(sym->ident)); return 0; } static int args_prototype(struct expression *expr) { struct symbol *fntype = expr->fn->ctype->ctype.base_type; int n = symbol_list_size(fntype->arguments); return eval_args(expr, n); } static int args_triadic(struct expression *expr) { return eval_args(expr, 3); } 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_int_const_expr, .expand = expand_constant_p }; static struct symbol_op safe_p_op = { .evaluate = evaluate_to_int_const_expr, .expand = expand_safe_p }; static struct symbol_op warning_op = { .evaluate = evaluate_to_int_const_expr, .expand = expand_warning }; static struct symbol_op expect_op = { .expand = expand_expect }; static struct symbol_op choose_op = { .args = args_triadic, .evaluate = evaluate_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 = { .evaluate = evaluate_pure_unop, .expand = expand_bswap, }; #define EXPAND_FINDBIT(name) \ static int expand_##name(struct expression *expr, int cost) \ { \ struct expression *arg; \ long long val; \ \ if (cost) \ return cost; \ \ arg = first_expression(expr->args); \ val = get_expression_value_silent(arg); \ switch (arg->ctype->bit_size) { \ case sizeof(int) * 8: \ val = __builtin_##name(val); break; \ case sizeof(long long) * 8: \ val = __builtin_##name##ll(val); break; \ default: /* impossible error */ \ return SIDE_EFFECTS; \ } \ \ expr->value = val; \ expr->type = EXPR_VALUE; \ expr->taint = 0; \ return 0; \ } \ \ static struct symbol_op name##_op = { \ .evaluate = evaluate_pure_unop, \ .expand = expand_##name, \ } EXPAND_FINDBIT(clz); EXPAND_FINDBIT(ctz); EXPAND_FINDBIT(clrsb); EXPAND_FINDBIT(ffs); EXPAND_FINDBIT(parity); EXPAND_FINDBIT(popcount); static int evaluate_fp_unop(struct expression *expr) { struct expression *arg; if (!eval_args(expr, 1)) return 0; arg = first_expression(expr->args); if (!is_float_type(arg->ctype)) { expression_error(expr, "non-floating-point argument in call to %s()", show_ident(expr->fn->ctype->ident)); return 0; } return 1; } static struct symbol_op fp_unop_op = { .evaluate = evaluate_fp_unop, }; static int expand_isdigit(struct expression *expr, int cost) { struct expression *arg = first_expression(expr->args); long long val = get_expression_value_silent(arg); if (cost) return cost; expr->value = (val >= '0') && (val <= '9'); expr->type = EXPR_VALUE; expr->taint = 0; return 0; } static struct symbol_op isdigit_op = { .evaluate = evaluate_pure_unop, .expand = expand_isdigit, }; static int evaluate_overflow_gen(struct expression *expr, int ptr) { struct expression *arg; int n = 0; /* there will be exactly 3; we'd already verified that */ FOR_EACH_PTR(expr->args, arg) { struct symbol *type; n++; if (!arg || !(type = arg->ctype)) return 0; // 1st & 2nd args must be a basic integer type // 3rd arg must be a pointer to such a type. if (n == 3 && ptr) { if (type->type == SYM_NODE) type = type->ctype.base_type; if (!type) return 0; if (type->type != SYM_PTR) goto err; type = type->ctype.base_type; if (!type) return 0; } if (type->type == SYM_NODE) type = type->ctype.base_type; if (!type) return 0; if (type->ctype.base_type != &int_type || type == &bool_ctype) goto err; } END_FOR_EACH_PTR(arg); // the builtin returns a bool expr->ctype = &bool_ctype; return 1; err: sparse_error(arg->pos, "invalid type for argument %d:", n); info(arg->pos, " %s", show_typename(arg->ctype)); expr->ctype = &bad_ctype; return 0; } static int evaluate_overflow(struct expression *expr) { return evaluate_overflow_gen(expr, 1); } static struct symbol_op overflow_op = { .args = args_triadic, .evaluate = evaluate_overflow, }; static int evaluate_overflow_p(struct expression *expr) { return evaluate_overflow_gen(expr, 0); } static struct symbol_op overflow_p_op = { .args = args_triadic, .evaluate = evaluate_overflow_p, }; /// // Evaluate the arguments of 'generic' integer operators. // // Parameters with a complete type are used like in a normal prototype. // The first parameter with a 'dynamic' type will be consider // as polymorphic and for each calls will be instancied with the type // of its effective argument. // The next dynamic parameters will the use this polymorphic type. // This allows to declare functions with some parameters having // a type variably defined at call time: // int foo(int, T, T); static int evaluate_generic_int_op(struct expression *expr) { struct symbol *fntype = expr->fn->ctype->ctype.base_type; struct symbol_list *types = NULL; struct symbol *ctype = NULL; struct expression *arg; struct symbol *t; int n = 0; PREPARE_PTR_LIST(fntype->arguments, t); FOR_EACH_PTR(expr->args, arg) { n++; if (!is_dynamic_type(t)) { ; } else if (!ctype) { // first 'dynamic' type, check that it's an integer t = arg->ctype; if (!t) return 0; if (t->type == SYM_NODE) t = t->ctype.base_type; if (!t) return 0; if (t->ctype.base_type != &int_type) goto err; // next 'dynamic' arguments will use this type ctype = t; } else { // use the previous 'dynamic' type t = ctype; } add_ptr_list(&types, t); NEXT_PTR_LIST(t); } END_FOR_EACH_PTR(arg); FINISH_PTR_LIST(t); return evaluate_arguments(types, expr->args); err: sparse_error(arg->pos, "non-integer type for argument %d:", n); info(arg->pos, " %s", show_typename(arg->ctype)); expr->ctype = &bad_ctype; return 0; } struct symbol_op generic_int_op = { .args = args_prototype, .evaluate = evaluate_generic_int_op, }; static int eval_atomic_common(struct expression *expr) { struct symbol *fntype = expr->fn->ctype->ctype.base_type; struct symbol_list *types = NULL; struct symbol *ctype = NULL; struct symbol *t; struct expression *arg; int n = 0; // The number of arguments has already be verified. // The first arg must be a pointer to an integral type. PREPARE_PTR_LIST(fntype->arguments, t); FOR_EACH_PTR(expr->args, arg) { struct symbol *ptrtype = NULL; if (++n == 1) { t = arg->ctype; if (!t) return 0; if (t->type == SYM_NODE) t = t->ctype.base_type; if (!t) return 0; if (t->type != SYM_PTR) goto err; ptrtype = t; t = t->ctype.base_type; if (!t) return 0; if (t->type == SYM_NODE) t = t->ctype.base_type; if (!t) return 0; if (t->type != SYM_PTR && t->ctype.base_type != &int_type) goto err; ctype = t; t = ptrtype; } else if (is_dynamic_type(t)) { t = ctype; } else if (t == &ptr_ctype) { t = ptrtype; } add_ptr_list(&types, t); NEXT_PTR_LIST(t); } END_FOR_EACH_PTR(arg); FINISH_PTR_LIST(t); if (!expr->ctype) // set the return type, if needed expr->ctype = ctype; return evaluate_arguments(types, expr->args); err: sparse_error(arg->pos, "invalid type for argument %d:", n); info(arg->pos, " %s", show_typename(arg->ctype)); expr->ctype = &bad_ctype; return 0; } static struct symbol_op atomic_op = { .args = args_prototype, .evaluate = eval_atomic_common, }; /// // expand __builtin_object_size() // // :note: type 1 and type 3 are not supported because the // needed information isn't available after evaluation. static int expand_object_size(struct expression *expr, int cost) { struct expression *arg = first_expression(expr->args); int type = get_expression_value_silent(ptr_list_nth(expr->args, 1)); unsigned long val = -1, off = 0; while (arg) { switch (arg->type) { case EXPR_IMPLIED_CAST: case EXPR_CAST: // ignore those arg = arg->cast_expression; continue; case EXPR_BINOP: // a constant add is (maybe) an offset if (!arg->right || arg->op != '+' || arg->right->type != EXPR_VALUE) break; off += arg->right->value; arg = arg->left; continue; case EXPR_PREOP: // a deref is just intermediate variable // and so the offset needs to be zeroed. if (arg->op == '*') { arg = arg->unop; off = 0; switch (arg->type) { case EXPR_SYMBOL: arg = arg->symbol->initializer; continue; default: break; } } break; case EXPR_SYMBOL: // the symbol we're looking after val = bits_to_bytes(arg->symbol->bit_size); break; case EXPR_CALL: // use alloc_size() attribute but only after linearization. return UNSAFE; default: break; } break; } if (val == -1) val = (type & 2) ? 0 : val; else if (type & 1) return UNSAFE; else val -= off; expr->flags |= CEF_SET_ICE; expr->type = EXPR_VALUE; expr->value = val; expr->taint = 0; return 0; } static struct symbol_op object_size_op = { .expand = expand_object_size, }; /* * Builtin functions */ static struct symbol size_t_alias; static struct symbol *get_ctype(struct symbol *sym) { if (sym == &size_t_alias) return size_t_ctype; return sym; } static void declare_builtin(int stream, const struct builtin_fn *entry) { struct symbol *sym = create_symbol(stream, entry->name, SYM_NODE, NS_SYMBOL); struct symbol *fun = alloc_symbol(sym->pos, SYM_FN); struct symbol *arg; int i; sym->ctype.base_type = fun; sym->ctype.modifiers = MOD_TOPLEVEL; sym->builtin = 1; sym->op = entry->op; fun->ctype.base_type = get_ctype(entry->ret_type); fun->variadic = entry->variadic; for (i = 0; (arg = entry->args[i]); i++) { struct symbol *anode = alloc_symbol(sym->pos, SYM_NODE); anode->ctype.base_type = get_ctype(arg); add_symbol(&fun->arguments, anode); } } void declare_builtins(int stream, const struct builtin_fn tbl[]) { if (!tbl) return; while (tbl->name) declare_builtin(stream, tbl++); } static const struct builtin_fn builtins_common[] = { #define size_t_ctype &size_t_alias #define va_list_ctype &ptr_ctype #define vol_ptr &volatile_ptr_ctype { "__atomic_add_fetch", NULL, 0, { vol_ptr, &dyntype, &int_ctype }, .op = &atomic_op }, { "__atomic_always_lock_free", &bool_ctype, 0, { size_t_ctype, vol_ptr }}, { "__atomic_and_fetch", NULL, 0, { vol_ptr, &dyntype, &int_ctype }, .op = &atomic_op }, { "__atomic_clear", &void_ctype, 0, { &volatile_bool_ptr_ctype, &int_ctype }}, { "__atomic_compare_exchange", &bool_ctype, 0, { vol_ptr, &ptr_ctype, &ptr_ctype, &bool_ctype, &int_ctype, &int_ctype }, .op = &atomic_op }, { "__atomic_compare_exchange_n", &bool_ctype, 0, { vol_ptr, &ptr_ctype, &dyntype, &bool_ctype, &int_ctype, &int_ctype }, .op = &atomic_op }, { "__atomic_exchange", &void_ctype, 0, { vol_ptr, &ptr_ctype, &ptr_ctype, &int_ctype }, .op = &atomic_op }, { "__atomic_exchange_n", NULL, 0, { vol_ptr, &dyntype, &int_ctype }, .op = &atomic_op }, { "__atomic_fetch_add", NULL, 0, { vol_ptr, &dyntype, &int_ctype }, .op = &atomic_op }, { "__atomic_fetch_and", NULL, 0, { vol_ptr, &dyntype, &int_ctype }, .op = &atomic_op }, { "__atomic_fetch_nand",NULL, 0, { vol_ptr, &dyntype, &int_ctype }, .op = &atomic_op }, { "__atomic_fetch_or", NULL, 0, { vol_ptr, &dyntype, &int_ctype }, .op = &atomic_op }, { "__atomic_fetch_sub", NULL, 0, { vol_ptr, &dyntype, &int_ctype }, .op = &atomic_op }, { "__atomic_fetch_xor", NULL, 0, { vol_ptr, &dyntype, &int_ctype }, .op = &atomic_op }, { "__atomic_is_lock_free", &bool_ctype, 0, { size_t_ctype, vol_ptr }}, { "__atomic_load", &void_ctype, 0, { vol_ptr, &ptr_ctype, &int_ctype }, .op = &atomic_op }, { "__atomic_load_n", NULL, 0, { vol_ptr, &int_ctype }, .op = &atomic_op }, { "__atomic_nand_fetch",NULL, 0, { vol_ptr, &dyntype, &int_ctype }, .op = &atomic_op }, { "__atomic_or_fetch", NULL, 0, { vol_ptr, &dyntype, &int_ctype }, .op = &atomic_op }, { "__atomic_signal_fence", &void_ctype, 0, { &int_ctype }}, { "__atomic_store", &void_ctype, 0, { vol_ptr, &ptr_ctype, &int_ctype }, .op = &atomic_op }, { "__atomic_store_n", &void_ctype, 0, { vol_ptr, &dyntype, &int_ctype }, .op = &atomic_op }, { "__atomic_sub_fetch", NULL, 0, { vol_ptr, &dyntype, &int_ctype }, .op = &atomic_op }, { "__atomic_test_and_set", &bool_ctype, 0, { vol_ptr, &int_ctype }}, { "__atomic_thread_fence", &void_ctype, 0, { &int_ctype }}, { "__atomic_xor_fetch", NULL, 0, { vol_ptr, &dyntype, &int_ctype }, .op = &atomic_op }, { "__builtin_choose_expr", NULL, 1, .op = &choose_op }, { "__builtin_constant_p", NULL, 1, .op = &constant_p_op }, { "__builtin_expect", &long_ctype, 0, { &long_ctype ,&long_ctype }, .op = &expect_op }, { "__builtin_safe_p", NULL, 1, .op = &safe_p_op }, { "__builtin_warning", NULL, 1, .op = &warning_op }, { "__builtin_abort", &void_ctype, 0 }, { "__builtin_abs", &int_ctype , 0, { &int_ctype }}, { "__builtin_add_overflow", &bool_ctype, 1, .op = &overflow_op }, { "__builtin_add_overflow_p", &bool_ctype, 1, .op = &overflow_p_op }, { "__builtin_alloca", &ptr_ctype, 0, { size_t_ctype }}, { "__builtin_bcmp", &int_ctype , 0, { &const_ptr_ctype, &const_ptr_ctype, size_t_ctype }}, { "__builtin_bcopy", &void_ctype, 0, { &const_ptr_ctype, &ptr_ctype, size_t_ctype }}, { "__builtin_bswap16", &ushort_ctype, 0, { &ushort_ctype }, .op = &bswap_op }, { "__builtin_bswap32", &uint_ctype, 0, { &uint_ctype }, .op = &bswap_op }, { "__builtin_bswap64", &ullong_ctype, 0, { &ullong_ctype }, .op = &bswap_op }, { "__builtin_bzero", &void_ctype, 0, { &ptr_ctype, size_t_ctype }}, { "__builtin_calloc", &ptr_ctype, 0, { size_t_ctype, size_t_ctype }}, { "__builtin_clrsb", &int_ctype, 0, { &int_ctype }, .op = &clrsb_op }, { "__builtin_clrsbl", &int_ctype, 0, { &long_ctype }, .op = &clrsb_op }, { "__builtin_clrsbll", &int_ctype, 0, { &llong_ctype }, .op = &clrsb_op }, { "__builtin_clz", &int_ctype, 0, { &int_ctype }, .op = &clz_op }, { "__builtin_clzl", &int_ctype, 0, { &long_ctype }, .op = &clz_op }, { "__builtin_clzll", &int_ctype, 0, { &llong_ctype }, .op = &clz_op }, { "__builtin_ctz", &int_ctype, 0, { &int_ctype }, .op = &ctz_op }, { "__builtin_ctzl", &int_ctype, 0, { &long_ctype }, .op = &ctz_op }, { "__builtin_ctzll", &int_ctype, 0, { &llong_ctype }, .op = &ctz_op }, { "__builtin_exit", &void_ctype, 0, { &int_ctype }}, { "__builtin_extract_return_addr", &ptr_ctype, 0, { &ptr_ctype }}, { "__builtin_fabs", &double_ctype, 0, { &double_ctype }}, { "__builtin_ffs", &int_ctype, 0, { &int_ctype }, .op = &ffs_op }, { "__builtin_ffsl", &int_ctype, 0, { &long_ctype }, .op = &ffs_op }, { "__builtin_ffsll", &int_ctype, 0, { &llong_ctype }, .op = &ffs_op }, { "__builtin_fma", &double_ctype, 0, { &double_ctype, &double_ctype, &double_ctype }}, { "__builtin_fmaf", &float_ctype, 0, { &float_ctype, &float_ctype, &float_ctype }}, { "__builtin_fmal", &ldouble_ctype, 0, { &ldouble_ctype, &ldouble_ctype, &ldouble_ctype }}, { "__builtin_frame_address", &ptr_ctype, 0, { &uint_ctype }}, { "__builtin_free", &void_ctype, 0, { &ptr_ctype }}, { "__builtin_huge_val", &double_ctype, 0 }, { "__builtin_huge_valf", &float_ctype, 0 }, { "__builtin_huge_vall", &ldouble_ctype, 0 }, { "__builtin_index", &string_ctype, 0, { &const_string_ctype, &int_ctype }}, { "__builtin_inf", &double_ctype, 0 }, { "__builtin_inff", &float_ctype, 0 }, { "__builtin_infl", &ldouble_ctype, 0 }, { "__builtin_isdigit", &int_ctype, 0, { &int_ctype }, .op = &isdigit_op }, { "__builtin_isfinite", &int_ctype, 1, .op = &fp_unop_op }, { "__builtin_isgreater", &int_ctype, 0, { &float_ctype, &float_ctype }}, { "__builtin_isgreaterequal", &int_ctype, 0, { &float_ctype, &float_ctype }}, { "__builtin_isinf", &int_ctype, 1, .op = &fp_unop_op }, { "__builtin_isinf_sign", &int_ctype, 1, .op = &fp_unop_op }, { "__builtin_isless", &int_ctype, 0, { &float_ctype, &float_ctype }}, { "__builtin_islessequal", &int_ctype, 0, { &float_ctype, &float_ctype }}, { "__builtin_islessgreater", &int_ctype, 0, { &float_ctype, &float_ctype }}, { "__builtin_isnan", &int_ctype, 1, .op = &fp_unop_op }, { "__builtin_isnormal", &int_ctype, 1, .op = &fp_unop_op }, { "__builtin_isunordered", &int_ctype, 0, { &float_ctype, &float_ctype }}, { "__builtin_labs", &long_ctype, 0, { &long_ctype }}, { "__builtin_llabs", &llong_ctype, 0, { &llong_ctype }}, { "__builtin_malloc", &ptr_ctype, 0, { size_t_ctype }}, { "__builtin_memchr", &ptr_ctype, 0, { &const_ptr_ctype, &int_ctype, size_t_ctype }}, { "__builtin_memcmp", &int_ctype, 0, { &const_ptr_ctype, &const_ptr_ctype, size_t_ctype }}, { "__builtin_memcpy", &ptr_ctype, 0, { &ptr_ctype, &const_ptr_ctype, size_t_ctype }}, { "__builtin_memmove", &ptr_ctype, 0, { &ptr_ctype, &const_ptr_ctype, size_t_ctype }}, { "__builtin_mempcpy", &ptr_ctype, 0, { &ptr_ctype, &const_ptr_ctype, size_t_ctype }}, { "__builtin_memset", &ptr_ctype, 0, { &ptr_ctype, &int_ctype, size_t_ctype }}, { "__builtin_mul_overflow", &bool_ctype, 1, .op = &overflow_op }, { "__builtin_mul_overflow_p", &bool_ctype, 1, .op = &overflow_p_op }, { "__builtin_nan", &double_ctype, 0, { &const_string_ctype }}, { "__builtin_nanf", &float_ctype, 0, { &const_string_ctype }}, { "__builtin_nanl", &ldouble_ctype, 0, { &const_string_ctype }}, { "__builtin_object_size", size_t_ctype, 0, { &const_ptr_ctype, &int_ctype }, .op = &object_size_op}, { "__builtin_parity", &int_ctype, 0, { &uint_ctype }, .op = &parity_op }, { "__builtin_parityl", &int_ctype, 0, { &ulong_ctype }, .op = &parity_op }, { "__builtin_parityll", &int_ctype, 0, { &ullong_ctype }, .op = &parity_op }, { "__builtin_popcount", &int_ctype, 0, { &uint_ctype }, .op = &popcount_op }, { "__builtin_popcountl", &int_ctype, 0, { &ulong_ctype }, .op = &popcount_op }, { "__builtin_popcountll", &int_ctype, 0, { &ullong_ctype }, .op = &popcount_op }, { "__builtin_prefetch", &void_ctype, 1, { &const_ptr_ctype }}, { "__builtin_printf", &int_ctype, 1, { &const_string_ctype }}, { "__builtin_puts", &int_ctype, 0, { &const_string_ctype }}, { "__builtin_realloc", &ptr_ctype, 0, { &ptr_ctype, size_t_ctype }}, { "__builtin_return_address", &ptr_ctype, 0, { &uint_ctype }}, { "__builtin_rindex", &string_ctype, 0, { &const_string_ctype, &int_ctype }}, { "__builtin_sadd_overflow", &bool_ctype, 0, { &int_ctype, &int_ctype, &int_ptr_ctype }}, { "__builtin_saddl_overflow", &bool_ctype, 0, { &long_ctype, &long_ctype, &long_ptr_ctype }}, { "__builtin_saddll_overflow", &bool_ctype, 0, { &llong_ctype, &llong_ctype, &llong_ptr_ctype }}, { "__builtin_signbit", &int_ctype, 1 , .op = &fp_unop_op }, { "__builtin_smul_overflow", &bool_ctype, 0, { &int_ctype, &int_ctype, &int_ptr_ctype }}, { "__builtin_smull_overflow", &bool_ctype, 0, { &long_ctype, &long_ctype, &long_ptr_ctype }}, { "__builtin_smulll_overflow", &bool_ctype, 0, { &llong_ctype, &llong_ctype, &llong_ptr_ctype }}, { "__builtin_snprintf", &int_ctype, 1, { &string_ctype, size_t_ctype, &const_string_ctype }}, { "__builtin_sprintf", &int_ctype, 1, { &string_ctype, &const_string_ctype }}, { "__builtin_ssub_overflow", &bool_ctype, 0, { &int_ctype, &int_ctype, &int_ptr_ctype }}, { "__builtin_ssubl_overflow", &bool_ctype, 0, { &long_ctype, &long_ctype, &long_ptr_ctype }}, { "__builtin_ssubll_overflow", &bool_ctype, 0, { &llong_ctype, &llong_ctype, &llong_ptr_ctype }}, { "__builtin_stpcpy", &string_ctype, 0, { &const_string_ctype, &const_string_ctype }}, { "__builtin_stpncpy", &string_ctype, 0, { &const_string_ctype, &const_string_ctype, size_t_ctype }}, { "__builtin_strcasecmp", &int_ctype, 0, { &const_string_ctype, &const_string_ctype }}, { "__builtin_strcasestr", &string_ctype, 0, { &const_string_ctype, &const_string_ctype }}, { "__builtin_strcat", &string_ctype, 0, { &string_ctype, &const_string_ctype }}, { "__builtin_strchr", &string_ctype, 0, { &const_string_ctype, &int_ctype }}, { "__builtin_strcmp", &int_ctype, 0, { &const_string_ctype, &const_string_ctype }}, { "__builtin_strcpy", &string_ctype, 0, { &string_ctype, &const_string_ctype }}, { "__builtin_strcspn", size_t_ctype, 0, { &const_string_ctype, &const_string_ctype }}, { "__builtin_strdup", &string_ctype, 0, { &const_string_ctype }}, { "__builtin_strlen", size_t_ctype, 0, { &const_string_ctype }}, { "__builtin_strncasecmp", &int_ctype, 0, { &const_string_ctype, &const_string_ctype, size_t_ctype }}, { "__builtin_strncat", &string_ctype, 0, { &string_ctype, &const_string_ctype, size_t_ctype }}, { "__builtin_strncmp", &int_ctype, 0, { &const_string_ctype, &const_string_ctype, size_t_ctype }}, { "__builtin_strncpy", &string_ctype, 0, { &string_ctype, &const_string_ctype, size_t_ctype }}, { "__builtin_strndup", &string_ctype, 0, { &const_string_ctype, size_t_ctype }}, { "__builtin_strnstr", &string_ctype, 0, { &const_string_ctype, &const_string_ctype, size_t_ctype }}, { "__builtin_strpbrk", &string_ctype, 0, { &const_string_ctype, &const_string_ctype }}, { "__builtin_strrchr", &string_ctype, 0, { &const_string_ctype, &int_ctype }}, { "__builtin_strspn", size_t_ctype, 0, { &const_string_ctype, &const_string_ctype }}, { "__builtin_strstr", &string_ctype, 0, { &const_string_ctype, &const_string_ctype }}, { "__builtin_sub_overflow", &bool_ctype, 1, .op = &overflow_op }, { "__builtin_sub_overflow_p", &bool_ctype, 1, .op = &overflow_p_op }, { "__builtin_trap", &void_ctype, 0 }, { "__builtin_uadd_overflow", &bool_ctype, 0, { &uint_ctype, &uint_ctype, &uint_ptr_ctype }}, { "__builtin_uaddl_overflow", &bool_ctype, 0, { &ulong_ctype, &ulong_ctype, &ulong_ptr_ctype }}, { "__builtin_uaddll_overflow", &bool_ctype, 0, { &ullong_ctype, &ullong_ctype, &ullong_ptr_ctype }}, { "__builtin_umul_overflow", &bool_ctype, 0, { &uint_ctype, &uint_ctype, &uint_ptr_ctype }}, { "__builtin_umull_overflow", &bool_ctype, 0, { &ulong_ctype, &ulong_ctype, &ulong_ptr_ctype }}, { "__builtin_umulll_overflow", &bool_ctype, 0, { &ullong_ctype, &ullong_ctype, &ullong_ptr_ctype }}, { "__builtin_unreachable", &void_ctype, 0 }, { "__builtin_usub_overflow", &bool_ctype, 0, { &uint_ctype, &uint_ctype, &uint_ptr_ctype }}, { "__builtin_usubl_overflow", &bool_ctype, 0, { &ulong_ctype, &ulong_ctype, &ulong_ptr_ctype }}, { "__builtin_usubll_overflow", &bool_ctype, 0, { &ullong_ctype, &ullong_ctype, &ullong_ptr_ctype }}, { "__builtin_va_arg_pack_len", size_t_ctype, 0 }, { "__builtin_vprintf", &int_ctype, 0, { &const_string_ctype, va_list_ctype }}, { "__builtin_vsnprintf", &int_ctype, 0, { &string_ctype, size_t_ctype, &const_string_ctype, va_list_ctype }}, { "__builtin_vsprintf", &int_ctype, 0, { &string_ctype, &const_string_ctype, va_list_ctype }}, { "__builtin___memcpy_chk", &ptr_ctype, 0, { &ptr_ctype, &const_ptr_ctype, size_t_ctype, size_t_ctype }}, { "__builtin___memmove_chk", &ptr_ctype, 0, { &ptr_ctype, &const_ptr_ctype, size_t_ctype, size_t_ctype }}, { "__builtin___mempcpy_chk", &ptr_ctype, 0, { &ptr_ctype, &const_ptr_ctype, size_t_ctype, size_t_ctype }}, { "__builtin___memset_chk", &ptr_ctype, 0, { &ptr_ctype, &int_ctype, size_t_ctype, size_t_ctype }}, { "__builtin___snprintf_chk", &int_ctype, 1, { &string_ctype, size_t_ctype, &int_ctype , size_t_ctype, &const_string_ctype }}, { "__builtin___sprintf_chk", &int_ctype, 1, { &string_ctype, &int_ctype, size_t_ctype, &const_string_ctype }}, { "__builtin___stpcpy_chk", &string_ctype, 0, { &string_ctype, &const_string_ctype, size_t_ctype }}, { "__builtin___strcat_chk", &string_ctype, 0, { &string_ctype, &const_string_ctype, size_t_ctype }}, { "__builtin___strcpy_chk", &string_ctype, 0, { &string_ctype, &const_string_ctype, size_t_ctype }}, { "__builtin___strncat_chk", &string_ctype, 0, { &string_ctype, &const_string_ctype, size_t_ctype, size_t_ctype }}, { "__builtin___strncpy_chk", &string_ctype, 0, { &string_ctype, &const_string_ctype, size_t_ctype, size_t_ctype }}, { "__builtin___vsnprintf_chk", &int_ctype, 0, { &string_ctype, size_t_ctype, &int_ctype, size_t_ctype, &const_string_ctype, va_list_ctype }}, { "__builtin___vsprintf_chk", &int_ctype, 0, { &string_ctype, &int_ctype, size_t_ctype, &const_string_ctype, va_list_ctype }}, { "__sync_add_and_fetch", NULL, 1, { vol_ptr, &dyntype }, .op = &atomic_op }, { "__sync_and_and_fetch", NULL, 1, { vol_ptr, &dyntype }, .op = &atomic_op }, { "__sync_bool_compare_and_swap", &bool_ctype, 1, { vol_ptr, &dyntype, &dyntype }, .op = &atomic_op}, { "__sync_fetch_and_add", NULL, 1, { vol_ptr, &dyntype }, .op = &atomic_op }, { "__sync_fetch_and_and", NULL, 1, { vol_ptr, &dyntype }, .op = &atomic_op }, { "__sync_fetch_and_nand", NULL, 1, { vol_ptr, &dyntype }, .op = &atomic_op }, { "__sync_fetch_and_or", NULL, 1, { vol_ptr, &dyntype }, .op = &atomic_op }, { "__sync_fetch_and_sub", NULL, 1, { vol_ptr, &dyntype }, .op = &atomic_op }, { "__sync_fetch_and_xor", NULL, 1, { vol_ptr, &dyntype }, .op = &atomic_op }, { "__sync_lock_release", &void_ctype, 1, { vol_ptr }, .op = &atomic_op }, { "__sync_lock_test_and_set", NULL, 1, { vol_ptr, &dyntype }, .op = &atomic_op }, { "__sync_nand_and_fetch", NULL, 1, { vol_ptr, &dyntype }, .op = &atomic_op }, { "__sync_or_and_fetch", NULL, 1, { vol_ptr, &dyntype }, .op = &atomic_op }, { "__sync_sub_and_fetch", NULL, 1, { vol_ptr, &dyntype }, .op = &atomic_op }, { "__sync_synchronize", &void_ctype, 1 }, { "__sync_val_compare_and_swap", NULL, 1, { vol_ptr, &dyntype, &dyntype }, .op = &atomic_op }, { "__sync_xor_and_fetch", NULL, 1, { vol_ptr, &dyntype }, .op = &atomic_op }, { } }; void init_builtins(int stream) { declare_builtins(stream, builtins_common); declare_builtins(stream, arch_target->builtins); init_linearized_builtins(stream); } sparse-0.6.4/builtin.h000066400000000000000000000005411411531012200146230ustar00rootroot00000000000000#ifndef _BUILTIN_H_ #define _BUILTIN_H_ #include "symbol.h" struct builtin_fn { const char *name; struct symbol *ret_type; unsigned int variadic:1; struct symbol *args[6]; struct symbol *_args_null_tail; struct symbol_op *op; }; void declare_builtins(int stream, const struct builtin_fn tbl[]); extern struct symbol_op generic_int_op; #endif sparse-0.6.4/c2xml.c000066400000000000000000000172421411531012200142030ustar00rootroot00000000000000/* * 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(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(file); xmlSaveFormatFileEnc("-", doc, "UTF-8", 1); xmlFreeDoc(doc); xmlCleanupParser(); return 0; } sparse-0.6.4/cgcc000077500000000000000000000247631411531012200136450ustar00rootroot00000000000000#!/usr/bin/perl -w # ----------------------------------------------------------------------------- use strict; use warnings; 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; my $nargs = 0; while (@ARGV) { $_ = shift(@ARGV); if ($nargs) { $nargs--; goto add_option; } # Look for a .c file. We don't want to run the checker on .o or .so files # in the link run. $do_check = 1 if /^[^-].*\.c$/; # Ditto for stdin. $do_check = 1 if $_ eq '-'; if (/^-(o|MF|MT|MQ)$/) { # Need to be checked explicitly since otherwise # the argument would be processed as a # (non-existant) source file or as an option. die ("$0: missing argument for $_") if !@ARGV; $nargs = 1; } # Ignore the extension if '-x c' is given. if ($_ eq '-x') { die ("$0: missing argument for $_") if !@ARGV; die ("$0: invalid argument for $_") if $ARGV[0] ne 'c'; $do_check = 1; $nargs = 1; } $m32 = 1 if /^-m32$/; $m64 = 1 if /^-m64$/; $gendeps = 1 if /^-(M|MM)$/; 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'; add_option: 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|constant-suffix|context|decl|default-bitfield-sign|designated-init|do-while|enum-mismatch|external-function-has-definition|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-ir|memcpy-max-count|diagnostic-prefix)(=\S*)?$/; return 1 if $arg =~ /^-f(mem2reg|optim)(-enable|-disable|=last)?$/; return 1 if $arg =~ /^-msize-(long|llp64)$/; 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 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 add_specs { my ($spec) = @_; if ($spec eq 'sunos') { return " --os=$spec" . ' -DSVR4=1' . ' -D__STDC__=0' . ' -D_REENTRANT' . ' -D_SOLARIS_THREADS' . ' -DNULL="((void *)0)"'; } elsif ($spec eq 'linux') { return " --os=$spec"; } elsif ($spec eq 'gnu/kfreebsd') { return &add_specs ('unix') . ' -D__FreeBSD_kernel__=1'; } elsif ($spec eq 'openbsd') { return " --os=$spec"; } elsif ($spec eq 'freebsd') { return " --os=$spec"; } elsif ($spec eq 'netbsd') { return " --os=$spec"; } elsif ($spec eq 'darwin') { return " --os=$spec"; } elsif ($spec eq 'gnu') { # Hurd return &add_specs ('unix') . # So, GNU is Unix, uh? ' -D__GNU__=1 -D__gnu_hurd__=1 -D__MACH__=1'; } elsif ($spec eq 'unix') { return ' -Dunix=1 -D__unix=1 -D__unix__=1'; } elsif ( $spec =~ /^cygwin/) { return ' --os=cygwin'; } elsif ($spec eq 'i386') { $m32 = 1; return ( ' --arch=i386' . &float_types (1, 1, 21, [24,8], [53,11], [64,15])); } elsif ($spec eq 'sparc') { return ( ' --arch=sparc' . &float_types (1, 1, 33, [24,8], [53,11], [113,15])); } elsif ($spec eq 'sparc64') { return ( ' --arch=sparc64' . &float_types (1, 1, 33, [24,8], [53,11], [113,15])); } elsif ($spec eq 'x86_64') { return (' --arch=x86_64' . &float_types (1, 1, 33, [24,8], [53,11], [113,15])); } elsif ($spec eq 'ppc') { return (' --arch=ppc' . &float_types (1, 1, 21, [24,8], [53,11], [113,15])); } elsif ($spec eq 'ppc64') { return ( ' --arch=ppc64' . &float_types (1, 1, 21, [24,8], [53,11], [113,15])); } elsif ($spec eq 'ppc64be') { return &add_specs ('ppc64') . ' -mbig-endian -D_CALL_ELF=1'; } elsif ($spec eq 'ppc64le') { return &add_specs ('ppc64') . ' -mlittle-endian -D_CALL_ELF=2'; } elsif ($spec eq 's390x') { return (' -D_BIG_ENDIAN' . ' --arch=s390x' . &float_types (1, 1, 36, [24,8], [53,11], [113,15])); } elsif ($spec eq 'riscv32') { return (' --arch=riscv32' . &float_types (1, 1, 33, [24,8], [53,11], [53,11])); } elsif ($spec eq 'riscv64') { return (' --arch=riscv64' . &float_types (1, 1, 33, [24,8], [53,11], [113,15])); } elsif ($spec eq 'arm') { return (' --arch=arm' . &float_types (1, 1, 36, [24,8], [53,11], [53, 11])); } elsif ($spec eq 'arm+hf') { return &add_specs ('arm') . ' -mfloat-abi=hard'; } elsif ($spec eq 'aarch64') { return (' --arch=aarch64' . &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 $gccmachine; my $arch; $gccmachine = `$ccom -dumpmachine`; chomp $gccmachine; if ($gccmachine =~ '^aarch64-') { return &add_specs ('aarch64'); } elsif ($gccmachine =~ '^arm-.*eabihf$') { return &add_specs ('arm+hf'); } elsif ($gccmachine =~ '^arm-') { return &add_specs ('arm'); } elsif ($gccmachine =~ '^i[23456]86-') { return &add_specs ('i386'); } elsif ($gccmachine =~ '^(powerpc|ppc)64le-') { return &add_specs ('ppc64le'); } elsif ($gccmachine =~ '^s390x-') { return &add_specs ('s390x'); } elsif ($gccmachine eq 'x86_64-linux-gnux32') { return &add_specs ('x86_64') . ' -mx32'; } elsif ($gccmachine =~ '^x86_64-') { return &add_specs ('x86_64'); } # fall back to uname -m to determine the specifics. # Note: this is only meaningful when using natively # since information about the host is used to # guess characteristics of the target. $arch = `uname -m`; chomp $arch; if ($arch =~ /^(i.?86|athlon)$/i) { return &add_specs ('i386'); } 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 ('ppc64be'); } elsif ($arch =~ /^(ppc64le)$/i) { return &add_specs ('ppc64le'); } elsif ($arch =~ /^(s390x)$/i) { return &add_specs ('s390x'); } elsif ($arch =~ /^(sparc64)$/i) { return &add_specs ('sparc64'); } elsif ($arch =~ /^arm(?:v[78]l)?$/i) { return &add_specs ('arm'); } elsif ($arch =~ /^(aarch64)$/i) { return &add_specs ('aarch64'); } } else { die "$0: invalid specs: $spec\n"; } } # ----------------------------------------------------------------------------- sparse-0.6.4/cgcc.1000066400000000000000000000020461411531012200137670ustar00rootroot00000000000000.\" 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.6.4/char.c000066400000000000000000000065441411531012200140760ustar00rootroot00000000000000#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; if (end == p) { sparse_error(token->pos, "empty character constant"); *val = 0; return; } 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 : wchar_ctype->bit_size, 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 ? wchar_ctype->bit_size: 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.6.4/char.h000066400000000000000000000002241411531012200140700ustar00rootroot00000000000000extern void get_char_constant(struct token *, unsigned long long *); extern struct token *get_string_constant(struct token *, struct expression *); sparse-0.6.4/compat-bsd.c000066400000000000000000000021671411531012200152070ustar00rootroot00000000000000/* * 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 "compat-linux.c" sparse-0.6.4/compat-cygwin.c000066400000000000000000000033231411531012200157320ustar00rootroot00000000000000/* * 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.6.4/compat-linux.c000066400000000000000000000001671411531012200155740ustar00rootroot00000000000000#define _GNU_SOURCE #include "lib.h" #include "allocate.h" #include "compat/mmap-blob.c" #include "compat/strtold.c" sparse-0.6.4/compat-mingw.c000066400000000000000000000031021411531012200155460ustar00rootroot00000000000000/* * 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.6.4/compat-solaris.c000066400000000000000000000000321411531012200161000ustar00rootroot00000000000000#include "compat-linux.c" sparse-0.6.4/compat.h000066400000000000000000000012551411531012200144430ustar00rootroot00000000000000#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 */ /* * 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.6.4/compat/000077500000000000000000000000001411531012200142675ustar00rootroot00000000000000sparse-0.6.4/compat/bswap.h000066400000000000000000000023021411531012200155510ustar00rootroot00000000000000#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.6.4/compat/mmap-blob.c000066400000000000000000000015251411531012200163040ustar00rootroot00000000000000#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.6.4/compat/strtold.c000066400000000000000000000001621411531012200161250ustar00rootroot00000000000000#include long double string_to_ld(const char *nptr, char **endptr) { return strtold(nptr, endptr); } sparse-0.6.4/compile-i386.c000066400000000000000000001554201411531012200152760ustar00rootroot00000000000000/* * 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 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, "$%lld", 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 (version %s)\"\n", sparse_version); } /* 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%lld\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 = expression_list_size(expr->expr_list); 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); free(list); } 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 = is_signed_type(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 = is_signed_type(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, *stot = NULL, *stof = 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 */ stot = x86_expression(expr->cond_true); emit_copy(new, stot, expr->ctype); cond_end = emit_conditional_end(target_false); /* handle if-false part of the expression */ stof = x86_expression(expr->cond_false); emit_copy(new, stof, 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 *stot = x86_expression(expr->cond_true); struct storage *stof = 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(stot, get_regclass(expr)); reg_false = get_reg_value(stof, 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, 0LL); 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 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.6.4/compile.c000066400000000000000000000046171411531012200146100ustar00rootroot00000000000000/* * 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(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(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.6.4/compile.h000066400000000000000000000003071411531012200146050ustar00rootroot00000000000000#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.6.4/cse.c000066400000000000000000000201741411531012200137260ustar00rootroot00000000000000/* * 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 "flowgraph.h" #include "linearize.h" #include "flow.h" #include "cse.h" #define INSN_HASH_SIZE 256 static struct instruction_list *insn_hash_table[INSN_HASH_SIZE]; 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; } void cse_collect(struct instruction *insn) { unsigned long hash; 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_MUL: 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: /* 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: /* floating-point arithmetic & comparison */ case OP_FPCMP ... OP_FPCMP_END: case OP_FADD: case OP_FSUB: case OP_FMUL: case OP_FDIV: hash += hashval(insn->src2); /* Fall through */ /* Unary */ case OP_NOT: case OP_NEG: case OP_FNEG: case OP_SYMADDR: hash += hashval(insn->src1); break; case OP_LABEL: hash += hashval(insn->bb_true); break; case OP_SETVAL: hash += hashval(insn->val); break; case OP_SETFVAL: hash += hashval(insn->fvalue); break; case OP_SEXT: case OP_ZEXT: case OP_TRUNC: case OP_PTRCAST: case OP_UTPTR: case OP_PTRTU: if (!insn->orig_type || insn->orig_type->bit_size < 0) return; hash += hashval(insn->src); // Note: see corresponding line in insn_compare() hash += hashval(insn->orig_type->bit_size); 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); } /* 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; int size1, size2; int diff; if (i1->opcode != i2->opcode) return i1->opcode < i2->opcode ? -1 : 1; switch (i1->opcode) { /* commutative binop */ case OP_ADD: case OP_MUL: 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: /* floating-point arithmetic */ case OP_FPCMP ... OP_FPCMP_END: case OP_FADD: case OP_FSUB: case OP_FMUL: case OP_FDIV: 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: case OP_FNEG: case OP_SYMADDR: if (i1->src1 != i2->src1) return i1->src1 < i2->src1 ? -1 : 1; break; case OP_LABEL: if (i1->bb_true != i2->bb_true) return i1->bb_true < i2->bb_true ? -1 : 1; break; case OP_SETVAL: if (i1->val != i2->val) return i1->val < i2->val ? -1 : 1; break; case OP_SETFVAL: diff = memcmp(&i1->fvalue, &i2->fvalue, sizeof(i1->fvalue)); if (diff) return diff; break; /* Other */ case OP_PHI: return phi_list_compare(i1->phi_list, i2->phi_list); case OP_SEXT: case OP_ZEXT: case OP_TRUNC: case OP_PTRCAST: case OP_UTPTR: case OP_PTRTU: if (i1->src != i2->src) return i1->src < i2->src ? -1 : 1; // Note: if it can be guaranted that identical ->src // implies identical orig_type->bit_size, then this // test and the hashing of the original size in // cse_collect() are not needed. // It must be generaly true but it isn't guaranted (yet). size1 = i1->orig_type->bit_size; size2 = i2->orig_type->bit_size; if (size1 != size2) return size1 < size2 ? -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; } 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 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 (domtree_dominates(b1, b2)) return cse_one_instruction(i2, i1); if (domtree_dominates(b2, b1)) 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); insert_last_instruction(common, i1); } else { i1 = i2; } return i1; } void cse_eliminate(struct entrypoint *ep) { int i; 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(list); } } } sparse-0.6.4/cse.h000066400000000000000000000002601411531012200137250ustar00rootroot00000000000000#ifndef CSE_H #define CSE_H struct instruction; struct entrypoint; /* cse.c */ void cse_collect(struct instruction *insn); void cse_eliminate(struct entrypoint *ep); #endif sparse-0.6.4/ctags.c000066400000000000000000000131411411531012200142510ustar00rootroot00000000000000/* * 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 && 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(filelist, file) { sparse(file); examine_symbol_list(file_scope->symbols); } END_FOR_EACH_PTR(file); examine_symbol_list(global_scope->symbols); sort_list((struct ptr_list **)&taglist, cmp_sym); show_tags(taglist); return 0; } sparse-0.6.4/dissect.c000066400000000000000000000363701411531012200146170ustar00rootroot00000000000000/* * 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; struct symbol *dissect_ctx; static struct reporter *reporter; 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 struct symbol *report_member(usage_t mode, struct position *pos, struct symbol *type, struct symbol *mem) { struct symbol *ret = mem->ctype.base_type; if (mem->ident || mem->type == SYM_BAD) 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 (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->kind = expr->op ?: 'v'; /* see EXPR_CALL */ } } 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); reporter->r_symbol(fix_mode(ret, mode), &expr->pos, sym); return ret; } static bool deanon(struct symbol *base, struct ident *node, struct symbol *parent) { struct ident *pi = parent ? parent->ident : NULL; char name[256]; if (!node) { base->ident = pi; return false; } snprintf(name, sizeof(name), "%.*s:%.*s", pi ? pi->len : 0, pi ? pi->name : NULL, node->len, node->name); base->ident = built_in_ident(name); return true; } static void report_memdef(struct symbol *sym, struct symbol *mem) { mem->kind = 'm'; if (sym && mem->ident) reporter->r_memdef(sym, mem); } static void examine_sym_node(struct symbol *node, struct symbol *parent) { struct ident *name = node->ident; struct symbol *base, *dctx; if (node->visited) return; node->visited = 1; node->kind = 'v'; 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: node = base; break; case SYM_FN: node->kind = 'f'; node = base; break; case SYM_STRUCT: case SYM_UNION: //case SYM_ENUM: if (base->inspected) return; base->inspected = 1; base->kind = 's'; if (!base->symbol_list) return; dctx = dissect_ctx; if (toplevel(base->scope)) dissect_ctx = NULL; if (base->ident || deanon(base, name, parent)) reporter->r_symdef(base); if (base->ident) parent = base; DO_LIST(base->symbol_list, mem, examine_sym_node(mem, parent); report_memdef(parent, mem)); dissect_ctx = dctx; 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) { struct symbol *mem = __lookup_member(type, name, addr); if (!mem) { static struct symbol bad_member = { .type = SYM_BAD, .ctype.base_type = &bad_ctype, .kind = 'm', }; if (!type->symbol_list) type->scope = file_scope; mem = &bad_member; mem->ident = name; } return mem; } 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: if (expr->fn->type == EXPR_SYMBOL) expr->fn->op = 'f'; /* for expr_symbol() */ 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_GENERIC: { struct type_expression *map; do_expression(U_VOID, expr->control); for (map = expr->map; map; map = map->next) ret = do_expression(mode, map->expr); if (expr->def) ret = do_expression(mode, expr->def); } break; case EXPR_SYMBOL: ret = report_symbol(mode, expr); } return ret; } static void do_asm_xputs(usage_t mode, struct asm_operand_list *xputs) { DO_LIST(xputs, op, do_expression(U_W_AOF | mode, op->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: { struct symbol *type = dissect_ctx->ctype.base_type; do_expression(u_lval(base_type(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 = base_type(sym); struct symbol *dctx = dissect_ctx; struct statement *stmt; reporter->r_symdef(sym); switch (type->type) { default: if (!sym->initializer) break; reporter->r_symbol(U_W_VAL, &sym->pos, sym); if (!dctx) dissect_ctx = sym; do_initializer(type, sym->initializer); dissect_ctx = dctx; break; case SYM_FN: stmt = sym->ctype.modifiers & MOD_INLINE ? type->inline_stmt : type->stmt; if (!stmt) break; if (dctx) sparse_error(dctx->pos, "dissect_ctx change %s -> %s", show_ident(dctx->ident), show_ident(sym->ident)); dissect_ctx = sym; do_sym_list(type->arguments); do_statement(U_VOID, stmt); dissect_ctx = dctx; } return type; } static void do_sym_list(struct symbol_list *list) { DO_LIST(list, sym, do_symbol(sym)); } void dissect(struct reporter *rep, struct string_list *filelist) { reporter = rep; DO_LIST(filelist, file, do_sym_list(__sparse(file))); } sparse-0.6.4/dissect.h000066400000000000000000000013671411531012200146220ustar00rootroot00000000000000#ifndef DISSECT_H #define DISSECT_H #include #include "parse.h" #include "expression.h" #include "scope.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_memdef)(struct symbol *, struct symbol *); void (*r_symbol)(unsigned, struct position *, struct symbol *); void (*r_member)(unsigned, struct position *, struct symbol *, struct symbol *); }; extern struct symbol *dissect_ctx; static inline bool sym_is_local(struct symbol *sym) { return !toplevel(sym->scope); } extern void dissect(struct reporter *, struct string_list *); #endif sparse-0.6.4/dominate.c000066400000000000000000000065561411531012200147640ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // dominate.c - compute the (iterated) dominance frontier of (a set of) nodes. // // Copyright (C) 2017 - Luc Van Oostenryck // // The algorithm used is the one described in: // "A Linear Time Algorithm for Placing phi-nodes" // by Vugranam C. Sreedhar and Guang R. Gao // #include "dominate.h" #include "flowgraph.h" #include "linearize.h" #include "flow.h" #include #include #include struct piggy { unsigned int max; struct basic_block_list *lists[0]; }; static struct piggy *bank_init(unsigned levels) { struct piggy *bank; bank = calloc(1, sizeof(*bank) + levels * sizeof(bank->lists[0])); bank->max = levels - 1; return bank; } static void bank_free(struct piggy *bank, unsigned int levels) { for (; levels-- ;) free_ptr_list(&bank->lists[levels]); free(bank); } static void bank_put(struct piggy *bank, struct basic_block *bb) { unsigned int level = bb->dom_level; assert(level <= bank->max); add_bb(&bank->lists[level], bb); } static inline struct basic_block *pop_bb(struct basic_block_list **list) { return delete_ptr_list_last((struct ptr_list **)list); } static struct basic_block *bank_get(struct piggy *bank) { int level = bank->max; do { struct basic_block *bb = pop_bb(&bank->lists[level]); if (bb) return bb; if (!level) return NULL; bank->max = --level; } while (1); } #define VISITED 0x1 #define INPHI 0x2 #define ALPHA 0x4 #define FLAGS 0x7 static void visit(struct piggy *bank, struct basic_block_list **idf, struct basic_block *x, int curr_level) { struct basic_block *y; x->generation |= 1; FOR_EACH_PTR(x->children, y) { unsigned flags = y->generation & FLAGS; if (y->idom == x) // J-edges will be processed later continue; if (y->dom_level > curr_level) continue; if (flags & INPHI) continue; y->generation |= INPHI; add_bb(idf, y); if (flags & ALPHA) continue; bank_put(bank, y); } END_FOR_EACH_PTR(y); FOR_EACH_PTR(x->doms, y) { if (y->generation & VISITED) continue; visit(bank, idf, y, curr_level); } END_FOR_EACH_PTR(y); } void idf_compute(struct entrypoint *ep, struct basic_block_list **idf, struct basic_block_list *alpha) { int levels = ep->dom_levels; struct piggy *bank = bank_init(levels); struct basic_block *bb; unsigned long generation = bb_generation; generation = bb_generation; generation += -generation & FLAGS; bb_generation = generation + (FLAGS + 1); // init all the nodes FOR_EACH_PTR(ep->bbs, bb) { // FIXME: this should be removed and the tests for // visited/in_phi/alpha should use a sparse set bb->generation = generation; } END_FOR_EACH_PTR(bb); FOR_EACH_PTR(alpha, bb) { bb->generation = generation | ALPHA; bank_put(bank, bb); } END_FOR_EACH_PTR(bb); while ((bb = bank_get(bank))) { visit(bank, idf, bb, bb->dom_level); } bank_free(bank, levels); } void idf_dump(struct entrypoint *ep) { struct basic_block *bb; domtree_build(ep); printf("%s's IDF:\n", show_ident(ep->name->ident)); FOR_EACH_PTR(ep->bbs, bb) { struct basic_block_list *alpha = NULL; struct basic_block_list *idf = NULL; struct basic_block *df; add_bb(&alpha, bb); idf_compute(ep, &idf, alpha); printf("\t%s\t<-", show_label(bb)); FOR_EACH_PTR(idf, df) { printf(" %s", show_label(df)); } END_FOR_EACH_PTR(df); printf("\n"); free_ptr_list(&idf); free_ptr_list(&alpha); } END_FOR_EACH_PTR(bb); } sparse-0.6.4/dominate.h000066400000000000000000000004021411531012200147510ustar00rootroot00000000000000#ifndef DOMINATE_H #define DOMINATE_H struct entrypoint; struct basic_block_list; void idf_compute(struct entrypoint *ep, struct basic_block_list **idf, struct basic_block_list *alpha); // For debugging only void idf_dump(struct entrypoint *ep); #endif sparse-0.6.4/evaluate.c000066400000000000000000003040601411531012200147610ustar00rootroot00000000000000/* * 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 "evaluate.h" #include "lib.h" #include "allocate.h" #include "parse.h" #include "token.h" #include "symbol.h" #include "target.h" #include "expression.h" struct symbol *current_fn; struct ident bad_address_space = { .len = 6, .name = "bad AS", }; static struct symbol *degenerate(struct expression *expr); static struct symbol *evaluate_symbol(struct symbol *sym); static inline int valid_expr_type(struct expression *expr) { return expr && valid_type(expr->ctype); } static inline int valid_subexpr_type(struct expression *expr) { return valid_expr_type(expr->left) && valid_expr_type(expr->right); } static struct symbol *unqualify_type(struct symbol *ctype) { if (!ctype) return ctype; if (ctype->type == SYM_NODE && (ctype->ctype.modifiers & MOD_QUALIFIER)) { struct symbol *unqual = alloc_symbol(ctype->pos, 0); *unqual = *ctype; unqual->ctype.modifiers &= ~MOD_QUALIFIER; return unqual; } return ctype; } 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 */ addr->flags = expr->flags; expr->type = EXPR_PREOP; expr->op = '*'; expr->unop = addr; expr->flags = CEF_NONE; /* 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; struct symbol *char_type = expr->wide ? wchar_ctype : &char_ctype; sym->array_size = alloc_const_expression(expr->pos, length); sym->bit_size = length * char_type->bit_size; sym->ctype.alignment = 1; sym->string = 1; sym->ctype.modifiers = MOD_STATIC; sym->ctype.base_type = array; sym->initializer = initstr; sym->examined = 1; sym->evaluated = 1; initstr->ctype = sym; initstr->string = expr->string; array->array_size = sym->array_size; array->bit_size = sym->bit_size; array->ctype.alignment = char_type->ctype.alignment; array->ctype.modifiers = MOD_STATIC; array->ctype.base_type = char_type; array->examined = 1; array->evaluated = 1; addr->symbol = sym; addr->ctype = &lazy_ptr_ctype; addr->flags = CEF_ADDR; 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 (type->rank < 0) { if (mod & MOD_UNSIGNED) return &uint_ctype; return &int_ctype; } return type; } /* * After integer promotons: * If both types are the same * -> no conversion needed * If the types have the same signedness (their rank must be different) * -> convert to the type of the highest rank * If rank(unsigned type) >= rank(signed type) * -> convert to the unsigned type * If size(signed type) > size(unsigned type) * -> convert to the signed type * Otherwise * -> convert to the unsigned type corresponding to the signed type. */ static struct symbol *bigger_int_type(struct symbol *left, struct symbol *right) { static struct symbol *unsigned_types[] = { [0] = &uint_ctype, [1] = &ulong_ctype, [2] = &ullong_ctype, [3] = &uint128_ctype, }; unsigned long lmod, rmod; struct symbol *stype, *utype; left = integer_promotion(left); right = integer_promotion(right); if (left == right) return left; lmod = left->ctype.modifiers; rmod = right->ctype.modifiers; if (((lmod ^ rmod) & MOD_UNSIGNED) == 0) return (left->rank > right->rank) ? left : right; if (lmod & MOD_UNSIGNED) { utype = left; stype = right; } else { stype = left; utype = right; } if (utype->rank >= stype->rank) return utype; if (stype->bit_size > utype->bit_size) return stype; utype = unsigned_types[stype->rank]; return utype; } static struct symbol *base_type(struct symbol *node, unsigned long *modp, struct ident **asp) { unsigned long mod = 0; struct ident *as = NULL; while (node) { mod |= node->ctype.modifiers; combine_address_space(node->pos, &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; struct ident *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", show_typename(typea)); info(pos, " %s", show_typename(typeb)); } } static int cast_flags(struct expression *expr, struct expression *target); static struct symbol *cast_to_bool(struct expression *expr); /* * This gets called for implicit casts in assignments and * integer promotion. */ 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; expr = alloc_expression(old->pos, EXPR_IMPLIED_CAST); expr->ctype = type; expr->cast_type = type; expr->cast_expression = old; expr->flags = cast_flags(expr, 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 = examine_symbol_type(type); 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; if (type->type != SYM_ARRAY) return 0; type = type->ctype.base_type; return is_byte_type(type) || is_wchar_type(type); } static struct symbol *bad_expr_type(struct expression *expr) { switch (expr->type) { case EXPR_BINOP: case EXPR_COMPARE: if (!valid_subexpr_type(expr)) break; sparse_error(expr->pos, "incompatible types for operation (%s):", show_special(expr->op)); info(expr->pos, " %s", show_typename(expr->left->ctype)); info(expr->pos, " %s", show_typename(expr->right->ctype)); break; case EXPR_PREOP: case EXPR_POSTOP: if (!valid_expr_type(expr->unop)) break; sparse_error(expr->pos, "incompatible type for operation (%s):", show_special(expr->op)); info(expr->pos, " %s", show_typename(expr->unop->ctype)); break; default: break; } expr->flags = CEF_NONE; 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) { if (rtype->rank > ltype->rank) 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); /* * An address constant +/- an integer constant expression * yields an address constant again [6.6(7)]. */ if ((expr->left->flags & CEF_ADDR) && (expr->right->flags & CEF_ICE)) expr->flags = CEF_ADDR; 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_QUALIFIER | MOD_FUN_ATTR) const char *type_difference(struct ctype *c1, struct ctype *c2, unsigned long mod1, unsigned long mod2) { struct ident *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; combine_address_space(t1->pos, &as1, t1->ctype.as); } move1 = 0; } if (move2) { if (t2 && t2->type != SYM_PTR) { mod2 |= t2->ctype.modifiers; combine_address_space(t2->pos, &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"; if (t1->rank != t2->rank) return "different type sizes"; diff = (mod1 ^ mod2) & ~MOD_IGNORE; if (!diff) return NULL; 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"); info(expr->pos, " '%s' has a non-power-of-2 size: %lu", show_typename(lbase), value); } } 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 (!valid_type(ctype)) return NULL; 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, "non-scalar type in conditional:"); info(expr->pos, " %s", show_typename(ctype)); return 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; expr->flags = expr->left->flags & expr->right->flags; expr->flags &= ~(CEF_CONST_MASK | CEF_ADDR); 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; /* number op number */ if (lclass & rclass & TYPE_NUM) { expr->flags = expr->left->flags & expr->right->flags; expr->flags &= ~CEF_CONST_MASK; 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 = unqualify_type(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; } enum null_constant_type { NON_NULL, NULL_PTR, NULL_ZERO, }; static inline int is_null_pointer_constant(struct expression *e) { if (e->ctype == &null_ctype) return NULL_PTR; if (!(e->flags & CEF_ICE)) return NON_NULL; return is_zero_constant(e) ? NULL_ZERO : NON_NULL; } 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; /* Type types? */ if (is_type_type(ltype) && is_type_type(rtype)) { /* * __builtin_types_compatible_p() yields an integer * constant expression */ expr->flags = CEF_SET_ICE; goto OK; } if (is_safe_type(left->ctype) || is_safe_type(right->ctype)) warning(expr->pos, "testing a 'safe expression'"); expr->flags = left->flags & right->flags & ~CEF_CONST_MASK & ~CEF_ADDR; /* 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 == NULL_ZERO) bad_null(left); if (is_null2 == NULL_ZERO) 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)) { expr->left = cast_to(left, rtype); goto OK; } if (is_null2 && (lclass & TYPE_PTR)) { expr->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) { expr->right = cast_to(right, ltype); goto OK; } if (rbase == &void_ctype) { expr->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); info(expr->pos, " %s", show_typename(ltype)); info(expr->pos, " %s", show_typename(rtype)); 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 **cond; 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); cond = &expr->conditional; ltype = ctype; if (expr->cond_true) { if (!evaluate_expression(expr->cond_true)) return NULL; ltype = degenerate(expr->cond_true); cond = &expr->cond_true; } expr->flags = (expr->conditional->flags & (*cond)->flags & expr->cond_false->flags & ~CEF_CONST_MASK); /* * In the standard, it is defined that an integer constant expression * shall only have operands that are themselves constant [6.6(6)]. * While this definition is very clear for expressions that need all * their operands to be evaluated, for conditional expressions with a * constant condition things are much less obvious. * So, as an extension, do the same as GCC seems to do: * Consider a conditional expression with a constant condition * as having the same constantness as the argument corresponding * to the truth value (including in the case of address constants * which are defined more stricly [6.6(9)]). */ if (expr->conditional->flags & (CEF_ACE | CEF_ADDR)) { int is_true = expr_truth_value(expr->conditional); struct expression *arg = is_true ? *cond : expr->cond_false; expr->flags = arg->flags & ~CEF_CONST_MASK; } lclass = classify_type(ltype, <ype); rclass = classify_type(rtype, &rtype); if (lclass & rclass & TYPE_NUM) { ctype = usual_conversions('?', *cond, expr->cond_false, lclass, rclass, ltype, rtype); *cond = cast_to(*cond, ctype); expr->cond_false = cast_to(expr->cond_false, ctype); goto out; } if ((lclass | rclass) & TYPE_PTR) { int is_null1 = is_null_pointer_constant(*cond); int is_null2 = is_null_pointer_constant(expr->cond_false); if (is_null1 && is_null2) { *cond = cast_to(*cond, &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 == NULL_ZERO) bad_null(*cond); *cond = cast_to(*cond, rtype); ctype = rtype; goto out; } if (is_null2 && (lclass & TYPE_PTR)) { if (is_null2 == NULL_ZERO) 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); info(expr->pos, " %s", show_typename(ltype)); info(expr->pos, " %s", show_typename(rtype)); /* * 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; } *cond = cast_to(*cond, 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 (op == SPECIAL_SHR_ASSIGN || op == SPECIAL_SHL_ASSIGN) { // shifts do integer promotions, but that's it. unrestrict(expr->left, tclass, &t); target = integer_promotion(t); unrestrict(expr->right, sclass, &s); source = integer_promotion(s); expr->right = cast_to(expr->right, source); // both gcc & clang seems to do this, so ... if (target->bit_size > source->bit_size) expr->right = cast_to(expr->right, &uint_ctype); goto Cast; } 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->rank == -2 && t2->rank == -2) return 1; if (t1->rank != t2->rank) 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)) goto Cast; 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; unsigned long modl, modr; struct symbol *b1, *b2; // NULL pointer is always OK int is_null = is_null_pointer_constant(*rp); if (is_null) { if (is_null == NULL_ZERO) 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 = t->ctype.modifiers & MOD_IGN; mod2 = s->ctype.modifiers & MOD_IGN; 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 & ~MOD_FUN_ATTR) { *typediff = "different modifiers"; return 0; } goto Cast; } /* It's OK if the target is more volatile or const than the source */ /* It's OK if the source is more pure/noreturn than the target */ modr = mod1 & ~MOD_REV_QUAL; modl = mod2 & MOD_REV_QUAL; *typediff = type_difference(&t->ctype, &s->ctype, modl, modr); 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; if (!check_assignment_types(target, rp, &typediff)) { struct symbol *source = *rp ? (*rp)->ctype : NULL; 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_addressable(struct expression *expr) { while (expr->type == EXPR_BINOP && expr->op == '+') expr = expr->left; if (expr->type == EXPR_SYMBOL) { struct symbol *sym = expr->symbol; sym->ctype.modifiers |= MOD_ADDRESSABLE; } } 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 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(expr, 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; combine_address_space(s->pos, &ptr->ctype.as, s->ctype.as); ptr->ctype.modifiers |= s->ctype.modifiers & MOD_PTRINHERIT; s->ctype.base_type = ptr; s->ctype.as = NULL; 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, struct ident *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) { combine_address_space(sym->pos, &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) { combine_address_space(sym->pos, &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; } if (ctype->builtin) sparse_error(expr->pos, "taking the address of built-in function '%s'", show_ident(ctype->ident)); *expr = *expr->unop; ctype = create_pointer(expr, ctype, 1); expr->ctype = ctype; mark_addressable(expr); 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; if (ctype->builtin) sparse_error(expr->pos, "taking the address of built-in function '%s'", show_ident(ctype->ident)); *expr = *op->unop; mark_addressable(expr); /* * 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 = CEF_NONE; 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; target = ctype->ctype.base_type; switch (ctype->type) { default: expression_error(expr, "cannot dereference this type"); return NULL; case SYM_FN: *expr = *op; return expr->ctype; case SYM_PTR: examine_symbol_type(target); node = alloc_symbol(expr->pos, SYM_NODE); 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.. */ node = alloc_symbol(expr->pos, SYM_NODE); 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; } 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); unsigned char flags = expr->unop->flags & ~CEF_CONST_MASK; /* 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->flags = flags; 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 '!': ctype = degenerate(expr->unop); expr->flags = expr->unop->flags & ~CEF_CONST_MASK; /* * A logical negation never yields an address constant * [6.6(9)]. */ expr->flags &= ~CEF_ADDR; 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; /* * The resulting address of a member access through an address * constant is an address constant again [6.6(9)]. */ add->flags = expr->flags; 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; struct ident *address_space; unsigned int mod; 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; combine_address_space(deref->pos, &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_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_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)) { if (Wpointer_arith) warning(expr->pos, "expression using sizeof(void)"); size = bits_in_char; } if (is_bool_type(type)) { if (Wsizeof_bool) warning(expr->pos, "expression using sizeof _Bool"); size = bits_to_bytes(bits_in_bool) * bits_in_char; } if (is_function(type->ctype.base_type)) { if (Wpointer_arith) warning(expr->pos, "expression using sizeof on a function"); size = bits_in_char; } if (has_flexible_array(type) && Wflexible_array_sizeof) warning(expr->pos, "using sizeof on a flexible structure"); if (is_array_type(type) && size < 0) { // VLA, 1-dimension only struct expression *base, *size; struct symbol *base_type; if (type->type == SYM_NODE) type = type->ctype.base_type; // strip the SYM_NODE base_type = get_base_type(type); if (!base_type) goto error; if (base_type->bit_size <= 0) { base = alloc_expression(expr->pos, EXPR_SIZEOF); base->cast_type = base_type; if (!evaluate_sizeof(base)) goto error; } else { base = alloc_expression(expr->pos, EXPR_VALUE); base->value = bits_to_bytes(base_type->bit_size); base->ctype = size_t_ctype; } size = alloc_expression(expr->pos, EXPR_CAST); size->cast_type = size_t_ctype; size->cast_expression = type->array_size; if (!evaluate_expression(size)) goto error; expr->left = size; expr->right = base; expr->type = EXPR_BINOP; expr->op = '*'; return expr->ctype = size_t_ctype; } error: 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; } int evaluate_arguments(struct symbol_list *argtypes, struct expression_list *head) { struct expression *expr; struct symbol *argtype; int i = 1; PREPARE_PTR_LIST(argtypes, 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) { if (type->rank < 0) *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_initializer(struct expression **ep, int nested, int class, struct symbol *ctype, unsigned long mods); /* * deal with traversing subobjects [6.7.8(17,18,20)] */ static void handle_list_initializer(struct expression *expr, int class, struct symbol *ctype, unsigned long mods) { struct expression *e, *last = NULL, *top = NULL, *next; int jumped = 0; // has the last designator multiple levels? if (expr->zero_init) free_ptr_list(&expr->expr_list); 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 && Wpast_deep_designator) { 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; mods |= ctype->ctype.modifiers & MOD_STORAGE; if (handle_initializer(v, 1, lclass, top->ctype, mods)) 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_initializer(struct expression **ep, int nested, int class, struct symbol *ctype, unsigned long mods) { 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"); /* * Initializers for static storage duration objects * shall be constant expressions or a string literal [6.7.8(4)]. */ mods |= ctype->ctype.modifiers; mods &= (MOD_TOPLEVEL | MOD_STATIC); if (mods && !(e->flags & (CEF_ACE | CEF_ADDR))) if (Wconstexpr_not_const) warning(e->pos, "non-constant initializer for static object"); 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_type(ctype)) { 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, mods); 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_type(ctype)) { *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) { struct symbol *char_type = e->wide ? wchar_ctype : &char_ctype; unsigned int size_with_null = ctype->bit_size + char_type->bit_size; if (size_with_null < type->bit_size) warning(e->pos, "too long initializer-string for array of char"); else if (Winit_cstring && size_with_null == 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_initializer(ep, 0, class, ctype, 0)) 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); if (oclass & TYPE_PTR) zero->ctype = otype; 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 int cast_flags(struct expression *expr, struct expression *old) { struct symbol *t; int class; int flags = CEF_NONE; class = classify_type(expr->ctype, &t); if (class & TYPE_NUM) { flags = old->flags & ~CEF_CONST_MASK; /* * Casts to numeric types never result in address * constants [6.6(9)]. */ flags &= ~CEF_ADDR; /* * As an extension, treat address constants cast to * integer type as an arithmetic constant. */ if (old->flags & CEF_ADDR) flags = CEF_ACE; /* * Cast to float type -> not an integer constant * expression [6.6(6)]. */ if (class & TYPE_FLOAT) flags &= ~CEF_CLR_ICE; /* * Casts of float literals to integer type results in * a constant integer expression [6.6(6)]. */ else if (old->flags & CEF_FLOAT) flags = CEF_SET_ICE; } else if (class & TYPE_PTR) { /* * Casts of integer literals to pointer type yield * address constants [6.6(9)]. * * As an extension, treat address constants cast to a * different pointer type as address constants again. * * As another extension, treat integer constant * expressions (in contrast to literals) cast to * pointer type as address constants. */ if (old->flags & (CEF_ICE | CEF_ADDR)) flags = CEF_ADDR; } return flags; } /// // check if a type matches one of the members of a union type // @utype: the union type // @type: to type to check // @return: to identifier of the matching type in the union. static struct symbol *find_member_type(struct symbol *utype, struct symbol *type) { struct symbol *t, *member; if (utype->type != SYM_UNION) return NULL; FOR_EACH_PTR(utype->symbol_list, member) { classify_type(member, &t); if (type == t) return member; } END_FOR_EACH_PTR(member); return NULL; } static struct symbol *evaluate_compound_literal(struct expression *expr, struct expression *source) { struct expression *addr = alloc_expression(expr->pos, EXPR_SYMBOL); struct symbol *sym = expr->cast_type; sym->initializer = source; evaluate_symbol(sym); addr->ctype = &lazy_ptr_ctype; /* Lazy eval */ addr->symbol = sym; if (sym->ctype.modifiers & MOD_TOPLEVEL) addr->flags |= CEF_ADDR; expr->type = EXPR_PREOP; expr->op = '*'; expr->deref = addr; expr->ctype = sym; return sym; } static struct symbol *evaluate_cast(struct expression *expr) { struct expression *source = expr->cast_expression; struct symbol *ctype; struct symbol *ttype, *stype; struct symbol *member; int tclass, sclass; struct ident *tas = NULL, *sas = NULL; if (!source) 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 * (cfr. compound literals: C99 & C11 6.5.2.5). * * 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 (source->type == EXPR_INITIALIZER) return evaluate_compound_literal(expr, source); ctype = examine_symbol_type(expr->cast_type); ctype = unqualify_type(ctype); expr->ctype = ctype; expr->cast_type = ctype; evaluate_expression(source); degenerate(source); tclass = classify_type(ctype, &ttype); expr->flags = cast_flags(expr, source); /* * 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 (ttype == &void_ctype) goto out; stype = source->ctype; if (!stype) { expression_error(expr, "cast from unknown type"); goto out; } sclass = classify_type(stype, &stype); if (expr->type == EXPR_FORCE_CAST) goto out; if (tclass & (TYPE_COMPOUND | TYPE_FN)) { /* * Special case: cast to union type (GCC extension) * The effect is similar to a compound literal except * that the result is a rvalue. */ if ((member = find_member_type(ttype, stype))) { struct expression *item, *init; if (Wunion_cast) warning(expr->pos, "cast to union type"); item = alloc_expression(source->pos, EXPR_IDENTIFIER); item->expr_ident = member->ident; item->ident_expression = source; init = alloc_expression(source->pos, EXPR_INITIALIZER); add_expression(&init->expr_list, item); // FIXME: this should be a rvalue evaluate_compound_literal(expr, init); return ctype; } warning(expr->pos, "cast to non-scalar"); } if (sclass & TYPE_COMPOUND) warning(expr->pos, "cast from non-scalar"); /* allowed cast unfouls */ if (sclass & TYPE_FOULED) stype = unfoul(stype); if (ttype != stype) { if ((tclass & TYPE_RESTRICT) && restricted_value(source, ttype)) warning(expr->pos, "cast to %s", show_typename(ttype)); if (sclass & TYPE_RESTRICT) { if (ttype == &bool_ctype) { if (sclass & TYPE_FOULED) warning(expr->pos, "%s degrades to integer", show_typename(stype)); } else { warning(expr->pos, "cast from %s", show_typename(stype)); } } } if ((ttype == &ulong_ctype || ttype == uintptr_ctype) && !Wcast_from_as) tas = &bad_address_space; else if (tclass == TYPE_PTR) { examine_pointer_target(ttype); tas = ttype->ctype.as; } if ((stype == &ulong_ctype || stype == uintptr_ctype)) sas = &bad_address_space; else if (sclass == TYPE_PTR) { examine_pointer_target(stype); sas = stype->ctype.as; } if (!tas && valid_as(sas)) warning(expr->pos, "cast removes address space '%s' of expression", show_as(sas)); if (valid_as(tas) && valid_as(sas) && tas != sas) warning(expr->pos, "cast between address spaces (%s -> %s)", show_as(sas), show_as(tas)); if (valid_as(tas) && !sas && !is_null_pointer_constant(source) && Wcast_to_as) warning(expr->pos, "cast adds address space '%s' to expression", show_as(tas)); if (!(ttype->ctype.modifiers & MOD_PTRINHERIT) && tclass == TYPE_PTR && !tas && (source->flags & CEF_ICE)) { if (ttype->ctype.base_type == &void_ctype) { if (is_zero_constant(source)) { /* NULL */ expr->type = EXPR_VALUE; expr->ctype = &null_ctype; expr->value = 0; return expr->ctype; } } } if (ttype == &bool_ctype) cast_to_bool(expr); // checks pointers to restricted while (Wbitwise_pointer && tclass == TYPE_PTR && sclass == TYPE_PTR) { tclass = classify_type(ttype->ctype.base_type, &ttype); sclass = classify_type(stype->ctype.base_type, &stype); if (ttype == stype) break; if (!ttype || !stype) break; if (ttype == &void_ctype || stype == &void_ctype) break; if (tclass & TYPE_RESTRICT) { warning(expr->pos, "cast to %s", show_typename(ctype)); break; } if (sclass & TYPE_RESTRICT) { warning(expr->pos, "cast from %s", show_typename(source->ctype)); break; } } 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); 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; if (fn->ctype == &bad_ctype) return NULL; 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->arguments, 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)); return NULL; } if (args > fnargs && !ctype->variadic) expression_error(expr, "too many arguments for function %s", show_ident(sym->ident)); } expr->ctype = ctype->ctype.base_type; if (sym->type == SYM_NODE) { if (evaluate_symbol_call(expr)) return expr->ctype; } 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 = CEF_SET_ICE; 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 = CEF_SET_ICE; 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); unsigned old_idx_flags; 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); old_idx_flags = idx->flags; idx = cast_to(idx, size_t_ctype); idx->flags = old_idx_flags; m = alloc_const_expression(expr->pos, bits_to_bytes(ctype->bit_size)); m->ctype = size_t_ctype; m->flags = CEF_SET_INT; expr->type = EXPR_BINOP; expr->left = idx; expr->right = m; expr->op = '*'; expr->ctype = size_t_ctype; expr->flags = m->flags & idx->flags & ~CEF_CONST_MASK; } } 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 & ~CEF_CONST_MASK; expr->op = '+'; expr->ctype = size_t_ctype; expr->left = copy; expr->right = e; } return size_t_ctype; } static void check_label_declaration(struct position pos, struct symbol *label) { switch (label->namespace) { case NS_LABEL: if (label->stmt) break; sparse_error(pos, "label '%s' was not declared", show_ident(label->ident)); /* fallthrough */ case NS_NONE: current_fn->bogus_linear = 1; default: break; } } static int type_selection(struct symbol *ctrl, struct symbol *type) { struct ctype c = { .base_type = ctrl }; struct ctype t = { .base_type = type }; return !type_difference(&c, &t, 0, 0); } static struct symbol *evaluate_generic_selection(struct expression *expr) { struct type_expression *map; struct expression *res; struct symbol source; struct symbol *ctrl; if (!evaluate_expression(expr->control)) return NULL; if (!(ctrl = degenerate(expr->control))) return NULL; source = *ctrl; source.ctype.modifiers &= ~(MOD_QUALIFIER|MOD_ATOMIC); for (map = expr->map; map; map = map->next) { struct symbol *stype = map->type; struct symbol *base; if (!evaluate_symbol(stype)) continue; base = stype->ctype.base_type; if (base->type == SYM_ARRAY && base->array_size) { get_expression_value_silent(base->array_size); if (base->array_size->type == EXPR_VALUE) continue; sparse_error(stype->pos, "variable length array type in generic selection"); continue; } if (is_func_type(stype)) { sparse_error(stype->pos, "function type in generic selection"); continue; } if (stype->bit_size <= 0 || is_void_type(stype)) { sparse_error(stype->pos, "incomplete type in generic selection"); continue; } if (!type_selection(&source, stype)) continue; res = map->expr; goto found; } res = expr->def; if (!res) { sparse_error(expr->pos, "no generic selection for '%s'", show_typename(ctrl)); return NULL; } found: *expr = *res; return evaluate_expression(expr); } 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: evaluate_expression(expr->left); evaluate_expression(expr->right); if (!valid_subexpr_type(expr)) 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: evaluate_expression(expr->left); evaluate_expression(expr->right); if (!valid_subexpr_type(expr)) return NULL; return evaluate_compare(expr); case EXPR_ASSIGNMENT: evaluate_expression(expr->left); evaluate_expression(expr->right); if (!valid_subexpr_type(expr)) 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; check_label_declaration(expr->pos, expr->label_symbol); 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); case EXPR_GENERIC: return evaluate_generic_selection(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; } 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 (%s):", show_ident(sym->ident), typediff); info(sym->pos, " %s", show_typename(sym)); info(next->pos, "note: previously declared as:"); info(next->pos, " %s", show_typename(next)); return; } } if (!declared) { unsigned long mod = sym->ctype.modifiers; if (mod & (MOD_STATIC | MOD_REGISTER | MOD_EXT_VISIBLE)) 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 = sym; 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, *rettype; evaluate_expression(expr); fntype = current_fn->ctype.base_type; rettype = fntype->ctype.base_type; if (!rettype || rettype == &void_ctype) { if (expr && expr->ctype && !is_void_type(expr->ctype)) expression_error(expr, "return expression in %s function", rettype?"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, rettype, &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 parse_asm_constraint(struct asm_operand *op) { struct expression *constraint = op->constraint; const char *str = constraint->string->data; int c; switch (str[0]) { case '\0': sparse_error(constraint->pos, "invalid ASM constraint (\"\")"); break; case '+': op->is_modify = true; /* fall-through */ case '=': op->is_assign = true; str++; break; } while ((c = *str++)) { switch (c) { case '=': case '+': sparse_error(constraint->pos, "invalid ASM constraint '%c'", c); break; case '&': op->is_earlyclobber = true; break; case '%': op->is_commutative = true; break; case 'r': op->is_register = true; break; case 'm': case 'o': case 'V': case 'Q': op->is_memory = true; break; case '<': case '>': // FIXME: ignored for now break; case ',': // FIXME: multiple alternative constraints break; case '0' ... '9': // FIXME: numeric matching constraint? break; case '[': // FIXME: symbolic matching constraint return; default: if (arch_target->asm_constraint) str = arch_target->asm_constraint(op, c, str); // FIXME: multi-letter constraints break; } } // FIXME: how to deal with multi-constraint? if (op->is_register) op->is_memory = 0; } static void verify_output_constraint(struct asm_operand *op) { struct expression *expr = op->constraint; const char *constraint = expr->string->data; if (!op->is_assign) expression_error(expr, "output constraint is not an assignment constraint (\"%s\")", constraint); } static void verify_input_constraint(struct asm_operand *op) { struct expression *expr = op->constraint; const char *constraint = expr->string->data; if (op->is_assign) expression_error(expr, "input constraint with assignment (\"%s\")", constraint); } static void evaluate_asm_memop(struct asm_operand *op) { if (op->is_memory) { struct expression *expr = op->expr; struct expression *addr; // implicit addressof addr = alloc_expression(expr->pos, EXPR_PREOP); addr->op = '&'; addr->unop = expr; evaluate_addressof(addr); op->expr = addr; } else { evaluate_expression(op->expr); degenerate(op->expr); } } static void evaluate_asm_statement(struct statement *stmt) { struct expression *expr; struct asm_operand *op; struct symbol *sym; if (!stmt->asm_string) return; FOR_EACH_PTR(stmt->asm_outputs, op) { /* Identifier */ /* Constraint */ if (op->constraint) { parse_asm_constraint(op); verify_output_constraint(op); } /* Expression */ expr = op->expr; if (!evaluate_expression(expr)) return; if (!lvalue_expression(expr)) warning(expr->pos, "asm output is not an lvalue"); evaluate_assign_to(expr, expr->ctype); evaluate_asm_memop(op); } END_FOR_EACH_PTR(op); FOR_EACH_PTR(stmt->asm_inputs, op) { /* Identifier */ /* Constraint */ if (op->constraint) { parse_asm_constraint(op); verify_input_constraint(op); } /* Expression */ if (!evaluate_expression(op->expr)) return; evaluate_asm_memop(op); } END_FOR_EACH_PTR(op); 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) { // no label associated, may be a computed goto evaluate_expression(stmt->goto_expression); return; } check_label_declaration(stmt->pos, label); } 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 unqualify_type(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.6.4/evaluate.h000066400000000000000000000017151411531012200147670ustar00rootroot00000000000000#ifndef EVALUATE_H #define EVALUATE_H struct expression; struct expression_list; struct statement; struct symbol; struct symbol_list; /// // evaluate the type of an expression // @expr: the expression to be evaluated // @return: the type of the expression or ``NULL`` // if the expression can't be evaluated struct symbol *evaluate_expression(struct expression *expr); /// // evaluate the type of a statement // @stmt: the statement to be evaluated // @return: the type of the statement or ``NULL`` // if it can't be evaluated struct symbol *evaluate_statement(struct statement *stmt); /// // evaluate the type of a set of symbols // @list: the list of the symbol to be evaluated void evaluate_symbol_list(struct symbol_list *list); /// // evaluate the arguments of a function // @argtypes: the list of the types in the prototype // @args: the list of the effective arguments int evaluate_arguments(struct symbol_list *argtypes, struct expression_list *args); #endif sparse-0.6.4/example.c000066400000000000000000001301771411531012200146140ustar00rootroot00000000000000/* * 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_COMPUTEDGOTO] = "jmp *", /* Binary */ [OP_ADD] = "add", [OP_SUB] = "sub", [OP_MUL] = "mul", [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", /* 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_LOAD] = "load", [OP_STORE] = "store", [OP_LABEL] = "label", [OP_SETVAL] = "set", /* Other */ [OP_PHI] = "phi", [OP_PHISOURCE] = "phisrc", [OP_COPY] = "copy", [OP_SEXT] = "sext", [OP_ZEXT] = "zext", [OP_TRUNC] = "trunc", [OP_FCVTU] = "fcvtu", [OP_FCVTS] = "fcvts", [OP_UCVTF] = "ucvtf", [OP_SCVTF] = "scvtf", [OP_FCVTF] = "fcvtf", [OP_UTPTR] = "utptr", [OP_PTRTU] = "utptr", [OP_PTRCAST] = "ptrcast", [OP_CALL] = "call", [OP_SLICE] = "slice", [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_TAG(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_TAG(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_TAG(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 || def->opcode == OP_LABEL)) { 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_TAG(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_TAG(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_TAG(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_LABEL & OP_SETVAL likewise doesn't actually generate any * code. On use, the "def" of the pseudo will be * looked up. */ case OP_LABEL: 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_MUL: case OP_AND: case OP_OR: case OP_XOR: 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_SEXT: case OP_ZEXT: case OP_TRUNC: case OP_PTRCAST: case OP_UTPTR: case OP_PTRTU: case OP_FCVTU: case OP_FCVTS: case OP_UCVTF: case OP_SCVTF: case OP_FCVTF: 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 || def->opcode == OP_LABEL)) { 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_TAG(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_TAG(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(filelist, file) { compile(sparse(file)); } END_FOR_EACH_PTR(file); return 0; } sparse-0.6.4/expand.c000066400000000000000000001011461411531012200144320ustar00rootroot00000000000000/* * 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 "evaluate.h" #include "expand.h" static int expand_expression(struct expression *); static int expand_statement(struct statement *); // If set, don't issue a warning on divide-by-0, invalid shift, ... // and don't mark the expression as erroneous but leave it as-is. // This allows testing some characteristics of the expression // without creating any side-effects (e.g.: is_zero_constant()). 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; } // expand compound literals (C99 & C11 6.5.2.5) // FIXME: is this the correct way to identify them? // All compound literals are anonymous but is // the reverse true? if (sym->initializer && !expr->symbol_name) return expand_expression(sym->initializer); /* 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->rank <= 0) { if (newtype->rank == 0) expr->fvalue = (double)expr->fvalue; else expr->fvalue = (float)expr->fvalue; } expr->type = EXPR_FVALUE; } /* Return true if constant shift size is valid */ static bool check_shift_count(struct expression *expr, struct expression *right) { struct symbol *ctype = expr->ctype; long long count = get_longlong(right); if (count >= 0 && count < ctype->bit_size) return true; return false; } /* * 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 (!check_shift_count(expr, right)) return 0; } 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; int rank = expr->ctype->rank; long double l, r, res; if (left->type != EXPR_FVALUE || right->type != EXPR_FVALUE) return 0; l = left->fvalue; r = right->fvalue; if (rank > 0) { 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 (rank == 0) { 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_QUALIFIER) 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 *valt = expr->cond_true; struct expression *valf = expr->cond_false; int cost, cond_cost; cond_cost = expand_expression(cond); if (cond->type == EXPR_VALUE) { unsigned flags = expr->flags; if (!cond->value) valt = valf; if (!valt) valt = cond; cost = expand_expression(valt); *expr = *valt; expr->flags = flags; if (expr->type == EXPR_VALUE) expr->taint |= cond->taint; return cost; } cost = expand_expression(valt); cost += expand_expression(valf); if (cost < SELECT_COST) { expr->type = EXPR_SELECT; cost -= BRANCH_COST - 1; } return cost + cond_cost + BRANCH_COST; } static void check_assignment(struct expression *expr) { struct expression *right; switch (expr->op) { case SPECIAL_SHL_ASSIGN: case SPECIAL_SHR_ASSIGN: right = expr->right; if (right->type != EXPR_VALUE) break; check_shift_count(expr, right); break; } return; } static int expand_assignment(struct expression *expr) { expand_expression(expr->left); expand_expression(expr->right); if (!conservative) check_assignment(expr); return SIDE_EFFECTS; } static int expand_addressof(struct expression *expr) { return expand_expression(expr->unop); } /// // lookup the type of a struct's memeber at the requested offset static struct symbol *find_member(struct symbol *sym, int offset) { struct ptr_list *head, *list; head = (struct ptr_list *) sym->symbol_list; list = head; if (!head) return NULL; do { int nr = list->nr; int i; for (i = 0; i < nr; i++) { struct symbol *ent = (struct symbol *) list->list[i]; int curr = ent->offset; if (curr == offset) return ent; if (curr > offset) return NULL; } } while ((list = list->next) != head); return NULL; } /// // lookup a suitable default initializer value at the requested offset static struct expression *default_initializer(struct symbol *sym, int offset) { static struct expression value; struct symbol *type; redo: switch (sym->type) { case SYM_NODE: sym = sym->ctype.base_type; goto redo; case SYM_STRUCT: type = find_member(sym, offset); if (!type) return NULL; break; case SYM_ARRAY: type = sym->ctype.base_type; break; default: return NULL; } if (is_integral_type(type)) value.type = EXPR_VALUE; else if (is_float_type(type)) value.type = EXPR_FVALUE; else return NULL; value.ctype = type; return &value; } /* * Look up a trustable initializer value at the requested offset. * * Return NULL if no such value can be found or statically trusted. */ static struct expression *constant_symbol_value(struct symbol *sym, int offset) { struct expression *value; if (sym->ctype.modifiers & MOD_ACCESS) 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) break; return entry->init_expr; } END_FOR_EACH_PTR(entry); value = default_initializer(sym, offset); } 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; while (unop->type == EXPR_BINOP && unop->op == '+') { struct expression *right = unop->right; if (right->type != EXPR_VALUE) break; offset += right->value; unop = unop->left; } if (unop->type == EXPR_SYMBOL) { struct symbol *sym = unop->symbol; struct symbol *ctype = expr->ctype; struct expression *value = constant_symbol_value(sym, offset); /* Const symbol with a constant initializer? */ if (value && value->ctype) { if (ctype->bit_size != value->ctype->bit_size) return UNSAFE; if (value->type == EXPR_VALUE) { if (!is_integral_type(ctype)) return UNSAFE; if (is_bitfield_type(value->ctype)) return UNSAFE; expr->type = EXPR_VALUE; expr->value = value->value; expr->taint = 0; return 0; } else if (value->type == EXPR_FVALUE) { if (!is_float_type(ctype)) return UNSAFE; 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; expand_expression(fn); if (fn->type != EXPR_PREOP) return SIDE_EFFECTS; if (ctype->ctype.modifiers & MOD_INLINE) { struct symbol *def; def = ctype->definition ? ctype->definition : ctype; if (inline_function(expr, def)) { struct symbol *fn = def->ctype.base_type; struct symbol *curr = current_fn; current_fn = def; evaluate_statement(expr->statement); current_fn = curr; fn->expanding = 1; cost = expand_expression(expr); fn->expanding = 0; return cost; } } 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_GENERIC: 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); expr->ctype = &int_ctype; expr->type = EXPR_VALUE; expr->value = 0; } } } 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; } static int expand_asm_statement(struct statement *stmt) { struct asm_operand *op; int cost = 0; FOR_EACH_PTR(stmt->asm_outputs, op) { cost += expand_expression(op->expr); } END_FOR_EACH_PTR(op); FOR_EACH_PTR(stmt->asm_inputs, op) { cost += expand_expression(op->expr); } END_FOR_EACH_PTR(op); return cost; } /* * 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: expand_asm_statement(stmt); 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 & CEF_ICE)) 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.6.4/expand.h000066400000000000000000000030001411531012200144250ustar00rootroot00000000000000#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.6.4/expression.c000066400000000000000000000640021411531012200153510ustar00rootroot00000000000000/* * 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" ALLOCATOR(type_expression, "type-expr-maps"); 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) { struct token *p; token = expect(token, '(', where); p = token; 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_label_scope(); token = compound_statement(token->next, stmt); end_label_scope(); token = expect(token, '}', "at end of statement expression"); } else token = parse_expression(token, expr); if (token == p) sparse_error(token->pos, "an expression is expected before ')'"); return expect(token, ')', where); } struct token *string_expression(struct token *token, struct expression **expr, const char *where) { struct token *next = primary_expression(token, expr); if (!*expr || (*expr)->type != EXPR_STRING) { sparse_error(token->pos, "string literal expected for %s", where); *expr = NULL; } return next; } /* * 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; /* NS_SYMBOL but in function-scope */ bind_symbol_with_scope(decl, ident, NS_SYMBOL, function_scope); 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); 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->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->op = '['; *p = e; p = &e->down; /* fall through */ case '.': token = token->next; e = alloc_expression(token->pos, EXPR_OFFSETOF); 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->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 && Wconstant_suffix) 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 = CEF_SET_INT; 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 = CEF_SET_FLOAT; expr->type = EXPR_FVALUE; return; Enoint: sparse_error(expr->pos, "constant %s is not a valid number", show_token(token)); expr->type = EXPR_VALUE; expr->value = 0; expr->ctype = &int_ctype; } static struct token *generic_selection(struct token *token, struct expression **tree) { struct expression *expr = alloc_expression(token->pos, EXPR_GENERIC); struct type_expression **last = &expr->map; token = expect(token, '(', "after '_Generic'"); token = assignment_expression(token, &expr->control); if (!match_op(token, ',')) { goto end; } while (match_op(token, ',')) { token = token->next; if (lookup_type(token)) { struct type_expression *map = __alloc_type_expression(0); token = typename(token, &map->type, NULL); token = expect(token, ':', "after typename"); token = assignment_expression(token, &map->expr); *last = map; last = &map->next; } else if (match_ident(token, &default_ident)) { if (expr->def) { warning(token->pos, "multiple default in generic expression"); info(expr->def->pos, "note: previous was here"); } token = token->next; token = expect(token, ':', "after typename"); token = assignment_expression(token, &expr->def); } } end: *tree = expr; return expect(token, ')', "after expression"); } 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 = CEF_SET_CHAR; 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 = CEF_SET_INT; 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; } if (token->ident == &_Generic_ident) { token = generic_selection(token->next, &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 = CEF_SET_ENUM; 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; /* * A pointer to an lvalue designating a static storage * duration object is an address constant [6.6(9)]. */ if (sym && (sym->ctype.modifiers & (MOD_TOPLEVEL | MOD_STATIC))) expr->flags = CEF_ADDR; 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"); break; } if (token->special == '[' && lookup_type(token->next)) { expr = alloc_expression(token->pos, EXPR_TYPE); 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 = CEF_SET_ICE; /* 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; *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, 1); if (!(sym->ctype.modifiers & MOD_ADDRESSABLE)) { sym->ctype.modifiers |= MOD_ADDRESSABLE; add_symbol(&function_computed_target_list, sym); } check_label_usage(sym, token->pos); label->flags = CEF_ADDR; 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 (toplevel(block_scope)) sym->ctype.modifiers |= MOD_TOPLEVEL; 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; 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->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->conditional = *tree; *tree = expr; token = parse_expression(token->next, &expr->cond_true); token = expect(token, ':', "in conditional expression"); token = conditional_expression(token, &expr->cond_false); } 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); struct token *next = token->next; expr->left = *tree; expr->op = op; *tree = expr; token = assignment_expression(next, &expr->right); if (token == next) expression_error(expr, "expression expected before '%s'", show_token(token)); return token; } } 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.6.4/expression.h000066400000000000000000000216171411531012200153630ustar00rootroot00000000000000#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, EXPR_GENERIC, }; /* * Flags for tracking the promotion of constness related attributes * from subexpressions to their parents. * * The flags are not independent as one might imply another. * The implications are as follows: * - CEF_INT, CEF_ENUM and * CEF_CHAR imply CEF_ICE. * * Use the CEF_*_SET_MASK and CEF_*_CLEAR_MASK * helper macros defined below to set or clear one of these flags. */ enum constexpr_flag { CEF_NONE = 0, /* * A constant in the sense of [6.4.4]: * - Integer constant [6.4.4.1] * - Floating point constant [6.4.4.2] * - Enumeration constant [6.4.4.3] * - Character constant [6.4.4.4] */ CEF_INT = (1 << 0), CEF_FLOAT = (1 << 1), CEF_ENUM = (1 << 2), CEF_CHAR = (1 << 3), /* * A constant expression in the sense of [6.6]: * - integer constant expression [6.6(6)] * - arithmetic constant expression [6.6(8)] * - address constant [6.6(9)] */ CEF_ICE = (1 << 4), CEF_ACE = (1 << 5), CEF_ADDR = (1 << 6), /* integer constant expression => arithmetic constant expression */ CEF_SET_ICE = (CEF_ICE | CEF_ACE), /* integer constant => integer constant expression */ CEF_SET_INT = (CEF_INT | CEF_SET_ICE), /* floating point constant => arithmetic constant expression */ CEF_SET_FLOAT = (CEF_FLOAT | CEF_ACE), /* enumeration constant => integer constant expression */ CEF_SET_ENUM = (CEF_ENUM | CEF_SET_ICE), /* character constant => integer constant expression */ CEF_SET_CHAR = (CEF_CHAR | CEF_SET_ICE), /* * Remove any "Constant" [6.4.4] flag, but retain the "constant * expression" [6.6] flags. */ CEF_CONST_MASK = (CEF_INT | CEF_FLOAT | CEF_CHAR), /* * not an integer constant expression => neither of integer, * enumeration and character constant */ CEF_CLR_ICE = (CEF_ICE | CEF_INT | CEF_ENUM | CEF_CHAR), }; enum { Taint_comma = 1, }; /* for expr->taint */ struct asm_operand { struct ident *name; struct expression *constraint; struct expression *expr; unsigned int is_assign:1; unsigned int is_modify:1; unsigned int is_earlyclobber:1; unsigned int is_commutative:1; unsigned int is_register:1; unsigned int is_memory:1; }; struct type_expression { struct symbol *type; struct expression *expr; struct type_expression *next; }; DECLARE_ALLOCATOR(type_expression); struct expression { enum expression_type type:8; unsigned flags:8; unsigned zero_init:1; 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; }; // EXPR_CAST, EXPR_FORCE_CAST, EXPR_IMPLIED_CAST, // EXPR_SIZEOF, EXPR_ALIGNOF and EXPR_PTRSIZEOF 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; }; }; // EXPR_GENERIC struct { struct expression *control; struct expression *def; struct type_expression *map; }; }; }; /// // Constant expression values // -------------------------- /// // test if an expression evaluates to the constant ``0``. // @return: ``1`` if @expr evaluate to ``0``, // ``0`` otherwise. int is_zero_constant(struct expression *expr); /// // test the compile time truth value of an expression // @return: // * ``-1`` if @expr is not constant, // * ``0`` or ``1`` depending on the truth value of @expr. 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 *string_expression(struct token *token, struct expression **expr, const char *where); struct token *assignment_expression(struct token *token, struct expression **tree); 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; expr->flags = CEF_NONE; 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; expr->flags = CEF_SET_INT; 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.6.4/flow.c000066400000000000000000000564771411531012200141420ustar00rootroot00000000000000/* * Copyright (C) 2004 Linus Torvalds */ /// // Flow simplification // ------------------- #include #include #include #include #include #include #include "parse.h" #include "expression.h" #include "linearize.h" #include "simplify.h" #include "flow.h" #include "target.h" unsigned long bb_generation; /// // remove phi-sources from a removed edge // // :note: It's possible to have several edges between the same BBs. // It's common with switches but it's also possible with branches. // This function will only remove a single phi-source per edge. int remove_phisources(struct basic_block *par, struct basic_block *old) { struct instruction *insn; int changed = 0; FOR_EACH_PTR(old->insns, insn) { pseudo_t phi; if (!insn->bb) continue; if (insn->opcode != OP_PHI) return changed; // found a phi-node in the target BB, // now look after its phi-sources. FOR_EACH_PTR(insn->phi_list, phi) { struct instruction *phisrc = phi->def; if (phi == VOID) continue; assert(phisrc->phi_node == insn); if (phisrc->bb != par) continue; // found a phi-source corresponding to this edge: // remove it but avoid the recursive killing. REPLACE_CURRENT_PTR(phi, VOID); remove_use(&phisrc->src); phisrc->bb = NULL; changed |= REPEAT_CSE; // Only the first one must be removed. goto next; } END_FOR_EACH_PTR(phi); next: ; } END_FOR_EACH_PTR(insn); return changed; } /// // remove all phisources but the one corresponding to the given target static int remove_other_phisources(struct basic_block *bb, struct multijmp_list *list, struct basic_block *target) { struct multijmp *jmp; int changed = 0; FOR_EACH_PTR(list, jmp) { if (jmp->target == target) { target = NULL; continue; } changed |= remove_phisources(bb, jmp->target); } END_FOR_EACH_PTR(jmp); return changed; } /* * 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; } /// // does the BB contains ignorable instructions but a final branch? // :note: something could be done for phi-sources but ... we'll see. static bool bb_is_forwarder(struct basic_block *bb) { struct instruction *insn; FOR_EACH_PTR(bb->insns, insn) { if (!insn->bb) continue; switch (insn->opcode) { case OP_NOP: case OP_INLINED_CALL: continue; case OP_CBR: case OP_BR: return true; default: goto out; } } END_FOR_EACH_PTR(insn); out: return false; } /// // check if the sources of a phi-node match with the parent BBs static bool phi_check(struct instruction *node) { struct basic_block *bb; pseudo_t phi; PREPARE_PTR_LIST(node->bb->parents, bb); FOR_EACH_PTR(node->phi_list, phi) { if (phi == VOID || !phi->def) continue; if (phi->def->bb != bb) return false; NEXT_PTR_LIST(bb); } END_FOR_EACH_PTR(phi); if (bb) return false; FINISH_PTR_LIST(bb); return true; } /* * 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 = !phi_check(first); FOR_EACH_PTR(first->phi_list, phi) { struct instruction *def = phi->def; struct basic_block *source, *target; pseudo_t pseudo; struct instruction *br; int cond; 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; cond = pseudo_truth_value(pseudo); if (cond < 0) continue; target = cond ? 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) { if (!insn->bb) continue; switch (insn->opcode) { case OP_CALL: /* FIXME! This should take "const" etc into account */ return 1; case OP_LOAD: if (!insn->type) return 1; if (insn->is_volatile) return 1; continue; 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 bb_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 = bb_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; convert_to_jump(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) { copy_ptr_list((struct ptr_list **)dst, (struct ptr_list *)src); } 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; } 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(struct instruction *insn, struct instruction *dom, int local) { switch (dom->opcode) { case OP_CALL: case OP_ENTRY: return local ? 0 : -1; case OP_LOAD: case OP_STORE: break; case OP_ASM: if (dom->clobber_memory) return -1; if (dom->output_memory) return -1; return 0; default: return 0; } if (dom->src != insn->src) { 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 (!overlapping_memop(insn, dom)) return 0; return -1; } return 1; } /* Kill a pseudo that is dead on exit from the bb */ // The context is: // * the variable is not global but may have its address used (local/non-local) // * the stores are only needed by others functions which would do some // loads via the escaped address // We start by the terminating BB (normal exit BB + no-return/unreachable) // We walkup the BB' intruction backward // * we're only concerned by loads, stores & calls // * if we reach a call -> we have to stop if var is non-local // * if we reach a load of our var -> we have to stop // * if we reach a store of our var -> we can kill it, it's dead // * we can ignore other stores & loads if the var is local // * if we reach another store or load done via non-symbol access // (so done via some address calculation) -> we have to stop // If we reach the top of the BB we can recurse into the parents BBs. static void kill_dead_stores_bb(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) { if (!insn->bb) continue; switch (insn->opcode) { case OP_LOAD: if (insn->src == pseudo) return; break; case OP_STORE: if (insn->src == pseudo) { kill_instruction_force(insn); continue; } break; case OP_CALL: if (!local) return; default: continue; } if (!local && insn->src->type != PSEUDO_SYM) return; } END_FOR_EACH_PTR_REVERSE(insn); FOR_EACH_PTR(bb->parents, parent) { if (bb_list_size(parent->children) > 1) continue; kill_dead_stores_bb(pseudo, generation, parent, local); } 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)) { if (insn->tainted) return; 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)); insn->tainted = 1; } } } static struct pseudo_user *first_user(pseudo_t p) { struct pseudo_user *pu; FOR_EACH_PTR(p->users, pu) { if (!pu) continue; return pu; } END_FOR_EACH_PTR(pu); return NULL; } void kill_dead_stores(struct entrypoint *ep, pseudo_t addr, int local) { unsigned long generation; struct basic_block *bb; switch (pseudo_user_list_size(addr->users)) { case 0: return; case 1: if (local) { struct pseudo_user *pu = first_user(addr); struct instruction *insn = pu->insn; if (insn->opcode == OP_STORE) { kill_instruction_force(insn); return; } } default: break; } generation = ++bb_generation; FOR_EACH_PTR(ep->bbs, bb) { if (bb->children) continue; kill_dead_stores_bb(addr, generation, bb, local); } END_FOR_EACH_PTR(bb); } 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) { if (!insn->bb) continue; 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; if (br->opcode == OP_CBR) { pseudo_t cond = br->cond; if (cond->type != PSEUDO_VAL) return NULL; target = cond->value ? target : br->bb_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); } /// // change a switch or a conditional branch into a branch int convert_to_jump(struct instruction *insn, struct basic_block *target) { struct basic_block *bb = insn->bb; struct basic_block *child; int changed = REPEAT_CSE; switch (insn->opcode) { case OP_CBR: changed |= remove_phisources(insn->bb, insn->bb_true == target ? insn->bb_false : insn->bb_true); break; case OP_SWITCH: changed |= remove_other_phisources(insn->bb, insn->multijmp_list, target); break; } kill_use(&insn->cond); insn->bb_true = target; insn->bb_false = NULL; insn->cond = NULL; insn->size = 0; insn->opcode = OP_BR; FOR_EACH_PTR(bb->children, child) { if (child == target) { target = NULL; // leave first occurence continue; } DELETE_CURRENT_PTR(child); remove_bb_from_list(&child->parents, bb, 1); changed |= REPEAT_CFG_CLEANUP; } END_FOR_EACH_PTR(child); PACK_PTR_LIST(&bb->children); repeat_phase |= changed; return changed; } static int retarget_parents(struct basic_block *bb, struct basic_block *target) { struct basic_block *parent; /* * 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))) { if (!rewrite_parent_branch(parent, bb, target)) return 0; } kill_bb(bb); return REPEAT_CFG_CLEANUP; } static void remove_merging_phisrc(struct instruction *insn, struct basic_block *bot) { struct instruction *node = insn->phi_node; pseudo_t phi; if (!node) { kill_instruction(insn); return; } FOR_EACH_PTR(node->phi_list, phi) { struct instruction *phisrc; if (phi == VOID) continue; phisrc = phi->def; if (phisrc->bb == bot) { kill_instruction(insn); return; } } END_FOR_EACH_PTR(phi); } static void remove_merging_phi(struct basic_block *top, struct instruction *insn) { pseudo_t phi; FOR_EACH_PTR(insn->phi_list, phi) { struct instruction *def; if (phi == VOID) continue; def = phi->def; if (def->bb != top) continue; convert_instruction_target(insn, def->src); kill_instruction(def); kill_instruction(insn); } END_FOR_EACH_PTR(phi); } /// // merge two BBs // @top: the first BB to be merged // @bot: the second BB to be merged static int merge_bb(struct basic_block *top, struct basic_block *bot) { struct instruction *insn; struct basic_block *bb; if (top == bot) return 0; top->children = bot->children; bot->children = NULL; bot->parents = NULL; FOR_EACH_PTR(top->children, bb) { replace_bb_in_list(&bb->parents, bot, top, 1); } END_FOR_EACH_PTR(bb); FOR_EACH_PTR(top->insns, insn) { if (!insn->bb) continue; if (insn->opcode != OP_PHISOURCE) continue; remove_merging_phisrc(insn, bot); } END_FOR_EACH_PTR(insn); kill_instruction(delete_last_instruction(&top->insns)); FOR_EACH_PTR(bot->insns, insn) { if (!insn->bb) continue; assert(insn->bb == bot); switch (insn->opcode) { case OP_PHI: remove_merging_phi(top, insn); continue; } insn->bb = top; add_instruction(&top->insns, insn); } END_FOR_EACH_PTR(insn); bot->insns = NULL; bot->ep = NULL; return REPEAT_CFG_CLEANUP; } /// // early simplification of the CFG // Three things are done here: // # inactive BB are removed // # branches to a 'forwarder' BB are redirected to the forwardee. // # merge single-child/single-parent BBs. int simplify_cfg_early(struct entrypoint *ep) { struct basic_block *bb; int changed = 0; FOR_EACH_PTR_REVERSE(ep->bbs, bb) { struct instruction *insn; struct basic_block *tgt; if (!bb->ep) { DELETE_CURRENT_PTR(bb); changed = REPEAT_CFG_CLEANUP; continue; } insn = last_instruction(bb->insns); if (!insn) continue; switch (insn->opcode) { case OP_BR: tgt = insn->bb_true; if (bb_is_forwarder(bb)) changed |= retarget_parents(bb, tgt); else if (bb_list_size(tgt->parents) == 1) changed |= merge_bb(bb, tgt); break; } } END_FOR_EACH_PTR_REVERSE(bb); return changed; } 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; 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_INLINED_CALL: 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); repeat_phase |= merge_bb(parent, bb); no_merge: /* nothing to do */; } END_FOR_EACH_PTR(bb); } sparse-0.6.4/flow.h000066400000000000000000000027361411531012200141340ustar00rootroot00000000000000#ifndef FLOW_H #define FLOW_H #include "lib.h" extern unsigned long bb_generation; #define REPEAT_CSE (1 << 0) #define REPEAT_CFG_CLEANUP (1 << 2) struct entrypoint; struct instruction; extern int remove_phisources(struct basic_block *par, struct basic_block *old); extern int simplify_flow(struct entrypoint *ep); extern void kill_dead_stores(struct entrypoint *ep, pseudo_t addr, int local); extern void simplify_symbol_usage(struct entrypoint *ep); extern void simplify_memops(struct entrypoint *ep); extern void pack_basic_blocks(struct entrypoint *ep); extern int simplify_cfg_early(struct entrypoint *ep); extern int convert_to_jump(struct instruction *insn, struct basic_block *target); extern void convert_instruction_target(struct instruction *insn, pseudo_t src); extern void remove_dead_insns(struct entrypoint *); extern void kill_bb(struct basic_block *); extern void kill_use(pseudo_t *); extern void remove_use(pseudo_t *); extern void kill_unreachable_bbs(struct entrypoint *ep); extern int kill_insn(struct instruction *, int force); static inline int kill_instruction(struct instruction *insn) { return kill_insn(insn, 0); } static inline int kill_instruction_force(struct instruction *insn) { return kill_insn(insn, 1); } void check_access(struct instruction *insn); int dominates(struct instruction *insn, struct instruction *dom, int local); extern void vrfy_flow(struct entrypoint *ep); extern int pseudo_in_list(struct pseudo_list *list, pseudo_t pseudo); #endif sparse-0.6.4/flowgraph.c000066400000000000000000000114711411531012200151450ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Various utilities for flowgraphs. // // Copyright (c) 2017 Luc Van Oostenryck. // #include "flowgraph.h" #include "linearize.h" #include "flow.h" // for bb_generation #include #include #include struct cfg_info { struct basic_block_list *list; unsigned long gen; unsigned int nr; }; static void label_postorder(struct basic_block *bb, struct cfg_info *info) { struct basic_block *child; if (bb->generation == info->gen) return; bb->generation = info->gen; FOR_EACH_PTR_REVERSE(bb->children, child) { label_postorder(child, info); } END_FOR_EACH_PTR_REVERSE(child); bb->postorder_nr = info->nr++; add_bb(&info->list, bb); } static void reverse_bbs(struct basic_block_list **dst, struct basic_block_list *src) { struct basic_block *bb; FOR_EACH_PTR_REVERSE(src, bb) { add_bb(dst, bb); } END_FOR_EACH_PTR_REVERSE(bb); } static void debug_postorder(struct entrypoint *ep) { struct basic_block *bb; printf("%s's reverse postorder:\n", show_ident(ep->name->ident)); FOR_EACH_PTR(ep->bbs, bb) { printf("\t.L%u: %u\n", bb->nr, bb->postorder_nr); } END_FOR_EACH_PTR(bb); } // // cfg_postorder - Set the BB's reverse postorder links // // Do a postorder DFS walk and set the links // (which will do the reverse part). // int cfg_postorder(struct entrypoint *ep) { struct cfg_info info = { .gen = ++bb_generation, }; label_postorder(ep->entry->bb, &info); // OK, now info.list contains the node in postorder // Reuse ep->bbs for the reverse postorder. free_ptr_list(&ep->bbs); ep->bbs = NULL; reverse_bbs(&ep->bbs, info.list); free_ptr_list(&info.list); if (dbg_postorder) debug_postorder(ep); return info.nr; } // // Calculate the dominance tree following: // "A simple, fast dominance algorithm" // by K. D. Cooper, T. J. Harvey, and K. Kennedy. // cfr. http://www.cs.rice.edu/∼keith/EMBED/dom.pdf // static struct basic_block *intersect_dom(struct basic_block *doms[], struct basic_block *b1, struct basic_block *b2) { int f1 = b1->postorder_nr, f2 = b2->postorder_nr; while (f1 != f2) { while (f1 < f2) { b1 = doms[f1]; f1 = b1->postorder_nr; } while (f2 < f1) { b2 = doms[f2]; f2 = b2->postorder_nr; } } return b1; } static void debug_domtree(struct entrypoint *ep) { struct basic_block *bb = ep->entry->bb; printf("%s's idoms:\n", show_ident(ep->name->ident)); FOR_EACH_PTR(ep->bbs, bb) { if (bb == ep->entry->bb) continue; // entry node has no idom printf("\t%s <- %s\n", show_label(bb), show_label(bb->idom)); } END_FOR_EACH_PTR(bb); } void domtree_build(struct entrypoint *ep) { struct basic_block *entry = ep->entry->bb; struct basic_block **doms; struct basic_block *bb; unsigned int size; int max_level = 0; int changed; // First calculate the (reverse) postorder. // This will give use us: // - the links to do a reverse postorder traversal // - the order number for each block size = cfg_postorder(ep); // initialize the dominators array doms = calloc(size, sizeof(*doms)); assert(entry->postorder_nr == size-1); doms[size-1] = entry; do { struct basic_block *b; changed = 0; FOR_EACH_PTR(ep->bbs, b) { struct basic_block *p; int bnr = b->postorder_nr; struct basic_block *new_idom = NULL; if (b == entry) continue; // ignore entry node FOR_EACH_PTR(b->parents, p) { unsigned int pnr = p->postorder_nr; if (!doms[pnr]) continue; if (!new_idom) { new_idom = p; continue; } new_idom = intersect_dom(doms, p, new_idom); } END_FOR_EACH_PTR(p); assert(new_idom); if (doms[bnr] != new_idom) { doms[bnr] = new_idom; changed = 1; } } END_FOR_EACH_PTR(b); } while (changed); FOR_EACH_PTR(ep->bbs, bb) { free_ptr_list(&bb->doms); } END_FOR_EACH_PTR(bb); // set the idom links FOR_EACH_PTR(ep->bbs, bb) { struct basic_block *idom = doms[bb->postorder_nr]; if (bb == entry) continue; // ignore entry node bb->idom = idom; add_bb(&idom->doms, bb); } END_FOR_EACH_PTR(bb); entry->idom = NULL; // set the dominance levels FOR_EACH_PTR(ep->bbs, bb) { struct basic_block *idom = bb->idom; int level = idom ? idom->dom_level + 1 : 0; bb->dom_level = level; if (max_level < level) max_level = level; } END_FOR_EACH_PTR(bb); ep->dom_levels = max_level + 1; free(doms); if (dbg_domtree) debug_domtree(ep); } // dt_dominates - does BB a dominates BB b? bool domtree_dominates(struct basic_block *a, struct basic_block *b) { if (a == b) // dominance is reflexive return true; if (a == b->idom) return true; if (b == a->idom) return false; // can't dominate if deeper in the DT if (a->dom_level >= b->dom_level) return false; // FIXME: can be faster if we have the DFS in-out numbers // walk up the dominator tree for (b = b->idom; b; b = b->idom) { if (b == a) return true; } return false; } sparse-0.6.4/flowgraph.h000066400000000000000000000015121411531012200151450ustar00rootroot00000000000000#ifndef FLOWGRAPH_H #define FLOWGRAPH_H /// // Utilities for flowgraphs // ------------------------ #include struct entrypoint; struct basic_block; /// // Set the BB's reverse postorder links // Each BB will also have its 'order number' set. int cfg_postorder(struct entrypoint *ep); /// // Build the dominance tree. // Each BB will then have: // - a link to its immediate dominator (::idom) // - the list of BB it immediately dominates (::doms) // - its level in the dominance tree (::dom_level) void domtree_build(struct entrypoint *ep); /// // Test the dominance between two basic blocks. // @a: the basic block expected to dominate // @b: the basic block expected to be dominated // @return: ``true`` if @a dominates @b, ``false`` otherwise. bool domtree_dominates(struct basic_block *a, struct basic_block *b); #endif sparse-0.6.4/gcc-attr-list.h000066400000000000000000000105771411531012200156440ustar00rootroot00000000000000GCC_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(alloc_align) GCC_ATTR(alloc_size) GCC_ATTR(altivec) GCC_ATTR(always_inline) GCC_ATTR(artificial) GCC_ATTR(assume_aligned) GCC_ATTR(aux) 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(constructor) GCC_ATTR(critical) GCC_ATTR(deprecated) 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(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_return) GCC_ATTR(function_return_mem) GCC_ATTR(function_return_reg) 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(indirect_branch) GCC_ATTR(indirect_branch_call) GCC_ATTR(indirect_branch_jump) 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(jli_always) GCC_ATTR(jli_fixed) 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(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(nocf_check) 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(nonstring) GCC_ATTR(noplt) GCC_ATTR(nosave_low_regs) GCC_ATTR(not_nested) GCC_ATTR(nothrow) GCC_ATTR(notshared) GCC_ATTR(optimize) GCC_ATTR(partial_save) GCC_ATTR(patchable_function_entry) GCC_ATTR(pcs) GCC_ATTR(persistent) GCC_ATTR(progmem) GCC_ATTR(protected) 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(secure_call) 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(trap_exit) GCC_ATTR(trapa_handler) GCC_ATTR(uncached) 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_if_not_aligned) GCC_ATTR(warn_unused) GCC_ATTR(warn_unused_result) GCC_ATTR(warning) GCC_ATTR(weak) GCC_ATTR(weakref) GCC_ATTR(zda) sparse-0.6.4/gdbhelpers000066400000000000000000000121061411531012200150460ustar00rootroot00000000000000 # 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_RESTRICT) printf "MOD_RESTRICT " end if ($arg0->modifiers & MOD_ATOMIC) printf "MOD_ATOMIC " end if ($arg0->modifiers & MOD_SIGNED) printf "MOD_SIGNED " end if ($arg0->modifiers & MOD_UNSIGNED) printf "MOD_UNSIGNED " 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_TOPLEVEL) printf "MOD_TOPLEVEL " end if ($arg0->modifiers & MOD_ASSIGNED) printf "MOD_ASSIGNED " 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.6.4/graph.c000066400000000000000000000132421411531012200142530ustar00rootroot00000000000000/* 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) { if (!insn->bb) continue; switch(insn->opcode) { case OP_STORE: if (insn->src->type == PSEUDO_SYM) { printf("%s store(%s)", s, show_ident(insn->src->sym->ident)); s = ","; } break; case OP_LOAD: if (insn->src->type == PSEUDO_SYM) { printf("%s load(%s)", s, show_ident(insn->src->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->bb) continue; 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(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(sym); } END_FOR_EACH_PTR(file); /* Graph inter-file calls */ FOR_EACH_PTR(all_syms, sym) { if (sym->ep) graph_calls(sym->ep, 0); } END_FOR_EACH_PTR(sym); printf("}\n"); return 0; } sparse-0.6.4/gvpr/000077500000000000000000000000001411531012200137625ustar00rootroot00000000000000sparse-0.6.4/gvpr/return-paths000077500000000000000000000041371411531012200163510ustar00rootroot00000000000000#!/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.6.4/gvpr/subg-fwd000077500000000000000000000031471411531012200154330ustar00rootroot00000000000000#!/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.6.4/gvpr/subg-rev000077500000000000000000000036271411531012200154520ustar00rootroot00000000000000#!/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.6.4/ident-list.h000066400000000000000000000043631411531012200152370ustar00rootroot00000000000000 #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(_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(c_alignas); IDENT(c_alignof); IDENT(c_generic_selections); IDENT(c_static_assert); __IDENT(pragma_ident, "__pragma__", 0); __IDENT(__VA_ARGS___ident, "__VA_ARGS__", 0); __IDENT(__func___ident, "__func__", 0); __IDENT(__FUNCTION___ident, "__FUNCTION__", 0); __IDENT(__PRETTY_FUNCTION___ident, "__PRETTY_FUNCTION__", 0); /* Sparse commands */ IDENT_RESERVED(__context__); IDENT_RESERVED(__range__); /* Magic function names we recognize */ IDENT(memset); IDENT(memcpy); IDENT(copy_to_user); IDENT(copy_from_user); IDENT(main); /* used by the symbolic checker */ IDENT(__assume); IDENT(__assert); IDENT(__assert_eq); IDENT(__assert_const); #undef __IDENT #undef IDENT #undef IDENT_RESERVED sparse-0.6.4/inline.c000066400000000000000000000367601411531012200144420ustar00rootroot00000000000000/* * 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" #include "evaluate.h" static void copy_statement(struct statement *src, struct statement *dst); 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 *valt = copy_expression(expr->cond_true); struct expression *valf = copy_expression(expr->cond_false); if (cond == expr->conditional && valt == expr->cond_true && valf == expr->cond_false) break; expr = dup_expression(expr); expr->conditional = cond; expr->cond_true = valt; expr->cond_false = valf; 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; } case EXPR_GENERIC: expr = dup_expression(expr); expr->control = copy_expression(expr->control); if (!evaluate_expression(expr)) return NULL; expr = copy_expression(expr); break; default: warning(expr->pos, "trying to copy expression type %d", expr->type); } return expr; } static struct asm_operand_list *copy_asm_operands(struct asm_operand_list *in) { struct asm_operand_list *out = NULL; struct asm_operand *old; FOR_EACH_PTR(in, old) { struct asm_operand *new = __alloc_asm_operand(0); new->name = old->name; new->constraint = copy_expression(old->constraint); new->expr = copy_expression(old->expr); add_ptr_list(&out, new); } END_FOR_EACH_PTR(old); 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 *valt = stmt->if_true; struct statement *valf = stmt->if_false; cond = copy_expression(cond); valt = copy_one_statement(valt); valf = copy_one_statement(valf); if (stmt->if_conditional == cond && stmt->if_true == valt && stmt->if_false == valf) break; stmt = dup_statement(stmt); stmt->if_conditional = cond; stmt->if_true = valt; stmt->if_false = valf; 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_operands(stmt->asm_inputs); stmt->asm_outputs = copy_asm_operands(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. */ static 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; 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); 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.6.4/ir.c000066400000000000000000000077641411531012200136000ustar00rootroot00000000000000// SPDX-License-Identifier: MIT #include "ir.h" #include "linearize.h" #include #include static int nbr_phi_operands(struct instruction *insn) { pseudo_t p; int nbr = 0; if (!insn->phi_list) return 0; FOR_EACH_PTR(insn->phi_list, p) { if (p == VOID) continue; nbr++; } END_FOR_EACH_PTR(p); return nbr; } static int check_phi_node(struct instruction *insn) { struct basic_block *par; pseudo_t phi; int err = 0; if (!has_users(insn->target)) return err; if (bb_list_size(insn->bb->parents) != nbr_phi_operands(insn)) { sparse_error(insn->pos, "bad number of phi operands in:\n\t%s", show_instruction(insn)); info(insn->pos, "parents: %d", bb_list_size(insn->bb->parents)); info(insn->pos, "phisrcs: %d", nbr_phi_operands(insn)); return 1; } PREPARE_PTR_LIST(insn->bb->parents, par); FOR_EACH_PTR(insn->phi_list, phi) { struct instruction *src; if (phi == VOID) continue; assert(phi->type == PSEUDO_PHI); src = phi->def; if (src->bb != par) { sparse_error(src->pos, "wrong BB for %s:", show_instruction(src)); info(src->pos, "expected: %s", show_label(par)); info(src->pos, " got: %s", show_label(src->bb)); err++; } NEXT_PTR_LIST(par); } END_FOR_EACH_PTR(phi); FINISH_PTR_LIST(par); return err; } static int check_user(struct instruction *insn, pseudo_t pseudo) { struct instruction *def; if (!pseudo) { show_entry(insn->bb->ep); sparse_error(insn->pos, "null pseudo in %s", show_instruction(insn)); return 1; } switch (pseudo->type) { case PSEUDO_PHI: case PSEUDO_REG: def = pseudo->def; if (def && def->bb) break; show_entry(insn->bb->ep); sparse_error(insn->pos, "wrong usage for %s in %s", show_pseudo(pseudo), show_instruction(insn)); return 1; default: break; } return 0; } static int check_branch(struct entrypoint *ep, struct instruction *insn, struct basic_block *bb) { if (bb->ep && lookup_bb(ep->bbs, bb)) return 0; sparse_error(insn->pos, "branch to dead BB: %s", show_instruction(insn)); return 1; } static int check_switch(struct entrypoint *ep, struct instruction *insn) { struct multijmp *jmp; int err = 0; FOR_EACH_PTR(insn->multijmp_list, jmp) { err = check_branch(ep, insn, jmp->target); if (err) return err; } END_FOR_EACH_PTR(jmp); return err; } static int check_return(struct instruction *insn) { struct symbol *ctype = insn->type; if (ctype && ctype->bit_size > 0 && insn->src == VOID) { sparse_error(insn->pos, "return without value"); return 1; } return 0; } static int validate_insn(struct entrypoint *ep, struct instruction *insn) { int err = 0; switch (insn->opcode) { case OP_SEL: case OP_RANGE: err += check_user(insn, insn->src3); /* fall through */ case OP_BINARY ... OP_BINCMP_END: err += check_user(insn, insn->src2); /* fall through */ case OP_UNOP ... OP_UNOP_END: case OP_SLICE: case OP_SYMADDR: case OP_PHISOURCE: err += check_user(insn, insn->src1); break; case OP_CBR: err += check_branch(ep, insn, insn->bb_true); err += check_branch(ep, insn, insn->bb_false); /* fall through */ case OP_COMPUTEDGOTO: err += check_user(insn, insn->cond); break; case OP_PHI: err += check_phi_node(insn); break; case OP_CALL: // FIXME: ignore for now break; case OP_STORE: err += check_user(insn, insn->target); /* fall through */ case OP_LOAD: err += check_user(insn, insn->src); break; case OP_RET: err += check_return(insn); break; case OP_BR: err += check_branch(ep, insn, insn->bb_true); break; case OP_SWITCH: err += check_switch(ep, insn); break; case OP_ENTRY: case OP_LABEL: case OP_SETVAL: default: break; } return err; } int ir_validate(struct entrypoint *ep) { struct basic_block *bb; int err = 0; if (!dbg_ir || has_error) return 0; FOR_EACH_PTR(ep->bbs, bb) { struct instruction *insn; FOR_EACH_PTR(bb->insns, insn) { if (!insn->bb) continue; err += validate_insn(ep, insn); } END_FOR_EACH_PTR(insn); } END_FOR_EACH_PTR(bb); if (err) abort(); return err; } sparse-0.6.4/ir.h000066400000000000000000000001451411531012200135670ustar00rootroot00000000000000#ifndef _IR_H #define _IR_H #include "linearize.h" int ir_validate(struct entrypoint *ep); #endif sparse-0.6.4/lib.c000066400000000000000000000267111411531012200137250ustar00rootroot00000000000000/* * '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 #include "lib.h" #include "allocate.h" #include "token.h" #include "parse.h" #include "symbol.h" #include "expression.h" #include "evaluate.h" #include "scope.h" #include "linearize.h" #include "target.h" #include "machine.h" #include "bits.h" static int prettify(const char **fnamep) { const char *name = *fnamep; int len = strlen(name); if (len > 2 && !memcmp(name, "./", 2)) { name += 2; len -= 2; } *fnamep = name; return len; } static const char *show_include_chain(int stream, const char *base) { static char buffer[200]; int len = 0; while ((stream = stream_prev(stream)) >= 0) { const char *p = stream_name(stream); int pretty_len; if (p == base) break; pretty_len = prettify(&p); if (pretty_len <= 0) break; /* * At worst, we'll need " (through %s, ...)" in addition to the * new filename */ if (pretty_len + len + 20 > sizeof(buffer)) { if (!len) return ""; memcpy(buffer+len, ", ...", 5); len += 5; break; } if (!len) { memcpy(buffer, " (through ", 10); len = 10; } else { buffer[len++] = ','; buffer[len++] = ' '; } memcpy(buffer+len, p, pretty_len); len += pretty_len; } if (!len) return ""; buffer[len] = ')'; buffer[len+1] = 0; return buffer; } static const char *show_stream_name(struct position pos) { const char *name = stream_name(pos.stream); static const char *last; if (name == base_filename) return name; if (name == last) return name; last = name; fprintf(stderr, "%s: note: in included file%s:\n", base_filename, show_include_chain(pos.stream, base_filename)); return name; } static void do_warn(const char *type, struct position pos, const char * fmt, va_list args) { static char buffer[512]; /* Shut up warnings if position is bad_token.pos */ if (pos.type == TOKEN_BAD) return; vsprintf(buffer, fmt, args); fflush(stdout); fprintf(stderr, "%s:%d:%d: %s%s%s\n", show_stream_name(pos), pos.line, pos.pos, diag_prefix, type, buffer); } 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 if position is bad_token.pos */ if (pos.type == TOKEN_BAD) return; /* Shut up warnings after an error */ has_error |= ERROR_CURR_PHASE; if (errors > fmax_errors) { 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 (!fmax_warnings || has_error) { show_info = 0; return; } if (!--fmax_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%s\n", diag_prefix, buffer); exit(1); } //////////////////////////////////////////////////////////////////////////////// static struct token *pre_buffer_begin = NULL; static struct token **pre_buffer_next = &pre_buffer_begin; 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); *pre_buffer_next = begin; pre_buffer_next = &end->next; } static void create_builtin_stream(void) { // Temporary hack add_pre_buffer("#define _Pragma(x)\n"); /* 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 __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"); } 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 || dump_macros_only) { if (!builtin) dump_macro_definitions(); if (dump_macros_only) return NULL; } 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); } base_filename = filename; // Tokenize the input stream token = tokenize(NULL, 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; base_filename = "command-line"; // Initialize symbol stream first, so that we can add defines etc init_symbols(); // initialize the default target to the native 'machine' target_config(MACH_NATIVE); args = argv; for (;;) { char *arg = *++args; if (!arg) break; if (arg[0] == '-' && arg[1]) { args = handle_switch(arg+1, args); continue; } add_ptr_list(filelist, arg); } handle_switch_finalize(); // Redirect stdout if needed if (dump_macro_defs || preprocess_only) do_output = 1; if (do_output && outfile && strcmp(outfile, "-")) { if (!freopen(outfile, "w", stdout)) die("error: cannot open %s: %s", outfile, strerror(errno)); } if (fdump_ir == 0) fdump_ir = PASS_FINAL; list = NULL; if (filelist) { // Initialize type system target_init(); init_ctype(); predefined_macros(); create_builtin_stream(); init_builtins(0); list = sparse_initial(); /* * Protect the initial token allocations, since * they need to survive all the others */ protect_token_alloc(); } /* * Evaluate the complete symbol list * Note: This is not needed for normal cases. * These symbols should only be predefined defines and * declaratons which will be evaluated later, when needed. * This is also the case when a file is directly included via * '-include ' on the command line *AND* the file only * contains defines, declarations and inline definitions. * However, in the rare cases where the given file should * contain some definitions, these will never be evaluated * and thus won't be able to be linearized correctly. * Hence the evaluate_symbol_list() here under. */ evaluate_symbol_list(list); 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.6.4/lib.h000066400000000000000000000160501411531012200137250ustar00rootroot00000000000000#ifndef LIB_H #define LIB_H #include #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" #include "utils.h" #include "bits.h" #include "options.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 #ifdef __gnu_hurd__ #define PATH_MAX 4096 // Hurd doesn't define this #endif extern const char *sparse_version; 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 asm_operand; 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(asm_operand_list, struct asm_operand); 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; #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; enum phase { PASS__PARSE, PASS__LINEARIZE, PASS__MEM2REG, PASS__OPTIM, PASS__FINAL, }; #define PASS_PARSE (1UL << PASS__PARSE) #define PASS_LINEARIZE (1UL << PASS__LINEARIZE) #define PASS_MEM2REG (1UL << PASS__MEM2REG) #define PASS_OPTIM (1UL << PASS__OPTIM) #define PASS_FINAL (1UL << PASS__FINAL) extern void add_pre_buffer(const char *fmt, ...) FORMAT_ATTR(1); extern void predefine(const char *name, int weak, const char *fmt, ...) FORMAT_ATTR(3); extern void predefine_strong(const char *name, ...) FORMAT_ATTR(1); extern void predefine_weak(const char *name, ...) FORMAT_ATTR(1); extern void predefine_nostd(const char *name); extern void predefined_macros(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(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 *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 struct symbol *first_symbol(struct symbol_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.6.4/linearize.c000066400000000000000000002054621411531012200151430ustar00rootroot00000000000000/* * 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 "optimize.h" #include "flow.h" #include "target.h" static pseudo_t linearize_statement(struct entrypoint *ep, struct statement *stmt); static pseudo_t linearize_expression(struct entrypoint *ep, struct expression *expr); static pseudo_t add_cast(struct entrypoint *ep, struct symbol *to, struct symbol *from, int op, pseudo_t src); static pseudo_t add_binary_op(struct entrypoint *ep, struct symbol *ctype, int op, pseudo_t left, pseudo_t right); static pseudo_t linearize_one_symbol(struct entrypoint *ep, struct symbol *sym); 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->pos = pos; bb->ep = ep; bb->nr = nr++; return bb; } static struct multijmp *alloc_multijmp(struct basic_block *target, long long begin, long long end) { struct multijmp *multijmp = __alloc_multijmp(0); multijmp->target = target; multijmp->begin = begin; multijmp->end = end; return multijmp; } const char *show_label(struct basic_block *bb) { static int n; static char buffer[4][16]; char *buf = buffer[3 & ++n]; if (!bb) return ".L???"; snprintf(buf, 64, ".L%u", bb->nr); return buf; } 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) { snprintf(buf, 64, ""); break; } if (sym->bb_target) { snprintf(buf, 64, "%s", show_label(sym->bb_target)); break; } if (sym->ident) { snprintf(buf, 64, "%s", show_ident(sym->ident)); break; } expr = sym->initializer; snprintf(buf, 64, "", verbose ? sym : NULL); 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; case PSEUDO_UNDEF: return "UNDEF"; 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_UNREACH] = "unreachable", [OP_COMPUTEDGOTO] = "jmp *", /* Binary */ [OP_ADD] = "add", [OP_SUB] = "sub", [OP_MUL] = "mul", [OP_DIVU] = "divu", [OP_DIVS] = "divs", [OP_MODU] = "modu", [OP_MODS] = "mods", [OP_SHL] = "shl", [OP_LSR] = "lsr", [OP_ASR] = "asr", /* Floating-point Binary */ [OP_FADD] = "fadd", [OP_FSUB] = "fsub", [OP_FMUL] = "fmul", [OP_FDIV] = "fdiv", /* Logical */ [OP_AND] = "and", [OP_OR] = "or", [OP_XOR] = "xor", /* 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", /* floating-point comparison */ [OP_FCMP_ORD] = "fcmpord", [OP_FCMP_OEQ] = "fcmpoeq", [OP_FCMP_ONE] = "fcmpone", [OP_FCMP_OLE] = "fcmpole", [OP_FCMP_OGE] = "fcmpoge", [OP_FCMP_OLT] = "fcmpolt", [OP_FCMP_OGT] = "fcmpogt", [OP_FCMP_UEQ] = "fcmpueq", [OP_FCMP_UNE] = "fcmpune", [OP_FCMP_ULE] = "fcmpule", [OP_FCMP_UGE] = "fcmpuge", [OP_FCMP_ULT] = "fcmpult", [OP_FCMP_UGT] = "fcmpugt", [OP_FCMP_UNO] = "fcmpuno", /* Uni */ [OP_NOT] = "not", [OP_NEG] = "neg", [OP_FNEG] = "fneg", /* Special three-input */ [OP_SEL] = "select", [OP_FMADD] = "fmadd", /* Memory */ [OP_LOAD] = "load", [OP_STORE] = "store", [OP_LABEL] = "label", [OP_SETVAL] = "set", [OP_SETFVAL] = "setfval", [OP_SYMADDR] = "symaddr", /* Other */ [OP_PHI] = "phi", [OP_PHISOURCE] = "phisrc", [OP_SEXT] = "sext", [OP_ZEXT] = "zext", [OP_TRUNC] = "trunc", [OP_FCVTU] = "fcvtu", [OP_FCVTS] = "fcvts", [OP_UCVTF] = "ucvtf", [OP_SCVTF] = "scvtf", [OP_FCVTF] = "fcvtf", [OP_UTPTR] = "utptr", [OP_PTRTU] = "ptrtu", [OP_PTRCAST] = "ptrcast", [OP_INLINED_CALL] = "# call", [OP_CALL] = "call", [OP_SLICE] = "slice", [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, %s, %s", show_pseudo(insn->cond), show_label(insn->bb_true), show_label(insn->bb_false)); break; case OP_BR: buf += sprintf(buf, "%s", show_label(insn->bb_true)); break; case OP_LABEL: buf += sprintf(buf, "%s <- ", show_pseudo(insn->target)); buf += sprintf(buf, "%s", show_label(insn->bb_true)); break; case OP_SETVAL: { struct expression *expr = insn->val; 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, "%Le", expr->fvalue); break; case EXPR_STRING: buf += sprintf(buf, "%.40s", show_string(expr->string)); break; case EXPR_SYMBOL: buf += sprintf(buf, "%s", show_ident(expr->symbol->ident)); break; case EXPR_LABEL: buf += sprintf(buf, "%s", show_label(expr->symbol->bb_target)); break; default: buf += sprintf(buf, "SETVAL EXPR TYPE %d", expr->type); } break; } case OP_SETFVAL: buf += sprintf(buf, "%s <- ", show_pseudo(insn->target)); buf += sprintf(buf, "%Le", insn->fvalue); 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, ", %lld -> %s", jmp->begin, show_label(jmp->target)); else if (jmp->begin < jmp->end) buf += sprintf(buf, ", %lld ... %lld -> %s", jmp->begin, jmp->end, show_label(jmp->target)); else buf += sprintf(buf, ", default -> %s", show_label(jmp->target)); } END_FOR_EACH_PTR(jmp); break; } case OP_COMPUTEDGOTO: { struct multijmp *jmp; buf += sprintf(buf, "%s", show_pseudo(insn->src)); FOR_EACH_PTR(insn->multijmp_list, jmp) { buf += sprintf(buf, ", %s", show_label(jmp->target)); } END_FOR_EACH_PTR(jmp); break; } case OP_UNREACH: break; case OP_PHISOURCE: { buf += sprintf(buf, "%s <- %s ", show_pseudo(insn->target), show_pseudo(insn->phi_src)); 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) { if (phi == VOID && !verbose) continue; buf += sprintf(buf, "%s %s", s, show_pseudo(phi)); s = ","; } END_FOR_EACH_PTR(phi); break; } case OP_LOAD: buf += sprintf(buf, "%s <- %lld[%s]", show_pseudo(insn->target), insn->offset, show_pseudo(insn->src)); break; case OP_STORE: buf += sprintf(buf, "%s -> %lld[%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_SEXT: case OP_ZEXT: case OP_TRUNC: case OP_FCVTU: case OP_FCVTS: case OP_UCVTF: case OP_SCVTF: case OP_FCVTF: case OP_UTPTR: case OP_PTRTU: 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_FPCMP ... OP_FPCMP_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: case OP_FMADD: 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 <- (%d) %s, %d", show_pseudo(insn->target), type_size(insn->orig_type), show_pseudo(insn->src), insn->from); break; case OP_NOT: case OP_NEG: case OP_FNEG: case OP_SYMADDR: 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("%s:\n", show_label(bb)); if (verbose) { pseudo_t needs, defines; printf("%s:%d\n", stream_name(bb->pos.stream), bb->pos.line); FOR_EACH_PTR(bb->needs, needs) { struct instruction *def = needs->def; if (def->opcode != OP_PHI) { printf(" **uses %s (from %s)**\n", show_pseudo(needs), show_label(def->bb)); } else { pseudo_t phi; const char *sep = " "; printf(" **uses %s (from", show_pseudo(needs)); FOR_EACH_PTR(def->phi_list, phi) { if (phi == VOID) continue; printf("%s(%s:%s)", sep, show_pseudo(phi), show_label(phi->def->bb)); sep = ", "; } END_FOR_EACH_PTR(phi); printf(")**\n"); } } END_FOR_EACH_PTR(needs); FOR_EACH_PTR(bb->defines, defines) { printf(" **defines %s **\n", show_pseudo(defines)); } END_FOR_EACH_PTR(defines); if (bb->parents) { struct basic_block *from; FOR_EACH_PTR(bb->parents, from) { printf(" **from %s (%s:%d:%d)**\n", show_label(from), stream_name(from->pos.stream), from->pos.line, from->pos.pos); } END_FOR_EACH_PTR(from); } if (bb->children) { struct basic_block *to; FOR_EACH_PTR(bb->children, to) { printf(" **to %s (%s:%d:%d)**\n", show_label(to), stream_name(to->pos.stream), to->pos.line, to->pos.pos); } END_FOR_EACH_PTR(to); } } FOR_EACH_PTR(bb->insns, insn) { if (!insn->bb && verbose < 2) continue; printf("\t%s\n", show_instruction(insn)); } END_FOR_EACH_PTR(insn); if (!bb_terminated(bb)) printf("\tEND\n"); } /// // show BB of non-removed instruction void show_insn_bb(struct instruction *insn) { if (!insn || !insn->bb) return; show_bb(insn->bb); } 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"); } /// // show the function containing the instruction but only if not already removed. void show_insn_entry(struct instruction *insn) { if (!insn || !insn->bb || !insn->bb->ep) return; show_entry(insn->bb->ep); } 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 add_unreachable(struct entrypoint *ep) { struct instruction *insn = alloc_instruction(OP_UNREACH, 0); add_one_insn(ep, insn); ep->active = NULL; } 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); } 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; select = alloc_typed_instruction(OP_SEL, phi_node->type); 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); insert_last_instruction(bb, select); } 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, 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); } } 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 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 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; } pseudo_t undef_pseudo(void) { pseudo_t pseudo = __alloc_pseudo(0); pseudo->type = PSEUDO_UNDEF; 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; } struct instruction *alloc_phisrc(pseudo_t pseudo, struct symbol *type) { struct instruction *insn = alloc_typed_instruction(OP_PHISOURCE, type); pseudo_t phi = __alloc_pseudo(0); static int nr = 0; phi->type = PSEUDO_PHI; phi->nr = ++nr; phi->def = insn; use_pseudo(insn, pseudo, &insn->phi_src); insn->target = phi; return insn; } pseudo_t alloc_phi(struct basic_block *source, pseudo_t pseudo, struct symbol *type) { struct instruction *insn; if (!source) return VOID; insn = alloc_phisrc(pseudo, type); insn->bb = source; add_instruction(&source->insns, insn); return insn->target; } struct instruction *alloc_phi_node(struct basic_block *bb, struct symbol *type, struct ident *ident) { struct instruction *phi_node = alloc_typed_instruction(OP_PHI, type); pseudo_t phi; phi = alloc_pseudo(phi_node); phi->ident = ident; phi->def = phi_node; phi_node->target = phi; phi_node->bb = bb; return phi_node; } void add_phi_node(struct basic_block *bb, struct instruction *phi_node) { struct instruction *insn; FOR_EACH_PTR(bb->insns, insn) { enum opcode op = insn->opcode; if (op == OP_PHI) continue; INSERT_CURRENT(phi_node, insn); return; } END_FOR_EACH_PTR(insn); // FIXME add_instruction(&bb->insns, phi_node); } struct instruction *insert_phi_node(struct basic_block *bb, struct symbol *var) { struct instruction *phi_node = alloc_phi_node(bb, var, var->ident); add_phi_node(bb, phi_node); return phi_node; } /* * 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 *type; // ctype struct symbol *btype; // base type of bitfields pseudo_t address; // pseudo containing address .. long long offset; // byte offset }; 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 *bitfield_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) { base = base->ctype.base_type; if (sym->packed) { int size = bits_to_bytes(sym->bit_offset + sym->bit_size); sym = __alloc_symbol(0); *sym = *base; sym->bit_size = bytes_to_bits(size); return sym; } return base; } } 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->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; if (!ep->active) return VOID; insn = alloc_typed_instruction(OP_LOAD, ad->btype); new = alloc_pseudo(insn); insn->target = new; insn->offset = ad->offset; insn->is_volatile = ad->type && (ad->type->ctype.modifiers & MOD_VOLATILE); 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; struct instruction *store; if (!bb) return; store = alloc_typed_instruction(OP_STORE, ad->btype); store->offset = ad->offset; store->is_volatile = ad->type && (ad->type->ctype.modifiers & MOD_VOLATILE); use_pseudo(store, value, &store->target); use_pseudo(store, ad->address, &store->src); add_one_insn(ep, store); } static pseudo_t linearize_bitfield_insert(struct entrypoint *ep, pseudo_t ori, pseudo_t val, struct symbol *ctype, struct symbol *btype) { unsigned int shift = ctype->bit_offset; unsigned int size = ctype->bit_size; unsigned long long mask = ((1ULL << size) - 1); unsigned long long smask= bits_mask(btype->bit_size); val = add_cast(ep, btype, ctype, OP_ZEXT, val); if (shift) { val = add_binary_op(ep, btype, OP_SHL, val, value_pseudo(shift)); mask <<= shift; } ori = add_binary_op(ep, btype, OP_AND, ori, value_pseudo(~mask & smask)); val = add_binary_op(ep, btype, OP_OR, ori, val); return val; } static pseudo_t linearize_store_gen(struct entrypoint *ep, pseudo_t value, struct access_data *ad) { struct symbol *ctype = ad->type; struct symbol *btype; pseudo_t store = value; if (!ep->active) return VOID; btype = ad->btype = bitfield_base_type(ctype); if (type_size(btype) != type_size(ctype)) { pseudo_t orig = add_load(ep, ad); store = linearize_bitfield_insert(ep, orig, value, ctype, btype); } add_store(ep, ad, store); return value; } static void taint_undefined_behaviour(struct instruction *insn) { pseudo_t src2; switch (insn->opcode) { case OP_LSR: case OP_ASR: case OP_SHL: src2 = insn->src2; if (src2->type != PSEUDO_VAL) break; if ((unsigned long long)src2->value >= insn->size) insn->tainted = 1; break; } } 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_cmp_op(struct entrypoint *ep, struct symbol *ctype, int op, struct symbol *itype, pseudo_t left, pseudo_t right) { pseudo_t target = add_binary_op(ep, ctype, op, left, right); target->def->itype = itype; 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_setfval(struct entrypoint *ep, struct symbol *ctype, long double fval) { struct instruction *insn = alloc_typed_instruction(OP_SETFVAL, ctype); pseudo_t target = alloc_pseudo(insn); insn->target = target; insn->fvalue = fval; add_one_insn(ep, insn); return target; } static pseudo_t add_symbol_address(struct entrypoint *ep, struct expression *expr) { struct instruction *insn = alloc_typed_instruction(OP_SYMADDR, expr->ctype); pseudo_t target = alloc_pseudo(insn); insn->target = target; use_pseudo(insn, symbol_pseudo(ep, expr->symbol), &insn->src); add_one_insn(ep, insn); return target; } static pseudo_t linearize_bitfield_extract(struct entrypoint *ep, pseudo_t val, struct symbol *ctype, struct symbol *btype) { unsigned int off = ctype->bit_offset; if (off) { pseudo_t shift = value_pseudo(off); val = add_binary_op(ep, btype, OP_LSR, val, shift); } val = cast_pseudo(ep, val, btype, ctype); return val; } static pseudo_t linearize_load_gen(struct entrypoint *ep, struct access_data *ad) { struct symbol *ctype = ad->type; struct symbol *btype; pseudo_t new; if (!ep->active) return VOID; btype = ad->btype = bitfield_base_type(ctype); new = add_load(ep, ad); if (ctype->bit_size != type_size(btype)) new = linearize_bitfield_extract(ep, new, ctype, btype); 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); return value; } 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); op = opcode_float(op, expr->ctype); if (is_float_type(expr->ctype)) one = add_setfval(ep, expr->ctype, expr->op_value); else one = value_pseudo(expr->op_value); if (ad.btype != ad.type) old = cast_pseudo(ep, old, ad.type, ad.btype); new = add_binary_op(ep, ad.btype, op, old, one); if (ad.btype != ad.type) new = cast_pseudo(ep, new, ad.btype, ad.type); linearize_store_gen(ep, new, &ad); return postop ? old : new; } static pseudo_t add_unop(struct entrypoint *ep, struct symbol *ctype, int op, pseudo_t src) { struct instruction *insn = alloc_typed_instruction(op, 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 add_cast(struct entrypoint *ep, struct symbol *to, struct symbol *from, int op, pseudo_t src) { pseudo_t new = add_unop(ep, to, op, src); new->def->orig_type = from; 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->orig_type = expr->base->ctype; use_pseudo(insn, pre, &insn->src); 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); struct symbol *ctype = expr->ctype; switch (expr->op) { case '+': return pre; case '!': { pseudo_t zero = value_pseudo(0); return add_cmp_op(ep, ctype, OP_SET_EQ, expr->unop->ctype, pre, zero); } case '~': return add_unop(ep, ctype, OP_NOT, pre); case '-': return add_unop(ep, ctype, opcode_float(OP_NEG, ctype), 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. */ enum mtype { MTYPE_UINT, MTYPE_SINT, MTYPE_PTR, MTYPE_VPTR, // TODO: must be removed ? MTYPE_FLOAT, MTYPE_BAD, }; static enum mtype get_mtype(struct symbol *s) { int sign = (s->ctype.modifiers & MOD_SIGNED) ? 1 : 0; retry: switch (s->type) { case SYM_NODE: s = s->ctype.base_type; goto retry; case SYM_PTR: if (s->ctype.base_type == &void_ctype) return MTYPE_VPTR; return MTYPE_PTR; case SYM_BITFIELD: case SYM_RESTRICT: case SYM_FOULED: case SYM_ENUM: s = s->ctype.base_type; /* fall-through */ case_int: return sign ? MTYPE_SINT : MTYPE_UINT; case SYM_BASETYPE: if (s->ctype.base_type == &fp_type) return MTYPE_FLOAT; if (s->ctype.base_type == &int_type) goto case_int; /* fall-through */ default: return MTYPE_BAD; } } static int get_cast_opcode(struct symbol *dst, struct symbol *src) { enum mtype stype = get_mtype(src); enum mtype dtype = get_mtype(dst); switch (dtype) { case MTYPE_FLOAT: switch (stype) { case MTYPE_FLOAT: if (dst->bit_size == src->bit_size) return OP_NOP; return OP_FCVTF; case MTYPE_UINT: return OP_UCVTF; case MTYPE_SINT: return OP_SCVTF; default: return OP_BADOP; } case MTYPE_PTR: switch (stype) { case MTYPE_UINT: case MTYPE_SINT: return OP_UTPTR; case MTYPE_PTR: case MTYPE_VPTR: return OP_PTRCAST; default: return OP_BADOP; } case MTYPE_VPTR: switch (stype) { case MTYPE_PTR: case MTYPE_VPTR: case MTYPE_UINT: stype = MTYPE_UINT; /* fall through */ case MTYPE_SINT: break; default: return OP_BADOP; } /* fall through */ case MTYPE_UINT: case MTYPE_SINT: switch (stype) { case MTYPE_FLOAT: return dtype == MTYPE_UINT ? OP_FCVTU : OP_FCVTS; case MTYPE_PTR: return OP_PTRTU; case MTYPE_VPTR: case MTYPE_UINT: case MTYPE_SINT: if (dst->bit_size ==src->bit_size) return OP_NOP; if (dst->bit_size < src->bit_size) return OP_TRUNC; return stype == MTYPE_SINT ? OP_SEXT : OP_ZEXT; default: return OP_BADOP; } /* fall through */ default: if (src->type == SYM_NODE) src = src->ctype.base_type; if (dst->type == SYM_NODE) dst = dst->ctype.base_type; if (src == dst) return OP_NOP; return OP_BADOP; } } static pseudo_t cast_pseudo(struct entrypoint *ep, pseudo_t src, struct symbol *from, struct symbol *to) { const struct position pos = current_pos; pseudo_t result; struct instruction *insn; int opcode; if (src == VOID) return VOID; if (!from || !to) return VOID; if (from->bit_size < 0 || to->bit_size < 0) return VOID; opcode = get_cast_opcode(to, from); switch (opcode) { case OP_NOP: return src; case OP_UTPTR: if (from->bit_size == to->bit_size) break; if (src == value_pseudo(0)) break; if (Wint_to_pointer_cast) warning(pos, "non size-preserving integer to pointer cast"); src = cast_pseudo(ep, src, from, size_t_ctype); from = size_t_ctype; break; case OP_PTRTU: if (from->bit_size == to->bit_size) break; if (Wpointer_to_int_cast) warning(pos, "non size-preserving pointer to integer cast"); src = cast_pseudo(ep, src, from, size_t_ctype); return cast_pseudo(ep, src, size_t_ctype, to); case OP_BADOP: return VOID; default: break; } insn = alloc_typed_instruction(opcode, 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 map_opcode(int opcode, struct symbol *ctype) { if (ctype && is_float_type(ctype)) return opcode_table[opcode].to_float; if (ctype && (ctype->ctype.modifiers & MOD_SIGNED)) { switch(opcode) { 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 (!type || src == VOID) return VOID; if (is_bool_type(type)) return src; if (src->type == PSEUDO_VAL && (src->value == 0 || src->value == 1)) return src; if (is_float_type(type)) { zero = add_setfval(ep, type, 0.0); op = map_opcode(OP_SET_NE, type); } else { zero = value_pseudo(0); op = OP_SET_NE; } return add_cmp_op(ep, &bool_ctype, op, type, 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_MUL, [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 = map_opcode(op_trans[expr->op - SPECIAL_BASE], ctype); dst = add_binary_op(ep, ctype, opcode, oldvalue, value); taint_undefined_behaviour(dst->def); value = cast_pseudo(ep, dst, ctype, expr->ctype); } value = linearize_store_gen(ep, value, &ad); return value; } static pseudo_t linearize_call_expression(struct entrypoint *ep, struct expression *expr) { struct expression *arg, *fn; struct instruction *insn; pseudo_t retval, call; struct ctype *ctype = NULL; struct symbol *fntype; struct context *context; if (!expr->ctype) return VOID; fn = expr->fn; fntype = fn->ctype; // handle builtins if (fntype->op && fntype->op->linearize) { retval = fntype->op->linearize(ep, expr); if (retval) return retval; } ctype = &fntype->ctype; insn = alloc_typed_instruction(OP_CALL, expr->ctype); add_symbol(&insn->fntypes, fntype); FOR_EACH_PTR(expr->args, arg) { pseudo_t new = linearize_expression(ep, arg); use_pseudo(insn, new, add_pseudo(&insn->arguments, new)); add_symbol(&insn->fntypes, arg->ctype); } END_FOR_EACH_PTR(arg); if (fn->type == EXPR_PREOP && fn->op == '*' && is_func_type(fn->ctype)) 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); if (ctype->modifiers & MOD_NORETURN) add_unreachable(ep); } 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 : OP_AND; 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_MUL, ['/'] = 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 = map_opcode(opcode[expr->op], expr->ctype); dst = add_binary_op(ep, expr->ctype, op, src1, src2); taint_undefined_behaviour(dst->def); return dst; } static pseudo_t linearize_logical_branch(struct entrypoint *ep, struct expression *expr, struct basic_block *bb_true, struct basic_block *bb_false); static 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, valt, valf, res; struct instruction *insn; valt = linearize_expression(ep, expr->cond_true); valf = linearize_expression(ep, expr->cond_false); cond = linearize_expression(ep, expr->conditional); insn = alloc_typed_instruction(OP_SEL, expr->ctype); if (!expr->cond_true) valt = cond; use_pseudo(insn, cond, &insn->src1); use_pseudo(insn, valt, &insn->src2); use_pseudo(insn, valf, &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 == VOID) ? phi2 : phi2->def->src; if (phi2 == VOID) return (phi1 == VOID) ? phi1 : phi1->def->src; phi_node = alloc_typed_instruction(OP_PHI, expr->ctype); link_phi(phi_node, phi1); link_phi(phi_node, 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; pseudo_t phi1, phi2; if (!expr_false || !ep->active) return VOID; bb_false = alloc_basic_block(ep, expr_false->pos); merge = alloc_basic_block(ep, expr->pos); src1 = linearize_expression(ep, cond); phi1 = alloc_phi(ep->active, src1, expr->ctype); add_branch(ep, src1, merge, bb_false); set_activeblock(ep, bb_false); src2 = linearize_expression(ep, expr_false); phi2 = alloc_phi(ep->active, src2, expr->ctype); 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; 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, expr->ctype); add_goto(ep, merge); set_activeblock(ep, bb_false); src2 = linearize_expression(ep, expr_false); phi2 = alloc_phi(ep->active, src2, expr->ctype); set_activeblock(ep, merge); return add_join_conditional(ep, expr, phi1, phi2); } static void insert_phis(struct basic_block *bb, pseudo_t src, struct symbol *ctype, struct instruction *node) { struct basic_block *parent; FOR_EACH_PTR(bb->parents, parent) { struct instruction *phisrc = alloc_phisrc(src, ctype); insert_last_instruction(parent, phisrc); link_phi(node, phisrc->target); } END_FOR_EACH_PTR(parent); } static pseudo_t linearize_logical(struct entrypoint *ep, struct expression *expr) { struct symbol *ctype = expr->ctype; struct basic_block *other, *merge; struct instruction *node; pseudo_t src1, src2, phi2; if (!ep->active || !expr->left || !expr->right) return VOID; other = alloc_basic_block(ep, expr->right->pos); merge = alloc_basic_block(ep, expr->pos); node = alloc_phi_node(merge, ctype, NULL); // LHS and its shortcut if (expr->op == SPECIAL_LOGICAL_OR) { linearize_cond_branch(ep, expr->left, merge, other); src1 = value_pseudo(1); } else { linearize_cond_branch(ep, expr->left, other, merge); src1 = value_pseudo(0); } insert_phis(merge, src1, ctype, node); // RHS set_activeblock(ep, other); src2 = linearize_expression_to_bool(ep, expr->right); src2 = cast_pseudo(ep, src2, &bool_ctype, ctype); phi2 = alloc_phi(ep->active, src2, ctype); link_phi(node, phi2); // join set_activeblock(ep, merge); add_instruction(&merge->insns, node); return node->target; } 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, }; struct symbol *itype = expr->right->ctype; int op = opcode_float(cmpop[expr->op], itype); pseudo_t src1 = linearize_expression(ep, expr->left); pseudo_t src2 = linearize_expression(ep, expr->right); pseudo_t dst = add_cmp_op(ep, expr->ctype, op, itype, src1, src2); return dst; } static 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 || !valid_type(expr->ctype) || !bb_reachable(ep->active)) return VOID; switch (expr->type) { case EXPR_STRING: case EXPR_VALUE: add_goto(ep, expr->value ? bb_true : bb_false); break; case EXPR_FVALUE: add_goto(ep, expr->fvalue ? bb_true : bb_false); break; case EXPR_LOGICAL: linearize_logical_branch(ep, expr, bb_true, bb_false); break; case EXPR_COMPARE: cond = linearize_compare(ep, expr); add_branch(ep, 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_to_bool(ep, expr); add_branch(ep, cond, bb_true, bb_false); break; } 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_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: ad->offset = initializer->init_offset; linearize_initializer(ep, initializer->init_expr, ad); break; default: { pseudo_t value = linearize_expression(ep, initializer); ad->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.type = arg; ad.address = symbol_pseudo(ep, arg); linearize_store_gen(ep, argument_pseudo(ep, nr), &ad); } static pseudo_t linearize_expression(struct entrypoint *ep, struct expression *expr) { if (!expr || !valid_type(expr->ctype)) return VOID; current_pos = expr->pos; switch (expr->type) { case EXPR_SYMBOL: linearize_one_symbol(ep, expr->symbol); return add_symbol_address(ep, expr); case EXPR_VALUE: return value_pseudo(expr->value); case EXPR_STRING: case EXPR_LABEL: return add_setval(ep, expr->ctype, expr); case EXPR_FVALUE: return add_setfval(ep, expr->ctype, expr->fvalue); 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. ad.type = sym; ad.address = symbol_pseudo(ep, sym); linearize_store_gen(ep, value_pseudo(0), &ad); } value = linearize_initializer(ep, sym->initializer, &ad); return value; } static pseudo_t linearize_compound_statement(struct entrypoint *ep, struct statement *stmt) { pseudo_t pseudo; struct statement *s; pseudo = VOID; FOR_EACH_PTR(stmt->stmts, s) { pseudo = linearize_statement(ep, s); } END_FOR_EACH_PTR(s); return pseudo; } static void add_return(struct entrypoint *ep, struct basic_block *bb, struct symbol *ctype, pseudo_t src) { struct instruction *phi_node = first_instruction(bb->insns); pseudo_t phi; if (!phi_node) { phi_node = alloc_typed_instruction(OP_PHI, ctype); phi_node->target = alloc_pseudo(phi_node); phi_node->bb = bb; add_instruction(&bb->insns, phi_node); } phi = alloc_phi(ep->active, src, ctype); phi->ident = &return_ident; link_phi(phi_node, phi); } static pseudo_t linearize_fn_statement(struct entrypoint *ep, struct statement *stmt) { struct instruction *phi_node; struct basic_block *bb; pseudo_t pseudo; pseudo = linearize_compound_statement(ep, stmt); if (!is_void_type(stmt->ret)) { // non-void function struct basic_block *active = ep->active; if (active && !bb_terminated(active)) { // missing return struct basic_block *bb_ret; bb_ret = get_bound_block(ep, stmt->ret); add_return(ep, bb_ret, stmt->ret, undef_pseudo()); } } bb = add_label(ep, stmt->ret); phi_node = first_instruction(bb->insns); if (phi_node) pseudo = 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); add_pseudo(&insn->arguments, value); } END_FOR_EACH_PTR(sym); } pseudo = linearize_fn_statement(ep, stmt); insn->target = pseudo; insn->func = symbol_pseudo(ep, stmt->inline_fn); bb = ep->active; if (!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; insn->increment = get_expression_value(expr); 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_rule(struct instruction *insn, struct asm_constraint_list **list, struct asm_operand *op, pseudo_t pseudo) { struct asm_constraint *rule = __alloc_asm_constraint(0); rule->is_memory = op->is_memory; rule->ident = op->name; rule->constraint = op->constraint ? op->constraint->string->data : ""; use_pseudo(insn, pseudo, &rule->pseudo); add_ptr_list(list, rule); } static void add_asm_input(struct entrypoint *ep, struct instruction *insn, struct asm_operand *op) { pseudo_t pseudo = linearize_expression(ep, op->expr); add_asm_rule(insn, &insn->asm_rules->inputs, op, pseudo); } static void add_asm_output_address(struct entrypoint *ep, struct instruction *insn, struct asm_operand *op) { pseudo_t pseudo; if (!op->is_memory) return; pseudo = linearize_expression(ep, op->expr); add_asm_rule(insn, &insn->asm_rules->outputs, op, pseudo); insn->output_memory = 1; } static void add_asm_output(struct entrypoint *ep, struct instruction *insn, struct asm_operand *op) { struct access_data ad = { NULL, }; pseudo_t pseudo; if (op->is_memory) return; if (!linearize_address_gen(ep, op->expr, &ad)) return; pseudo = alloc_pseudo(insn); linearize_store_gen(ep, pseudo, &ad); add_asm_rule(insn, &insn->asm_rules->outputs, op, pseudo); } static pseudo_t linearize_asm_statement(struct entrypoint *ep, struct statement *stmt) { struct instruction *insn; struct expression *expr, *clob; struct asm_rules *rules; struct asm_operand *op; 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.. */ FOR_EACH_PTR(stmt->asm_inputs, op) { add_asm_input(ep, insn, op); } END_FOR_EACH_PTR(op); /* ... and the addresses for memory outputs */ FOR_EACH_PTR(stmt->asm_outputs, op) { add_asm_output_address(ep, insn, op); } END_FOR_EACH_PTR(op); add_one_insn(ep, insn); /* Assign the outputs */ FOR_EACH_PTR(stmt->asm_outputs, op) { add_asm_output(ep, insn, op); } END_FOR_EACH_PTR(op); /* and finally, look if it clobbers memory */ FOR_EACH_PTR(stmt->asm_clobbers, clob) { if (!strcmp(clob->string->data, "memory")) insn->clobber_memory = 1; } END_FOR_EACH_PTR(clob); 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 symbol *ret = stmt->ret_target; struct basic_block *bb_return = get_bound_block(ep, ret); struct basic_block *active; pseudo_t src = linearize_expression(ep, expr); active = ep->active; if (active && !is_void_type(ret)) { add_return(ep, bb_return, ret, src); } 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 expression *expr = stmt->switch_expression; struct multijmp *jmp; pseudo_t pseudo; if (!expr || !expr->ctype) return VOID; pseudo = linearize_expression(ep, expr); active = ep->active; if (!active) { active = alloc_basic_block(ep, stmt->pos); set_activeblock(ep, active); } switch_ins = alloc_typed_instruction(OP_SWITCH, expr->ctype); 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 if (case_stmt->case_expression->type != EXPR_VALUE) { continue; } else { struct expression *case_to = case_stmt->case_to; long long begin, end; begin = end = case_stmt->case_expression->value; if (case_to && case_to->type == EXPR_VALUE) end = 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; } static 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->src); 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); // If the condition is invalid, the following // statement(s) are not evaluated. if (!cond || !valid_type(cond->ctype)) return VOID; 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 void check_tainted_insn(struct instruction *insn) { unsigned long long uval; long long sval; pseudo_t src2; switch (insn->opcode) { case OP_DIVU: case OP_DIVS: case OP_MODU: case OP_MODS: if (insn->src2 == value_pseudo(0)) warning(insn->pos, "divide by zero"); break; case OP_SHL: case OP_LSR: case OP_ASR: src2 = insn->src2; if (src2->type != PSEUDO_VAL) break; uval = src2->value; if (uval < insn->size) break; sval = sign_extend(uval, insn->size); if (Wshift_count_negative && sval < 0) warning(insn->pos, "shift count is negative (%lld)", sval); else if (Wshift_count_overflow) warning(insn->pos, "shift too big (%llu) for type %s", uval, show_typename(insn->type)); } } /// // issue warnings after all possible DCE static void late_warnings(struct entrypoint *ep) { struct basic_block *bb; FOR_EACH_PTR(ep->bbs, bb) { struct instruction *insn; FOR_EACH_PTR(bb->insns, insn) { if (!insn->bb) continue; if (insn->tainted) check_tainted_insn(insn); switch (insn->opcode) { case OP_LOAD: // Check for illegal offsets. check_access(insn); break; } } END_FOR_EACH_PTR(insn); } END_FOR_EACH_PTR(bb); } static struct entrypoint *linearize_fn(struct symbol *sym, struct symbol *base_type) { struct statement *stmt = base_type->stmt; struct entrypoint *ep; struct basic_block *bb; struct symbol *ret_type; struct symbol *arg; struct instruction *entry; struct instruction *ret; pseudo_t result; int i; if (!stmt || sym->bogus_linear) return NULL; ep = alloc_entrypoint(); ep->name = sym; sym->ep = ep; bb = alloc_basic_block(ep, sym->pos); set_activeblock(ep, bb); if (stmt->type == STMT_ASM) { // top-level asm linearize_asm_statement(ep, stmt); return ep; } 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_fn_statement(ep, stmt); ret_type = base_type->ctype.base_type; ret = alloc_typed_instruction(OP_RET, ret_type); if (type_size(ret_type) > 0) use_pseudo(ret, result, &ret->src); add_one_insn(ep, ret); optimize(ep); late_warnings(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; } /* * Builtin functions */ static pseudo_t linearize_fma(struct entrypoint *ep, struct expression *expr) { struct instruction *insn = alloc_typed_instruction(OP_FMADD, expr->ctype); struct expression *arg; PREPARE_PTR_LIST(expr->args, arg); use_pseudo(insn, linearize_expression(ep, arg), &insn->src1); NEXT_PTR_LIST(arg) use_pseudo(insn, linearize_expression(ep, arg), &insn->src2); NEXT_PTR_LIST(arg) use_pseudo(insn, linearize_expression(ep, arg), &insn->src3); FINISH_PTR_LIST(arg); add_one_insn(ep, insn); return insn->target = alloc_pseudo(insn); } static pseudo_t linearize_isdigit(struct entrypoint *ep, struct expression *expr) { struct instruction *insn; pseudo_t src; insn = alloc_typed_instruction(OP_SUB, &int_ctype); src = linearize_expression(ep, first_expression(expr->args)); use_pseudo(insn, src, &insn->src1); insn->src2 = value_pseudo('0'); src = insn->target = alloc_pseudo(insn); add_one_insn(ep, insn); insn = alloc_typed_instruction(OP_SET_BE, &int_ctype); use_pseudo(insn, src, &insn->src1); insn->src2 = value_pseudo(9); insn->target = alloc_pseudo(insn); insn->itype = &int_ctype; add_one_insn(ep, insn); return insn->target; } static pseudo_t linearize_unreachable(struct entrypoint *ep, struct expression *exp) { add_unreachable(ep); return VOID; } static struct sym_init { const char *name; pseudo_t (*linearize)(struct entrypoint *, struct expression*); struct symbol_op op; } builtins_table[] = { // must be declared in builtin.c:declare_builtins[] { "__builtin_fma", linearize_fma }, { "__builtin_fmaf", linearize_fma }, { "__builtin_fmal", linearize_fma }, { "__builtin_isdigit", linearize_isdigit }, { "__builtin_unreachable", linearize_unreachable }, { } }; void init_linearized_builtins(int stream) { struct sym_init *ptr; for (ptr = builtins_table; ptr->name; ptr++) { struct symbol *sym; sym = create_symbol(stream, ptr->name, SYM_NODE, NS_SYMBOL); if (!sym->op) sym->op = &ptr->op; sym->op->type |= KW_BUILTIN; sym->op->linearize = ptr->linearize; } } sparse-0.6.4/linearize.h000066400000000000000000000213051411531012200151400ustar00rootroot00000000000000#ifndef LINEARIZE_H #define LINEARIZE_H #include "lib.h" #include "allocate.h" #include "token.h" #include "opcode.h" #include "parse.h" #include "symbol.h" #include "ptrmap.h" struct instruction; struct pseudo_user { struct instruction *insn; pseudo_t *userp; }; DECLARE_ALLOCATOR(pseudo_user); DECLARE_PTR_LIST(pseudo_user_list, struct pseudo_user); DECLARE_PTRMAP(phi_map, struct symbol *, struct instruction *); enum pseudo_type { PSEUDO_VOID, PSEUDO_UNDEF, PSEUDO_PHI, PSEUDO_REG, PSEUDO_ARG, PSEUDO_SYM, PSEUDO_VAL, }; 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) static inline bool is_zero(pseudo_t pseudo) { return pseudo->type == PSEUDO_VAL && pseudo->value == 0; } static inline bool is_nonzero(pseudo_t pseudo) { return pseudo->type == PSEUDO_VAL && pseudo->value != 0; } static inline bool is_positive(pseudo_t pseudo, unsigned size) { return pseudo->type == PSEUDO_VAL && !(pseudo->value & sign_bit(size)); } struct multijmp { struct basic_block *target; long long begin, end; }; struct asm_constraint { pseudo_t pseudo; const char *constraint; const struct ident *ident; unsigned int is_memory:1; }; 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:7, tainted:1, size:24; struct basic_block *bb; struct position pos; struct symbol *type; pseudo_t target; union { struct /* entrypoint */ { struct pseudo_list *arg_list; }; struct /* branch */ { pseudo_t cond; struct basic_block *bb_true, *bb_false; }; struct /* switch */ { pseudo_t _cond; struct multijmp_list *multijmp_list; }; struct /* phi_node */ { pseudo_t phi_var; // used for SSA conversion struct pseudo_list *phi_list; unsigned int used:1; }; struct /* phi source */ { pseudo_t phi_src; struct instruction *phi_node; }; struct /* unops */ { pseudo_t src; unsigned from; /* slice */ struct symbol *orig_type; /* casts */ }; struct /* memops */ { pseudo_t addr; /* alias .src */ long long offset; unsigned int is_volatile:1; }; struct /* binops and sel */ { pseudo_t src1, src2, src3; }; struct /* compare */ { pseudo_t _src1, _src2; // alias .src[12] struct symbol *itype; // input operands' type }; struct /* setval */ { struct expression *val; }; struct /* setfval */ { long double fvalue; }; struct /* call */ { pseudo_t func; struct pseudo_list *arguments; struct symbol_list *fntypes; }; struct /* context */ { int increment; int check; struct expression *context_expr; }; struct /* asm */ { const char *string; struct asm_rules *asm_rules; unsigned int clobber_memory:1; unsigned int output_memory:1; }; }; }; struct basic_block_list; struct instruction_list; struct basic_block { struct position pos; unsigned long generation; struct entrypoint *ep; struct basic_block_list *parents; /* sources */ struct basic_block_list *children; /* destinations */ struct instruction_list *insns; /* Linear list of instructions */ struct basic_block *idom; /* link to the immediate dominator */ unsigned int nr; /* unique id for label's names */ int dom_level; /* level in the dominance tree */ struct basic_block_list *doms; /* list of BB idominated by this one */ struct pseudo_list *needs, *defines; union { struct phi_map *phi_map;/* needed during SSA conversion */ int postorder_nr; /* postorder number */ int context; /* needed during context checking */ void *priv; }; }; // // return the opcode of the instruction defining ``SRC`` if existing // and OP_BADOP if not. It also assigns the defining instruction // to ``DEF``. #define DEF_OPCODE(DEF, SRC) \ (((SRC)->type == PSEUDO_REG && (DEF = (SRC)->def)) ? DEF->opcode : OP_BADOP) 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 insert_last_instruction(struct basic_block *bb, struct instruction *insn) { struct instruction *last = delete_last_instruction(&bb->insns); add_instruction(&bb->insns, insn); add_instruction(&bb->insns, last); insn->bb = bb; } 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 pseudo_in_list(struct pseudo_list *list, pseudo_t pseudo) { return lookup_ptr_list_entry((struct ptr_list *)list, pseudo); } 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 int lookup_bb(struct basic_block_list *list, struct basic_block *bb) { return lookup_ptr_list_entry((struct ptr_list *)list, bb); } 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_UNDEF && p->type != PSEUDO_VAL); } static inline bool has_definition(pseudo_t p) { return p->type == PSEUDO_REG || p->type == PSEUDO_PHI; } static inline int pseudo_user_list_size(struct pseudo_user_list *list) { return ptr_list_size((struct ptr_list *)list); } static inline bool pseudo_user_list_empty(struct pseudo_user_list *list) { return ptr_list_empty((struct ptr_list *)list); } static inline int has_users(pseudo_t p) { return !pseudo_user_list_empty(p->users); } static inline bool one_use(pseudo_t p) { return !ptr_list_multiple((struct ptr_list *)(p->users)); } static inline int nbr_users(pseudo_t p) { return pseudo_user_list_size(p->users); } 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 link_phi(struct instruction *node, pseudo_t phi) { use_pseudo(node, phi, add_pseudo(&node->phi_list, phi)); phi->def->phi_node = node; } 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; unsigned int dom_levels; /* max levels in the dom tree */ }; extern void insert_select(struct basic_block *bb, struct instruction *br, struct instruction *phi, pseudo_t if_true, pseudo_t if_false); struct instruction *alloc_phisrc(pseudo_t pseudo, struct symbol *type); struct instruction *alloc_phi_node(struct basic_block *bb, struct symbol *type, struct ident *ident); struct instruction *insert_phi_node(struct basic_block *bb, struct symbol *var); void add_phi_node(struct basic_block *bb, struct instruction *phi_node); pseudo_t alloc_phi(struct basic_block *source, pseudo_t pseudo, struct symbol *type); pseudo_t alloc_pseudo(struct instruction *def); pseudo_t value_pseudo(long long val); pseudo_t undef_pseudo(void); struct entrypoint *linearize_symbol(struct symbol *sym); int unssa(struct entrypoint *ep); void show_entry(struct entrypoint *ep); void show_insn_entry(struct instruction *insn); const char *show_pseudo(pseudo_t pseudo); void show_bb(struct basic_block *bb); void show_insn_bb(struct instruction *insn); const char *show_instruction(struct instruction *insn); const char *show_label(struct basic_block *bb); #endif /* LINEARIZE_H */ sparse-0.6.4/liveness.c000066400000000000000000000161601411531012200150040ustar00rootroot00000000000000/* * Register - track pseudo usage, maybe eventually try to do register * allocation. * * Copyright (C) 2004 Linus Torvalds */ #include #include "liveness.h" #include "parse.h" #include "expression.h" #include "linearize.h" #include "flow.h" static void phi_defines(struct instruction * phi_node, pseudo_t target, void (*defines)(struct basic_block *, 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) { if (entry->is_memory) use(bb, entry->pseudo); else 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: case OP_COMPUTEDGOTO: USES(src); break; case OP_CBR: case OP_SWITCH: USES(cond); break; /* Binary */ case OP_BINARY ... OP_BINARY_END: case OP_FPCMP ... OP_FPCMP_END: case OP_BINCMP ... OP_BINCMP_END: USES(src1); USES(src2); DEFINES(target); break; /* Uni */ case OP_UNOP ... OP_UNOP_END: case OP_SYMADDR: case OP_SLICE: USES(src); 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_LABEL: case OP_SETVAL: case OP_SETFVAL: 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_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_ASM: asm_liveness(bb, insn, def, use); break; case OP_RANGE: USES(src1); USES(src2); USES(src3); break; case OP_BADOP: case OP_NOP: case OP_CONTEXT: break; } } 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); } static 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); } 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.6.4/liveness.h000066400000000000000000000003431411531012200150050ustar00rootroot00000000000000#ifndef LIVENESS_H #define LIVENESS_H struct entrypoint; /* liveness.c */ void clear_liveness(struct entrypoint *ep); void track_pseudo_liveness(struct entrypoint *ep); void track_pseudo_death(struct entrypoint *ep); #endif sparse-0.6.4/machine.h000066400000000000000000000055671411531012200145760ustar00rootroot00000000000000#ifndef MACHINE_H #define MACHINE_H #if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) #define ARCH_BIG_ENDIAN 1 #else #define ARCH_BIG_ENDIAN 0 #endif enum bitness { ARCH_LP32, ARCH_X32, ARCH_LP64, ARCH_LLP64, }; #ifdef __LP64__ #define ARCH_M64_DEFAULT ARCH_LP64 #elif defined(__x86_64__) || defined(__x86_64) #define ARCH_M64_DEFAULT ARCH_X32 #else #define ARCH_M64_DEFAULT ARCH_LP32 #endif enum machine { MACH_ARM, MACH_ARM64, MACH_I386, MACH_X86_64, MACH_MIPS32, MACH_MIPS64, MACH_PPC32, MACH_PPC64, MACH_RISCV32, MACH_RISCV64, MACH_SPARC32, MACH_SPARC64, MACH_S390, MACH_S390X, MACH_ALPHA, MACH_BFIN, MACH_H8300, MACH_M68K, MACH_MICROBLAZE, MACH_NDS32, MACH_NIOS2, MACH_OPENRISC, MACH_SH, MACH_XTENSA, MACH_UNKNOWN }; #if defined(__aarch64__) #define MACH_NATIVE MACH_ARM64 #elif defined(__alpha__) || defined(__alpha) #define MACH_NATIVE MACH_ALPHA #elif defined(__arm__) #define MACH_NATIVE MACH_ARM #elif defined(__x86_64__) || defined(__x86_64) #define MACH_NATIVE MACH_X86_64 #elif defined(__i386__) || defined(__i386) #define MACH_NATIVE MACH_I386 #elif defined(__mips64__) || (defined(__mips) && __mips == 64) #define MACH_NATIVE MACH_MIPS64 #elif defined(__mips__) || defined(__mips) #define MACH_NATIVE MACH_MIPS32 #elif defined(__powerpc64__) || defined(__ppc64__) #define MACH_NATIVE MACH_PPC64 #elif defined(__powerpc__) || defined(__powerpc) || defined(__ppc__) #define MACH_NATIVE MACH_PPC32 #elif defined(__riscv) && (__riscv_xlen == 64) #define MACH_NATIVE MACH_RISCV64 #elif defined(__riscv) && (__riscv_xlen == 32) #define MACH_NATIVE MACH_RISCV32 #elif defined(__sparc_v9__) || defined(__sparcv9) #define MACH_NATIVE MACH_SPARC64 #elif defined(__sparc__) || defined(__sparc) #define MACH_NATIVE MACH_SPARC32 #elif defined(__m68k__) #define MACH_NATIVE MACH_M68K #elif defined(__s390x__) || defined(__zarch__) #define MACH_NATIVE MACH_S390X #elif defined(__s390__) #define MACH_NATIVE MACH_S390 #else #define MACH_NATIVE MACH_UNKNOWN #endif enum fp_abi { FP_ABI_HARD, FP_ABI_SOFT, FP_ABI_HYBRID, }; #if defined(__ARM_PCS_VFP) #define FP_ABI_NATIVE FP_ABI_HARD #elif defined(__ARM_PCS) && !defined(__SOFTFP__) #define FP_ABI_NATIVE FP_ABI_HYBRID #else #define FP_ABI_NATIVE FP_ABI_SOFT #endif enum { OS_UNKNOWN, OS_NONE, OS_UNIX, OS_CYGWIN, OS_DARWIN, OS_FREEBSD, OS_LINUX, OS_NETBSD, OS_OPENBSD, OS_SUNOS, }; #if defined(__CYGWIN__) #define OS_NATIVE OS_CYGWIN #elif defined(__APPLE__) #define OS_NATIVE OS_DARWIN #elif defined(__FreeBSD__) #define OS_NATIVE OS_FREEBSD #elif defined(__linux__) || defined(__linux) #define OS_NATIVE OS_LINUX #elif defined(__NetBSD__) #define OS_NATIVE OS_NETBSD #elif defined(__OpenBSD__) #define OS_NATIVE OS_OPENBSD #elif defined(__sun__) || defined(__sun) #define OS_NATIVE OS_SUNOS #elif defined(__unix__) || defined(__unix) #define OS_NATIVE OS_UNIX #else #define OS_NATIVE OS_UNKNOWN #endif #endif sparse-0.6.4/memops.c000066400000000000000000000153101411531012200144500ustar00rootroot00000000000000/* * 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 "simplify.h" #include "flow.h" static void rewrite_load_instruction(struct instruction *insn, struct pseudo_list *dominators) { pseudo_t new = NULL; pseudo_t phi; /* * Check for somewhat common case of duplicate * phi nodes. */ FOR_EACH_PTR(dominators, phi) { if (!new) new = phi->def->phi_src; else if (new != phi->def->phi_src) goto complex_phi; } 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. */ replace_with_pseudo(insn, new); FOR_EACH_PTR(dominators, phi) { kill_instruction(phi->def); } END_FOR_EACH_PTR(phi); goto end; complex_phi: kill_use(&insn->src); insn->opcode = OP_PHI; insn->phi_list = dominators; end: repeat_phase |= REPEAT_CSE; } static int find_dominating_parents(struct instruction *insn, struct basic_block *bb, struct pseudo_list **dominators, int local) { struct basic_block *parent; FOR_EACH_PTR(bb->parents, parent) { struct instruction *phisrc; struct instruction *one; pseudo_t phi; FOR_EACH_PTR_REVERSE(parent->insns, one) { int dominance; if (!one->bb) continue; if (one == insn) goto no_dominance; dominance = dominates(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 == bb->generation) continue; parent->generation = bb->generation; if (!find_dominating_parents(insn, parent, dominators, local)) return 0; continue; found_dominator: phisrc = alloc_phisrc(one->target, one->type); phisrc->phi_node = insn; insert_last_instruction(parent, phisrc); phi = phisrc->target; phi->ident = phi->ident ? : one->target->ident; 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; if (pu->userp != &insn->src) 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 bool compatible_loads(struct instruction *a, struct instruction *b) { if (is_integral_type(a->type) && is_float_type(b->type)) return false; if (is_float_type(a->type) && is_integral_type(b->type)) return false; return true; } 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; if (insn->is_volatile) continue; if (!has_users(insn->target)) { kill_instruction(insn); continue; } RECURSE_PTR_REVERSE(insn, dom) { int dominance; if (!dom->bb) continue; dominance = dominates(insn, dom, local); if (dominance) { /* possible partial dominance? */ if (dominance < 0) { if (dom->opcode == OP_LOAD) continue; goto next_load; } if (!compatible_loads(insn, dom)) goto next_load; /* Yeehaa! Found one! */ replace_with_pseudo(insn, dom->target); goto next_load; } } END_FOR_EACH_PTR_REVERSE(dom); /* OK, go find the parents */ bb->generation = ++bb_generation; dominators = NULL; if (find_dominating_parents(insn, bb, &dominators, local)) { /* This happens with initial assignments to structures etc.. */ if (!dominators) { if (local) { assert(pseudo->type != PSEUDO_ARG); replace_with_pseudo(insn, value_pseudo(0)); } goto next_load; } rewrite_load_instruction(insn, dominators); } else { // cleanup pending phi-sources int repeat = repeat_phase; pseudo_t phi; FOR_EACH_PTR(dominators, phi) { kill_instruction(phi->def); } END_FOR_EACH_PTR(phi); repeat_phase = repeat; } } next_load: /* Do the next one */; } END_FOR_EACH_PTR_REVERSE(insn); } static bool try_to_kill_store(struct instruction *insn, struct instruction *dom, int local) { int dominance = dominates(insn, dom, local); if (dominance) { /* possible partial dominance? */ if (dominance < 0) return false; if (insn->target == dom->target && insn->bb == dom->bb) { // found a memop which makes the store redundant kill_instruction_force(insn); return false; } if (dom->opcode == OP_LOAD) return false; if (dom->is_volatile) return false; /* Yeehaa! Found one! */ kill_instruction_force(dom); } return true; } 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 basic_block *par; struct instruction *dom; pseudo_t pseudo = insn->src; int local; if (!insn->type) continue; if (insn->is_volatile) continue; local = local_pseudo(pseudo); RECURSE_PTR_REVERSE(insn, dom) { if (!dom->bb) continue; if (!try_to_kill_store(insn, dom, local)) goto next_store; } END_FOR_EACH_PTR_REVERSE(dom); /* OK, we should check the parents now */ FOR_EACH_PTR(bb->parents, par) { if (bb_list_size(par->children) != 1) goto next_parent; FOR_EACH_PTR(par->insns, dom) { if (!dom->bb) continue; if (dom == insn) goto next_parent; if (!try_to_kill_store(insn, dom, local)) goto next_parent; } END_FOR_EACH_PTR(dom); next_parent: ; } END_FOR_EACH_PTR(par); } next_store: /* Do the next one */; } END_FOR_EACH_PTR_REVERSE(insn); } void simplify_memops(struct entrypoint *ep) { struct basic_block *bb; pseudo_t pseudo; 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); FOR_EACH_PTR(ep->accesses, pseudo) { struct symbol *var = pseudo->sym; unsigned long mod; if (!var) continue; mod = var->ctype.modifiers; if (mod & (MOD_VOLATILE | MOD_NONLOCAL | MOD_STATIC)) continue; kill_dead_stores(ep, pseudo, local_pseudo(pseudo)); } END_FOR_EACH_PTR(pseudo); } sparse-0.6.4/obfuscate.c000066400000000000000000000042431411531012200151260ustar00rootroot00000000000000/* * 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(filelist, file) { emit_symbol_list(sparse(file)); } END_FOR_EACH_PTR(file); return 0; } sparse-0.6.4/opcode.c000066400000000000000000000027171411531012200144300ustar00rootroot00000000000000/* * Copyright (C) 2017 Luc Van Oostenryck * * 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 "opcode.h" const struct opcode_table opcode_table[OP_LAST] = { #define OPCODE(OP,NG,SW,SG,TF,N,FL) \ [OP_##OP] = { \ .negate = OP_##NG, \ .swap = OP_##SW, \ .sign = OP_##SG, \ .to_float = OP_##TF, \ .arity = N, \ .flags = FL, \ }, #define OPCODE_RANGE(OP,S,E) #include "opcode.def" #undef OPCODE #undef OPCODE_RANGE }; sparse-0.6.4/opcode.def000066400000000000000000000153711411531012200147440ustar00rootroot00000000000000// OPCODE negated swaped sign float arity, flags OPCODE(BADOP, BADOP, BADOP, BADOP, BADOP, 0, OPF_NONE) /* Entry */ OPCODE(ENTRY, BADOP, BADOP, BADOP, BADOP, 0, OPF_NONE) /* Terminator */ OPCODE(RET, BADOP, BADOP, BADOP, BADOP, 1, OPF_NONE) OPCODE(BR, BADOP, BADOP, BADOP, BADOP, 0, OPF_NONE) OPCODE(CBR, BADOP, BADOP, BADOP, BADOP, 1, OPF_NONE) OPCODE(SWITCH, BADOP, BADOP, BADOP, BADOP, 1, OPF_NONE) OPCODE(UNREACH, BADOP, BADOP, BADOP, BADOP, 0, OPF_NONE) OPCODE(COMPUTEDGOTO, BADOP, BADOP, BADOP, BADOP, 1, OPF_NONE) OPCODE_RANGE(TERMINATOR, RET, COMPUTEDGOTO) /* Binary */ OPCODE(ADD, BADOP, BADOP, BADOP, FADD, 2, OPF_TARGET|OPF_COMMU|OPF_ASSOC|OPF_BINOP) OPCODE(MUL, BADOP, BADOP, BADOP, FMUL, 2, OPF_TARGET|OPF_COMMU|OPF_ASSOC|OPF_BINOP) OPCODE(SUB, BADOP, BADOP, BADOP, FSUB, 2, OPF_TARGET|OPF_BINOP) OPCODE(DIVU, BADOP, BADOP, DIVS, FDIV, 2, OPF_TARGET|OPF_BINOP) OPCODE(DIVS, BADOP, BADOP, DIVU, FDIV, 2, OPF_TARGET|OPF_BINOP) OPCODE(MODU, BADOP, BADOP, MODS, BADOP, 2, OPF_TARGET|OPF_BINOP) OPCODE(MODS, BADOP, BADOP, MODU, BADOP, 2, OPF_TARGET|OPF_BINOP) OPCODE(LSR, BADOP, BADOP, ASR, BADOP, 2, OPF_TARGET|OPF_BINOP) OPCODE(ASR, BADOP, BADOP, LSR, BADOP, 2, OPF_TARGET|OPF_BINOP) OPCODE(SHL, BADOP, BADOP, BADOP, BADOP, 2, OPF_TARGET|OPF_BINOP) /* Floating-point binops */ OPCODE(FADD, BADOP, BADOP, BADOP, BADOP, 2, OPF_TARGET) OPCODE(FSUB, BADOP, BADOP, BADOP, BADOP, 2, OPF_TARGET) OPCODE(FMUL, BADOP, BADOP, BADOP, BADOP, 2, OPF_TARGET) OPCODE(FDIV, BADOP, BADOP, BADOP, BADOP, 2, OPF_TARGET) /* Logical */ OPCODE(AND, BADOP, BADOP, BADOP, BADOP, 2, OPF_TARGET|OPF_COMMU|OPF_ASSOC|OPF_BINOP) OPCODE(OR, BADOP, BADOP, BADOP, BADOP, 2, OPF_TARGET|OPF_COMMU|OPF_ASSOC|OPF_BINOP) OPCODE(XOR, BADOP, BADOP, BADOP, BADOP, 2, OPF_TARGET|OPF_COMMU|OPF_ASSOC|OPF_BINOP) OPCODE_RANGE(BINARY, ADD, XOR) /* floating-point comparison */ OPCODE(FCMP_ORD, FCMP_UNO, FCMP_ORD, BADOP, BADOP, 2, OPF_TARGET) OPCODE(FCMP_OEQ, FCMP_UNE, FCMP_OEQ, BADOP, BADOP, 2, OPF_TARGET) OPCODE(FCMP_ONE, FCMP_UEQ, FCMP_ONE, BADOP, BADOP, 2, OPF_TARGET) OPCODE(FCMP_UEQ, FCMP_ONE, FCMP_UEQ, BADOP, BADOP, 2, OPF_TARGET) OPCODE(FCMP_UNE, FCMP_OEQ, FCMP_UNE, BADOP, BADOP, 2, OPF_TARGET) OPCODE(FCMP_OLT, FCMP_UGE, FCMP_OGT, BADOP, BADOP, 2, OPF_TARGET) OPCODE(FCMP_OLE, FCMP_UGT, FCMP_OGE, BADOP, BADOP, 2, OPF_TARGET) OPCODE(FCMP_OGE, FCMP_ULT, FCMP_OLE, BADOP, BADOP, 2, OPF_TARGET) OPCODE(FCMP_OGT, FCMP_ULE, FCMP_OLT, BADOP, BADOP, 2, OPF_TARGET) OPCODE(FCMP_ULT, FCMP_OGE, FCMP_UGT, BADOP, BADOP, 2, OPF_TARGET) OPCODE(FCMP_ULE, FCMP_OGT, FCMP_UGE, BADOP, BADOP, 2, OPF_TARGET) OPCODE(FCMP_UGE, FCMP_OLT, FCMP_ULE, BADOP, BADOP, 2, OPF_TARGET) OPCODE(FCMP_UGT, FCMP_OLE, FCMP_ULT, BADOP, BADOP, 2, OPF_TARGET) OPCODE(FCMP_UNO, FCMP_ORD, FCMP_UNO, BADOP, BADOP, 2, OPF_TARGET) OPCODE_RANGE(FPCMP, FCMP_ORD, FCMP_UNO) /* Binary comparison */ OPCODE(SET_EQ, SET_NE, SET_EQ, SET_EQ, FCMP_OEQ, 2, OPF_TARGET|OPF_BINOP|OPF_COMPARE|OPF_COMMU) OPCODE(SET_LT, SET_GE, SET_GT, SET_B, FCMP_OLT, 2, OPF_TARGET|OPF_BINOP|OPF_COMPARE|OPF_SIGNED) OPCODE(SET_LE, SET_GT, SET_GE, SET_BE, FCMP_OLE, 2, OPF_TARGET|OPF_BINOP|OPF_COMPARE|OPF_SIGNED) OPCODE(SET_GE, SET_LT, SET_LE, SET_AE, FCMP_OGE, 2, OPF_TARGET|OPF_BINOP|OPF_COMPARE|OPF_SIGNED) OPCODE(SET_GT, SET_LE, SET_LT, SET_A, FCMP_OGT, 2, OPF_TARGET|OPF_BINOP|OPF_COMPARE|OPF_SIGNED) OPCODE(SET_B, SET_AE, SET_A, SET_LT, FCMP_OLT, 2, OPF_TARGET|OPF_BINOP|OPF_COMPARE|OPF_UNSIGNED) OPCODE(SET_BE, SET_A, SET_AE, SET_LE, FCMP_OLE, 2, OPF_TARGET|OPF_BINOP|OPF_COMPARE|OPF_UNSIGNED) OPCODE(SET_AE, SET_B, SET_BE, SET_GE, FCMP_OGE, 2, OPF_TARGET|OPF_BINOP|OPF_COMPARE|OPF_UNSIGNED) OPCODE(SET_A, SET_BE, SET_B, SET_GT, FCMP_OGT, 2, OPF_TARGET|OPF_BINOP|OPF_COMPARE|OPF_UNSIGNED) OPCODE(SET_NE, SET_EQ, SET_NE, SET_NE, FCMP_UNE, 2, OPF_TARGET|OPF_BINOP|OPF_COMPARE|OPF_COMMU) OPCODE_RANGE(BINCMP, SET_EQ, SET_NE) /* Uni */ OPCODE(NOT, BADOP, BADOP, BADOP, BADOP, 1, OPF_TARGET|OPF_UNOP) OPCODE(NEG, BADOP, BADOP, BADOP, FNEG, 1, OPF_TARGET|OPF_UNOP) OPCODE(FNEG, BADOP, BADOP, BADOP, BADOP, 1, OPF_TARGET) OPCODE(TRUNC, BADOP, BADOP, BADOP, BADOP, 1, OPF_TARGET) OPCODE(ZEXT, BADOP, BADOP, SEXT, BADOP, 1, OPF_TARGET) OPCODE(SEXT, BADOP, BADOP, ZEXT, BADOP, 1, OPF_TARGET) OPCODE(FCVTU, BADOP, BADOP, FCVTS, BADOP, 1, OPF_TARGET) OPCODE(FCVTS, BADOP, BADOP, FCVTU, BADOP, 1, OPF_TARGET) OPCODE(UCVTF, BADOP, BADOP, SCVTF, BADOP, 1, OPF_TARGET) OPCODE(SCVTF, BADOP, BADOP, UCVTF, BADOP, 1, OPF_TARGET) OPCODE(FCVTF, BADOP, BADOP, BADOP, BADOP, 1, OPF_TARGET) OPCODE(UTPTR, BADOP, BADOP, BADOP, BADOP, 1, OPF_TARGET) OPCODE(PTRTU, BADOP, BADOP, BADOP, BADOP, 1, OPF_TARGET) OPCODE(PTRCAST, BADOP, BADOP, BADOP, BADOP, 1, OPF_TARGET) OPCODE_RANGE(UNOP, NOT, PTRCAST) OPCODE(SYMADDR, BADOP, BADOP, BADOP, BADOP, 1, OPF_TARGET) OPCODE(SLICE, BADOP, BADOP, BADOP, BADOP, 1, OPF_TARGET) /* Select - three input values */ OPCODE(SEL, BADOP, BADOP, BADOP, BADOP, 3, OPF_TARGET) OPCODE(FMADD, BADOP, BADOP, BADOP, BADOP, 3, OPF_TARGET) /* Memory */ OPCODE(LOAD, BADOP, BADOP, BADOP, BADOP, 1, OPF_TARGET) OPCODE(STORE, BADOP, BADOP, BADOP, BADOP, 1, OPF_NONE) /* Other */ OPCODE(PHISOURCE, BADOP, BADOP, BADOP, BADOP, 1, OPF_TARGET) OPCODE(PHI, BADOP, BADOP, BADOP, BADOP, 0, OPF_TARGET) OPCODE(LABEL, BADOP, BADOP, BADOP, BADOP, 0, OPF_TARGET) OPCODE(SETVAL, BADOP, BADOP, BADOP, BADOP, 0, OPF_TARGET) OPCODE(SETFVAL, BADOP, BADOP, BADOP, BADOP, 0, OPF_TARGET) OPCODE(CALL, BADOP, BADOP, BADOP, BADOP, 1, OPF_TARGET) OPCODE(INLINED_CALL, BADOP, BADOP, BADOP, BADOP, 0, OPF_NONE) OPCODE(NOP, BADOP, BADOP, BADOP, BADOP, 0, OPF_NONE) OPCODE(DEATHNOTE, BADOP, BADOP, BADOP, BADOP, 0, OPF_NONE) OPCODE(ASM, BADOP, BADOP, BADOP, BADOP, 0, OPF_NONE) /* Sparse tagging (line numbers, context, whatever) */ OPCODE(CONTEXT, BADOP, BADOP, BADOP, BADOP, 0, OPF_NONE) OPCODE(RANGE, BADOP, BADOP, BADOP, BADOP, 3, OPF_NONE) /* Needed to translate SSA back to normal form */ OPCODE(COPY, BADOP, BADOP, BADOP, BADOP, 1, OPF_TARGET) sparse-0.6.4/opcode.h000066400000000000000000000020431411531012200144250ustar00rootroot00000000000000#ifndef OPCODE_H #define OPCODE_H #include "symbol.h" enum opcode { #define OPCODE(OP,NG,SW,SG,TF,N,FL) OP_##OP, #define OPCODE_RANGE(OP,S,E) OP_##OP = OP_##S, OP_##OP##_END = OP_##E, #include "opcode.def" #undef OPCODE #undef OPCODE_RANGE OP_LAST, /* keep this one last! */ }; extern const struct opcode_table { int negate:8; int swap:8; int sign:8; int to_float:8; unsigned int arity:2; unsigned int :6; unsigned int flags:8; #define OPF_NONE 0 #define OPF_TARGET (1 << 0) #define OPF_COMMU (1 << 1) #define OPF_ASSOC (1 << 2) #define OPF_UNOP (1 << 3) #define OPF_BINOP (1 << 4) #define OPF_COMPARE (1 << 5) #define OPF_SIGNED (1 << 6) #define OPF_UNSIGNED (1 << 7) } opcode_table[]; static inline int opcode_negate(int opcode) { return opcode_table[opcode].negate; } static inline int opcode_swap(int opcode) { return opcode_table[opcode].swap; } static inline int opcode_float(int opcode, struct symbol *type) { if (!type || !is_float_type(type)) return opcode; return opcode_table[opcode].to_float; } #endif sparse-0.6.4/optimize.c000066400000000000000000000050111411531012200150050ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // Copyright (C) 2004 Linus Torvalds // Copyright (C) 2004 Christopher Li /// // Optimization main loop // ---------------------- #include #include "optimize.h" #include "flowgraph.h" #include "linearize.h" #include "liveness.h" #include "simplify.h" #include "flow.h" #include "cse.h" #include "ir.h" #include "ssa.h" int repeat_phase; 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 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) { if (!insn->bb) continue; repeat_phase |= simplify_instruction(insn); if (!insn->bb) continue; assert(insn->bb == bb); cse_collect(insn); } END_FOR_EACH_PTR(insn); } END_FOR_EACH_PTR(bb); } static void cleanup_cfg(struct entrypoint *ep) { kill_unreachable_bbs(ep); domtree_build(ep); } /// // optimization main loop void optimize(struct entrypoint *ep) { if (fdump_ir & PASS_LINEARIZE) show_entry(ep); /* * Do trivial flow simplification - branches to * branches, kill dead basicblocks etc */ kill_unreachable_bbs(ep); ir_validate(ep); cfg_postorder(ep); if (simplify_cfg_early(ep)) kill_unreachable_bbs(ep); ir_validate(ep); domtree_build(ep); /* * Turn symbols into pseudos */ if (fpasses & PASS_MEM2REG) ssa_convert(ep); ir_validate(ep); if (fdump_ir & PASS_MEM2REG) show_entry(ep); if (!(fpasses & PASS_OPTIM)) return; repeat: /* * Remove trivial instructions, and try to CSE * the rest. */ do { simplify_memops(ep); do { repeat_phase = 0; clean_up_insns(ep); if (repeat_phase & REPEAT_CFG_CLEANUP) kill_unreachable_bbs(ep); cse_eliminate(ep); simplify_memops(ep); } while (repeat_phase); pack_basic_blocks(ep); if (repeat_phase & REPEAT_CFG_CLEANUP) cleanup_cfg(ep); } while (repeat_phase); 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); if (repeat_phase & REPEAT_CFG_CLEANUP) cleanup_cfg(ep); goto repeat; } /* Finally, add deathnotes to pseudos now that we have them */ if (dbg_dead) track_pseudo_death(ep); } sparse-0.6.4/optimize.h000066400000000000000000000001721411531012200150150ustar00rootroot00000000000000#ifndef OPTIMIZE_H #define OPTIMIZE_H struct entrypoint; /* optimize.c */ void optimize(struct entrypoint *ep); #endif sparse-0.6.4/options.c000066400000000000000000000577341411531012200146630ustar00rootroot00000000000000// SPDX-License-Identifier: MIT /* * 'sparse' library helper routines. * * Copyright (C) 2003 Transmeta Corp. * 2003-2004 Linus Torvalds * 2017-2020 Luc Van Oostenryck */ #include "options.h" #include "lib.h" #include "machine.h" #include "target.h" #include #include #include #include #ifndef __GNUC__ # define __GNUC__ 2 # define __GNUC_MINOR__ 95 # define __GNUC_PATCHLEVEL__ 0 #endif enum flag_type { FLAG_OFF, FLAG_ON, FLAG_FORCE_OFF }; int die_if_error = 0; int do_output = 1; int gcc_major = __GNUC__; int gcc_minor = __GNUC_MINOR__; int gcc_patchlevel = __GNUC_PATCHLEVEL__; int has_error = 0; int optimize_level; int optimize_size; int preprocess_only; int preprocessing; int verbose; #define CMDLINE_INCLUDE 20 int cmdline_include_nr = 0; char *cmdline_include[CMDLINE_INCLUDE]; const char *base_filename; const char *diag_prefix = ""; const char *gcc_base_dir = GCC_BASE; const char *multiarch_dir = MULTIARCH_TRIPLET; const char *outfile = NULL; enum standard standard = STANDARD_GNU89; int arch_big_endian = ARCH_BIG_ENDIAN; int arch_cmodel = CMODEL_UNKNOWN; int arch_fp_abi = FP_ABI_NATIVE; int arch_m64 = ARCH_M64_DEFAULT; int arch_msize_long = 0; int arch_os = OS_NATIVE; int dbg_compound = 0; int dbg_dead = 0; int dbg_domtree = 0; int dbg_entry = 0; int dbg_ir = 0; int dbg_postorder = 0; int dump_macro_defs = 0; int dump_macros_only = 0; unsigned long fdump_ir; int fhosted = 1; unsigned int fmax_errors = 100; unsigned int fmax_warnings = 100; int fmem_report = 0; unsigned long long fmemcpy_max_count = 100000; unsigned long fpasses = ~0UL; int fpic = 0; int fpie = 0; int fshort_wchar = 0; int funsigned_bitfields = 0; int funsigned_char = 0; int Waddress = 0; int Waddress_space = 1; int Wbitwise = 1; int Wbitwise_pointer = 0; int Wcast_from_as = 0; int Wcast_to_as = 0; int Wcast_truncate = 1; int Wconstant_suffix = 0; int Wconstexpr_not_const = 0; int Wcontext = 1; int Wdecl = 1; int Wdeclarationafterstatement = -1; int Wdefault_bitfield_sign = 0; int Wdesignated_init = 1; int Wdo_while = 0; int Wenum_mismatch = 1; int Wexternal_function_has_definition = 1; int Wflexible_array_array = 1; int Wflexible_array_nested = 0; int Wflexible_array_sizeof = 0; int Wflexible_array_union = 0; int Wimplicit_int = 1; int Winit_cstring = 0; int Wint_to_pointer_cast = 1; int Wmemcpy_max_count = 1; int Wnewline_eof = 1; int Wnon_pointer_null = 1; int Wold_initializer = 1; int Wold_style_definition = 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 Wpast_deep_designator = 0; int Wpedantic = 0; int Wpointer_arith = 0; int Wpointer_to_int_cast = 1; int Wptr_subtraction_blows = 0; int Wreturn_void = 0; int Wshadow = 0; int Wshift_count_negative = 1; int Wshift_count_overflow = 1; int Wsizeof_bool = 0; int Wsparse_error = FLAG_FORCE_OFF; int Wstrict_prototypes = 1; int Wtautological_compare = 0; int Wtransparent_union = 0; int Wtypesign = 0; int Wundef = 0; int Wuninitialized = 1; int Wunion_cast = 0; int Wuniversal_initializer = 0; int Wunknown_attribute = 0; int Wvla = 1; //////////////////////////////////////////////////////////////////////////////// // Helpers for option parsing static const char *match_option(const char *arg, const char *prefix) { unsigned int n = strlen(prefix); if (strncmp(arg, prefix, n) == 0) return arg + n; return NULL; } struct val_map { const char *name; int val; }; static int handle_subopt_val(const char *opt, const char *arg, const struct val_map *map, int *flag) { const char *name; if (*arg++ != '=') die("missing argument for option '%s'", opt); for (;(name = map->name); map++) { if (strcmp(name, arg) == 0 || strcmp(name, "*") == 0) { *flag = map->val; return 1; } if (strcmp(name, "?") == 0) die("invalid argument '%s' in option '%s'", arg, opt); } return 0; } struct mask_map { const char *name; unsigned long mask; }; static int apply_mask(unsigned long *val, const char *str, unsigned len, const struct mask_map *map, int neg) { const char *name; for (;(name = map->name); map++) { if (!strncmp(name, str, len) && !name[len]) { if (neg == 0) *val |= map->mask; else *val &= ~map->mask; return 0; } } return 1; } static int handle_suboption_mask(const char *arg, const char *opt, const struct mask_map *map, unsigned long *flag) { if (*opt == '\0') { apply_mask(flag, "", 0, map, 0); return 1; } if (*opt++ != '=') return 0; while (1) { unsigned int len = strcspn(opt, ",+"); int neg = 0; if (len == 0) goto end; if (!strncmp(opt, "no-", 3)) { opt += 3; len -= 3; neg = 1; } if (apply_mask(flag, opt, len, map, neg)) die("error: wrong option '%.*s' for \'%s\'", len, opt, arg); end: opt += len; if (*opt++ == '\0') break; } return 1; } #define OPT_INVERSE 1 #define OPT_VAL 2 struct flag { const char *name; int *flag; int (*fun)(const char *arg, const char *opt, const struct flag *, int options); unsigned long mask; int val; }; static int handle_switches(const char *ori, const char *opt, const struct flag *flags) { const char *arg = opt; int val = 1; // Prefixe "no-" mean to turn flag off. if (strncmp(arg, "no-", 3) == 0) { arg += 3; val = 0; } for (; flags->name; flags++) { const char *opt = match_option(arg, flags->name); int rc; if (!opt) continue; if (flags->fun) { int options = 0; if (!val) options |= OPT_INVERSE; if ((rc = flags->fun(ori, opt, flags, options))) return rc; } // boolean flag if (opt[0] == '\0' && flags->flag) { if (flags->mask & OPT_VAL) val = flags->val; if (flags->mask & OPT_INVERSE) val = !val; *flags->flag = val; return 1; } } // not handled return 0; } static char **handle_onoff_switch(char *arg, char **next, const struct flag flags[]) { int flag = FLAG_ON; char *p = arg + 1; unsigned i; // Prefixes "no" and "no-" mean to turn warning off. if (p[0] == 'n' && p[1] == 'o') { p += 2; if (p[0] == '-') p++; flag = FLAG_FORCE_OFF; } for (i = 0; flags[i].name; i++) { if (!strcmp(p,flags[i].name)) { *flags[i].flag = flag; return next; } } // Unknown. return NULL; } static void handle_onoff_switch_finalize(const struct flag flags[]) { unsigned i; for (i = 0; flags[i].name; i++) { if (*flags[i].flag == FLAG_FORCE_OFF) *flags[i].flag = FLAG_OFF; } } static int handle_switch_setval(const char *arg, const char *opt, const struct flag *flag, int options) { *(flag->flag) = flag->mask; return 1; } #define OPTNUM_ZERO_IS_INF 1 #define OPTNUM_UNLIMITED 2 #define OPT_NUMERIC(NAME, TYPE, FUNCTION) \ static int opt_##NAME(const char *arg, const char *opt, TYPE *ptr, int flag) \ { \ char *end; \ TYPE val; \ \ val = FUNCTION(opt, &end, 0); \ if (*end != '\0' || end == opt) { \ if ((flag & OPTNUM_UNLIMITED) && !strcmp(opt, "unlimited")) \ val = ~val; \ else \ die("error: wrong argument to \'%s\'", arg); \ } \ if ((flag & OPTNUM_ZERO_IS_INF) && val == 0) \ val = ~val; \ *ptr = val; \ return 1; \ } OPT_NUMERIC(ullong, unsigned long long, strtoull) OPT_NUMERIC(uint, unsigned int, strtoul) //////////////////////////////////////////////////////////////////////////////// // Option parsing static char **handle_switch_a(char *arg, char **next) { if (!strcmp(arg, "ansi")) standard = STANDARD_C89; return next; } static char **handle_switch_D(char *arg, char **next) { const char *name = arg + 1; const char *value = "1"; if (!*name) { arg = *++next; if (!arg) die("argument to `-D' is missing"); name = arg; } for (;;arg++) { char c; c = *arg; if (!c) break; if (c == '=') { *arg = '\0'; value = arg + 1; break; } } add_pre_buffer("#define %s %s\n", name, value); return next; } static char **handle_switch_d(char *arg, char **next) { char *arg_char = arg + 1; /* * -d, where is a sequence of characters, not preceded * by a space. If you specify characters whose behaviour conflicts, * the result is undefined. */ while (*arg_char) { switch (*arg_char) { case 'M': /* dump just the macro definitions */ dump_macros_only = 1; dump_macro_defs = 0; break; case 'D': /* like 'M', but also output pre-processed text */ dump_macro_defs = 1; dump_macros_only = 0; break; case 'N': /* like 'D', but only output macro names not bodies */ break; case 'I': /* like 'D', but also output #include directives */ break; case 'U': /* like 'D', but only output expanded macros */ break; } arg_char++; } return next; } static char **handle_switch_E(char *arg, char **next) { if (arg[1] == '\0') preprocess_only = 1; return next; } static int handle_ftabstop(const char *arg, const char *opt, const struct flag *flag, int options) { unsigned long val; char *end; if (*opt == '\0') die("error: missing argument to \"%s\"", arg); /* we silently ignore silly values */ val = strtoul(opt, &end, 10); if (*end == '\0' && 1 <= val && val <= 100) tabstop = val; return 1; } static int handle_fpasses(const char *arg, const char *opt, const struct flag *flag, int options) { unsigned long mask; mask = flag->mask; if (*opt == '\0') { if (options & OPT_INVERSE) fpasses &= ~mask; else fpasses |= mask; return 1; } if (options & OPT_INVERSE) return 0; if (!strcmp(opt, "-enable")) { fpasses |= mask; return 1; } if (!strcmp(opt, "-disable")) { fpasses &= ~mask; return 1; } if (!strcmp(opt, "=last")) { // clear everything above mask |= mask - 1; fpasses &= mask; return 1; } return 0; } static int handle_fdiagnostic_prefix(const char *arg, const char *opt, const struct flag *flag, int options) { switch (*opt) { case '\0': diag_prefix = "sparse: "; return 1; case '=': diag_prefix = xasprintf("%s: ", opt+1); return 1; default: return 0; } } static int handle_fdump_ir(const char *arg, const char *opt, const struct flag *flag, int options) { static const struct mask_map dump_ir_options[] = { { "", PASS_LINEARIZE }, { "linearize", PASS_LINEARIZE }, { "mem2reg", PASS_MEM2REG }, { "final", PASS_FINAL }, { }, }; return handle_suboption_mask(arg, opt, dump_ir_options, &fdump_ir); } static int handle_fmemcpy_max_count(const char *arg, const char *opt, const struct flag *flag, int options) { opt_ullong(arg, opt, &fmemcpy_max_count, OPTNUM_ZERO_IS_INF|OPTNUM_UNLIMITED); return 1; } static int handle_fmax_errors(const char *arg, const char *opt, const struct flag *flag, int options) { opt_uint(arg, opt, &fmax_errors, OPTNUM_UNLIMITED); return 1; } static int handle_fmax_warnings(const char *arg, const char *opt, const struct flag *flag, int options) { opt_uint(arg, opt, &fmax_warnings, OPTNUM_UNLIMITED); return 1; } static struct flag fflags[] = { { "diagnostic-prefix", NULL, handle_fdiagnostic_prefix }, { "dump-ir", NULL, handle_fdump_ir }, { "freestanding", &fhosted, NULL, OPT_INVERSE }, { "hosted", &fhosted }, { "linearize", NULL, handle_fpasses, PASS_LINEARIZE }, { "max-errors=", NULL, handle_fmax_errors }, { "max-warnings=", NULL, handle_fmax_warnings }, { "mem-report", &fmem_report }, { "memcpy-max-count=", NULL, handle_fmemcpy_max_count }, { "tabstop=", NULL, handle_ftabstop }, { "mem2reg", NULL, handle_fpasses, PASS_MEM2REG }, { "optim", NULL, handle_fpasses, PASS_OPTIM }, { "pic", &fpic, handle_switch_setval, 1 }, { "PIC", &fpic, handle_switch_setval, 2 }, { "pie", &fpie, handle_switch_setval, 1 }, { "PIE", &fpie, handle_switch_setval, 2 }, { "signed-bitfields", &funsigned_bitfields, NULL, OPT_INVERSE }, { "unsigned-bitfields", &funsigned_bitfields, NULL, }, { "signed-char", &funsigned_char, NULL, OPT_INVERSE }, { "short-wchar", &fshort_wchar }, { "unsigned-char", &funsigned_char, NULL, }, { }, }; static char **handle_switch_f(char *arg, char **next) { if (handle_switches(arg-1, arg+1, fflags)) 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_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_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 int handle_march(const char *opt, const char *arg, const struct flag *flag, int options) { if (arch_target->parse_march) arch_target->parse_march(arg); return 1; } static int handle_mcmodel(const char *opt, const char *arg, const struct flag *flag, int options) { static const struct val_map cmodels[] = { { "kernel", CMODEL_KERNEL }, { "large", CMODEL_LARGE }, { "medany", CMODEL_MEDANY }, { "medium", CMODEL_MEDIUM }, { "medlow", CMODEL_MEDLOW }, { "small", CMODEL_SMALL }, { "tiny", CMODEL_TINY }, { }, }; return handle_subopt_val(opt, arg, cmodels, flag->flag); } static int handle_mfloat_abi(const char *opt, const char *arg, const struct flag *flag, int options) { static const struct val_map fp_abis[] = { { "hard", FP_ABI_HARD }, { "soft", FP_ABI_SOFT }, { "softfp", FP_ABI_HYBRID }, { "?" }, }; return handle_subopt_val(opt, arg, fp_abis, flag->flag); } 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 const struct flag mflags[] = { { "64", &arch_m64, NULL, OPT_VAL, ARCH_LP64 }, { "32", &arch_m64, NULL, OPT_VAL, ARCH_LP32 }, { "31", &arch_m64, NULL, OPT_VAL, ARCH_LP32 }, { "16", &arch_m64, NULL, OPT_VAL, ARCH_LP32 }, { "x32",&arch_m64, NULL, OPT_VAL, ARCH_X32 }, { "size-llp64", &arch_m64, NULL, OPT_VAL, ARCH_LLP64 }, { "size-long", &arch_msize_long }, { "arch=", NULL, handle_march }, { "big-endian", &arch_big_endian, NULL }, { "little-endian", &arch_big_endian, NULL, OPT_INVERSE }, { "cmodel", &arch_cmodel, handle_mcmodel }, { "float-abi", &arch_fp_abi, handle_mfloat_abi }, { "hard-float", &arch_fp_abi, NULL, OPT_VAL, FP_ABI_HARD }, { "soft-float", &arch_fp_abi, NULL, OPT_VAL, FP_ABI_SOFT }, { } }; static char **handle_switch_m(char *arg, char **next) { if (!strcmp(arg, "multiarch-dir")) { return handle_multiarch_dir(arg, next); } else { handle_switches(arg-1, arg+1, mflags); } 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_switch_O(char *arg, char **next) { int level = 1; if (arg[1] >= '0' && arg[1] <= '9') level = arg[1] - '0'; optimize_level = level; optimize_size = arg[1] == 's'; return next; } static char **handle_switch_o(char *arg, char **next) { if (!strcmp(arg, "o")) { // "-o foo" if (!*++next) die("argument to '-o' is missing"); outfile = *next; } // else "-ofoo" return next; } static const struct flag pflags[] = { { "pedantic", &Wpedantic, NULL, OPT_VAL, FLAG_ON }, { } }; static char **handle_switch_p(char *arg, char **next) { handle_switches(arg-1, arg, pflags); return next; } static char **handle_switch_s(const char *arg, char **next) { if ((arg = match_option(arg, "std="))) { 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 if (!strcmp(arg, "c17") || !strcmp(arg, "c18") || !strcmp(arg, "iso9899:2017") || !strcmp(arg, "iso9899:2018")) standard = STANDARD_C17; else if (!strcmp(arg, "gnu17") || !strcmp(arg, "gnu18")) standard = STANDARD_GNU17; else die("Unsupported C dialect"); } return next; } static char **handle_switch_U(char *arg, char **next) { const char *name = arg + 1; if (*name == '\0') { name = *++next; if (!name) die("argument to `-U' is missing"); } add_pre_buffer("#undef %s\n", name); return next; } static struct flag debugs[] = { { "compound", &dbg_compound}, { "dead", &dbg_dead}, { "domtree", &dbg_domtree}, { "entry", &dbg_entry}, { "ir", &dbg_ir}, { "postorder", &dbg_postorder}, { } }; static char **handle_switch_v(char *arg, char **next) { char ** ret = handle_onoff_switch(arg, next, debugs); if (ret) return ret; // Unknown. do { verbose++; } while (*++arg == 'v'); return next; } static void handle_switch_v_finalize(void) { handle_onoff_switch_finalize(debugs); } static const struct flag warnings[] = { { "address", &Waddress }, { "address-space", &Waddress_space }, { "bitwise", &Wbitwise }, { "bitwise-pointer", &Wbitwise_pointer}, { "cast-from-as", &Wcast_from_as }, { "cast-to-as", &Wcast_to_as }, { "cast-truncate", &Wcast_truncate }, { "constant-suffix", &Wconstant_suffix }, { "constexpr-not-const", &Wconstexpr_not_const}, { "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 }, { "external-function-has-definition", &Wexternal_function_has_definition }, { "flexible-array-array", &Wflexible_array_array }, { "flexible-array-nested", &Wflexible_array_nested }, { "flexible-array-sizeof", &Wflexible_array_sizeof }, { "flexible-array-union", &Wflexible_array_union }, { "implicit-int", &Wimplicit_int }, { "init-cstring", &Winit_cstring }, { "int-to-pointer-cast", &Wint_to_pointer_cast }, { "memcpy-max-count", &Wmemcpy_max_count }, { "non-pointer-null", &Wnon_pointer_null }, { "newline-eof", &Wnewline_eof }, { "old-initializer", &Wold_initializer }, { "old-style-definition", &Wold_style_definition }, { "one-bit-signed-bitfield", &Wone_bit_signed_bitfield }, { "override-init", &Woverride_init }, { "override-init-all", &Woverride_init_all }, { "paren-string", &Wparen_string }, { "past-deep-designator", &Wpast_deep_designator }, { "pedantic", &Wpedantic }, { "pointer-to-int-cast", &Wpointer_to_int_cast }, { "ptr-subtraction-blows", &Wptr_subtraction_blows }, { "return-void", &Wreturn_void }, { "shadow", &Wshadow }, { "shift-count-negative", &Wshift_count_negative }, { "shift-count-overflow", &Wshift_count_overflow }, { "sizeof-bool", &Wsizeof_bool }, { "strict-prototypes", &Wstrict_prototypes }, { "pointer-arith", &Wpointer_arith }, { "sparse-error", &Wsparse_error }, { "tautological-compare", &Wtautological_compare }, { "transparent-union", &Wtransparent_union }, { "typesign", &Wtypesign }, { "undef", &Wundef }, { "uninitialized", &Wuninitialized }, { "union-cast", &Wunion_cast }, { "universal-initializer", &Wuniversal_initializer }, { "unknown-attribute", &Wunknown_attribute }, { "vla", &Wvla }, { } }; static char **handle_switch_W(char *arg, char **next) { char ** ret = handle_onoff_switch(arg, next, warnings); if (ret) return ret; if (!strcmp(arg, "Wsparse-all")) { int i; for (i = 0; warnings[i].name; i++) { if (*warnings[i].flag != FLAG_FORCE_OFF) *warnings[i].flag = FLAG_ON; } } // Unknown. return next; } static void handle_switch_W_finalize(void) { handle_onoff_switch_finalize(warnings); /* default Wdeclarationafterstatement based on the C dialect */ if (-1 == Wdeclarationafterstatement) { switch (standard) { case STANDARD_C89: case STANDARD_C94: Wdeclarationafterstatement = 1; break; default: Wdeclarationafterstatement = 0; break; } } } static char **handle_switch_x(char *arg, char **next) { if (!*++next) die("missing argument for -x option"); return next; } static char **handle_arch(char *arg, char **next) { enum machine mach; if (*arg++ != '=') die("missing argument for --arch option"); mach = target_parse(arg); if (mach != MACH_UNKNOWN) target_config(mach); return next; } 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; } static char **handle_os(char *arg, char **next) { if (*arg++ != '=') die("missing argument for --os option"); target_os(arg); return next; } static char **handle_version(char *arg, char **next) { printf("%s\n", sparse_version); exit(0); } 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[] = { { "arch", handle_arch, 1 }, { "os", handle_os, 1 }, { "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; } 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 'p': return handle_switch_p(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 'x': return handle_switch_x(arg, next); case '-': return handle_long_options(arg + 1, next); default: break; } /* * Ignore unknown command line options: * they're probably gcc switches */ return next; } void handle_switch_finalize(void) { handle_switch_v_finalize(); handle_switch_W_finalize(); } sparse-0.6.4/options.h000066400000000000000000000066451411531012200146630ustar00rootroot00000000000000#ifndef OPTIONS_H #define OPTIONS_H enum { CMODEL_UNKNOWN, CMODEL_KERNEL, CMODEL_LARGE, CMODEL_MEDANY, CMODEL_MEDIUM, CMODEL_MEDLOW, CMODEL_PIC, CMODEL_SMALL, CMODEL_TINY, CMODEL_LAST, }; enum standard { STANDARD_NONE, STANDARD_GNU, STANDARD_C89, STANDARD_GNU89 = STANDARD_C89 | STANDARD_GNU, STANDARD_C94, STANDARD_GNU94 = STANDARD_C94 | STANDARD_GNU, STANDARD_C99, STANDARD_GNU99 = STANDARD_C99 | STANDARD_GNU, STANDARD_C11, STANDARD_GNU11 = STANDARD_C11 | STANDARD_GNU, STANDARD_C17, STANDARD_GNU17 = STANDARD_C17 | STANDARD_GNU, }; extern int die_if_error; extern int do_output; extern int gcc_major; extern int gcc_minor; extern int gcc_patchlevel; extern int optimize_level; extern int optimize_size; extern int preprocess_only; extern int preprocessing; extern int repeat_phase; extern int verbose; extern int cmdline_include_nr; extern char *cmdline_include[]; extern const char *base_filename; extern const char *diag_prefix; extern const char *gcc_base_dir; extern const char *multiarch_dir; extern const char *outfile; extern enum standard standard; extern unsigned int tabstop; extern int arch_big_endian; extern int arch_cmodel; extern int arch_fp_abi; extern int arch_m64; extern int arch_msize_long; extern int arch_os; extern int dbg_compound; extern int dbg_dead; extern int dbg_domtree; extern int dbg_entry; extern int dbg_ir; extern int dbg_postorder; extern int dump_macro_defs; extern int dump_macros_only; extern unsigned long fdump_ir; extern int fhosted; extern unsigned int fmax_errors; extern unsigned int fmax_warnings; extern int fmem_report; extern unsigned long long fmemcpy_max_count; extern unsigned long fpasses; extern int fpic; extern int fpie; extern int fshort_wchar; extern int funsigned_bitfields; extern int funsigned_char; extern int Waddress; extern int Waddress_space; extern int Wbitwise; extern int Wbitwise_pointer; extern int Wcast_from_as; extern int Wcast_to_as; extern int Wcast_truncate; extern int Wconstant_suffix; extern int Wconstexpr_not_const; 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 Wexternal_function_has_definition; extern int Wflexible_array_array; extern int Wflexible_array_nested; extern int Wflexible_array_sizeof; extern int Wflexible_array_union; extern int Wimplicit_int; extern int Winit_cstring; extern int Wint_to_pointer_cast; extern int Wmemcpy_max_count; extern int Wnewline_eof; extern int Wnon_pointer_null; extern int Wold_initializer; extern int Wold_style_definition; 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 Wpast_deep_designator; extern int Wpedantic; extern int Wpointer_arith; extern int Wpointer_to_int_cast; extern int Wptr_subtraction_blows; extern int Wreturn_void; extern int Wshadow; extern int Wshift_count_negative; extern int Wshift_count_overflow; extern int Wsizeof_bool; extern int Wsparse_error; extern int Wstrict_prototypes; extern int Wtautological_compare; extern int Wtransparent_union; extern int Wtypesign; extern int Wundef; extern int Wuninitialized; extern int Wunion_cast; extern int Wuniversal_initializer; extern int Wunknown_attribute; extern int Wvla; extern char **handle_switch(char *arg, char **next); extern void handle_switch_finalize(void); #endif sparse-0.6.4/parse.c000066400000000000000000002471771411531012200143040ustar00rootroot00000000000000/* * 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); typedef struct token *declarator_t(struct token *, struct symbol *, struct decl_state *); static declarator_t struct_specifier, union_specifier, enum_specifier, attribute_specifier, typeof_specifier, storage_specifier, thread_specifier; static declarator_t generic_qualifier; static declarator_t autotype_specifier; 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_function, 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; static to_mode_t to_pointer_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, }; static void asm_modifier(struct token *token, unsigned long *mods, unsigned long mod) { if (*mods & mod) warning(token->pos, "duplicated asm modifier"); *mods |= mod; } static struct symbol_op typedef_op = { .type = KW_MODIFIER, .declarator = storage_specifier, }; static struct symbol_op inline_op = { .type = KW_MODIFIER, .declarator = generic_qualifier, .asm_modifier = asm_modifier, }; static struct symbol_op noreturn_op = { .type = KW_MODIFIER, .declarator = generic_qualifier, }; 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 = storage_specifier, }; static struct symbol_op register_op = { .type = KW_MODIFIER, .declarator = storage_specifier, }; static struct symbol_op static_op = { .type = KW_MODIFIER|KW_STATIC, .declarator = storage_specifier, }; static struct symbol_op extern_op = { .type = KW_MODIFIER, .declarator = storage_specifier, }; static struct symbol_op thread_op = { .type = KW_MODIFIER, .declarator = thread_specifier, }; static struct symbol_op const_op = { .type = KW_QUALIFIER, .declarator = generic_qualifier, }; static struct symbol_op volatile_op = { .type = KW_QUALIFIER, .declarator = generic_qualifier, .asm_modifier = asm_modifier, }; static struct symbol_op restrict_op = { .type = KW_QUALIFIER, .declarator = generic_qualifier, }; static struct symbol_op atomic_op = { .type = KW_QUALIFIER, .declarator = generic_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 autotype_op = { .type = KW_SPECIFIER, .declarator = autotype_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 = CInt, }; 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, .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, .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, .test = Set_S|Set_Char|Set_Float|Set_Short|Set_Vlong, .set = Set_Long, }; static struct symbol_op int128_op = { .type = KW_SPECIFIER, .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|Set_Vlong, .class = CInt, }; 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, .attribute = attribute_context, }; static struct symbol_op range_op = { .statement = parse_range_statement, }; static struct symbol_op asm_op = { .type = KW_ASM, .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_fun_op = { .attribute = attribute_function, }; 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_pointer_op = { .type = KW_MODE, .to_mode = to_pointer_mode }; static struct symbol_op mode_word_op = { .type = KW_MODE, .to_mode = to_word_mode }; /* * Define the keyword and their effects. * The entries in the 'typedef' and put in NS_TYPEDEF and * are automatically set as reserved keyword while the ones * in the 'keyword' table are just put in NS_KEYWORD. * * The entries are added via the 3 macros: * N() for entries with "name" only, * D() for entries with "name" & "__name__", * A() for entries with "name", "__name" & "__name__", * U() for entries with "__name" & "__name__". */ static struct init_keyword { const char *name; struct symbol_op *op; struct symbol *type; unsigned long mods; } typedefs[] = { #define N(I, O,...) { I, O,##__VA_ARGS__ } #define D(I, O,...) N(I,O,##__VA_ARGS__ ), \ N("__" I "__",O,##__VA_ARGS__) #define A(I, O,...) N(I,O,##__VA_ARGS__ ), \ N("__" I,O,##__VA_ARGS__), \ N("__" I "__",O,##__VA_ARGS__) #define U(I, O,...) N("__" I,O,##__VA_ARGS__), \ N("__" I "__",O,##__VA_ARGS__) /* Storage classes */ N("auto", &auto_op, .mods = MOD_AUTO), N("register", ®ister_op, .mods = MOD_REGISTER), N("static", &static_op, .mods = MOD_STATIC), N("extern", &extern_op, .mods = MOD_EXTERN), N("__thread", &thread_op), N("_Thread_local", &thread_op), A("inline", &inline_op, .mods = MOD_INLINE), /* Typedef ... */ N("typedef", &typedef_op, .mods = MOD_USERTYPE), A("typeof", &typeof_op), N("__auto_type", &autotype_op), /* Type qualifiers */ A("const", &const_op, .mods = MOD_CONST), A("volatile", &volatile_op, .mods = MOD_VOLATILE), A("restrict", &restrict_op, .mods = MOD_RESTRICT), N("_Atomic", &atomic_op, .mods = MOD_ATOMIC), N("_Noreturn", &noreturn_op, .mods = MOD_NORETURN), N("_Alignas", &alignas_op), U("attribute", &attribute_op), /* Type specifiers */ N("struct", &struct_op), N("union", &union_op), N("enum", &enum_op), N("void", &spec_op, .type = &void_ctype), N("char", &char_op), N("short", &short_op), N("int", &int_op), N("long", &long_op), N("float", &float_op), N("double", &double_op), A("signed", &signed_op), N("unsigned", &unsigned_op), N("__int128", &int128_op), N("_Bool", &spec_op, .type = &bool_ctype), /* Predeclared types */ N("__builtin_va_list", &spec_op, .type = &ptr_ctype), N("__builtin_ms_va_list",&spec_op, .type = &ptr_ctype), N("__int128_t", &spec_op, .type = &sint128_ctype), N("__uint128_t", &spec_op, .type = &uint128_ctype), N("_Float32", &spec_op, .type = &float32_ctype), N("_Float32x", &spec_op, .type = &float32x_ctype), N("_Float64", &spec_op, .type = &float64_ctype), N("_Float64x", &spec_op, .type = &float64x_ctype), N("_Float128", &spec_op, .type = &float128_ctype), }, keywords[] = { /* Statements */ N("if", &if_op), N("return", &return_op), N("break", &loop_iter_op), N("continue", &loop_iter_op), N("default", &default_op), N("case", &case_op), N("switch", &switch_op), N("for", &for_op), N("while", &while_op), N("do", &do_op), N("goto", &goto_op), A("asm", &asm_op), N("context", &context_op), N("__context__", &__context___op), N("__range__", &range_op), N("_Static_assert", &static_assert_op), /* Attributes */ D("packed", &packed_op), D("aligned", &aligned_op), D("nocast", &attr_mod_op, .mods = MOD_NOCAST), D("noderef", &attr_mod_op, .mods = MOD_NODEREF), D("safe", &attr_mod_op, .mods = MOD_SAFE), D("unused", &attr_mod_op, .mods = MOD_UNUSED), D("externally_visible", &attr_mod_op, .mods = MOD_EXT_VISIBLE), D("force", &attr_force_op), D("bitwise", &attr_bitwise_op, .mods = MOD_BITWISE), D("address_space", &address_space_op), D("designated_init", &designated_init_op), D("transparent_union", &transparent_union_op), D("noreturn", &attr_fun_op, .mods = MOD_NORETURN), D("pure", &attr_fun_op, .mods = MOD_PURE), A("const", &attr_fun_op, .mods = MOD_PURE), D("gnu_inline", &attr_fun_op, .mods = MOD_GNU_INLINE), /* Modes */ D("mode", &mode_op), D("QI", &mode_QI_op), D("HI", &mode_HI_op), D("SI", &mode_SI_op), D("DI", &mode_DI_op), D("TI", &mode_TI_op), D("byte", &mode_QI_op), D("pointer", &mode_pointer_op), D("word", &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", }; static void init_keyword(int stream, struct init_keyword *kw, enum namespace ns) { struct symbol *sym = create_symbol(stream, kw->name, SYM_KEYWORD, ns); sym->ident->keyword = 1; sym->ident->reserved |= (ns == NS_TYPEDEF); sym->ctype.modifiers = kw->mods; sym->ctype.base_type = kw->type; sym->op = kw->op; } void init_parser(int stream) { int i; for (i = 0; i < ARRAY_SIZE(typedefs); i++) init_keyword(stream, &typedefs[i], NS_TYPEDEF); for (i = 0; i < ARRAY_SIZE(keywords); i++) init_keyword(stream, &keywords[i], NS_KEYWORD); 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; } } } static struct token *skip_to(struct token *token, int op) { while (!match_op(token, op) && !eof_token(token)) token = token->next; return token; } static struct token bad_token = { .pos.type = TOKEN_BAD }; struct token *expect(struct token *token, int op, const char *where) { if (!match_op(token, op)) { 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; } /// // issue an error message on new parsing errors // @token: the current token // @errmsg: the error message // If the current token is from a previous error, an error message // has already been issued, so nothing more is done. // Otherwise, @errmsg is displayed followed by the current token. static void unexpected(struct token *token, const char *errmsg) { if (token == &bad_token) return; sparse_error(token->pos, "%s", errmsg); sparse_error(token->pos, "got %s", show_token(token)); } // 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); } 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_ctype(struct position pos, struct ctype *dst, struct ctype *src); 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, int used) { 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); if (used) sym->used = 1; 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 decl_state attr = { }; struct symbol *sym; struct position *repos; token = handle_attributes(token, &attr); 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, '{')) return 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; // Mark the structure as needing re-examination sym->examined = 0; } else if (match_op(token, '{')) { // private struct/union/enum type sym = alloc_symbol(token->pos, type); set_current_scope(sym); // used by dissect ctx->ctype.base_type = sym; } else { sparse_error(token->pos, "expected declaration"); ctx->ctype.base_type = &bad_ctype; return token; } token = parse(token->next, sym); token = expect(token, '}', "at end of specifier"); attr.ctype.base_type = sym; token = handle_attributes(token, &attr); apply_ctype(token->pos, &sym->ctype, &attr.ctype); sym->packed = attr.packed; 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 symbol *sym, 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 symbol *sym, struct decl_state *ctx) { return struct_union_enum_specifier(SYM_UNION, token, ctx, parse_union_declaration); } /// // safe right shift // // This allow to use a shift amount as big (or bigger) // than the width of the value to be shifted, in which case // the result is, of course, 0. static unsigned long long rshift(unsigned long long val, unsigned int n) { if (n >= (sizeof(val) * 8)) return 0; return val >> n; } struct range { long long neg; unsigned long long pos; }; static void update_range(struct range *range, unsigned long long uval, struct symbol *vtype) { long long sval = uval; if (is_signed_type(vtype) && (sval < 0)) { if (sval < range->neg) range->neg = sval; } else { if (uval > range->pos) range->pos = uval; } } static int type_is_ok(struct symbol *type, struct range range) { int shift = type->bit_size; int is_unsigned = type->ctype.modifiers & MOD_UNSIGNED; if (!is_unsigned) shift--; if (rshift(range.pos, shift)) return 0; if (range.neg == 0) return 1; if (is_unsigned) return 0; if (rshift(~range.neg, shift)) return 0; return 1; } static struct range type_range(struct symbol *type) { struct range range; unsigned int size = type->bit_size; unsigned long long max; long long min; if (is_signed_type(type)) { min = sign_bit(size); max = min - 1; } else { min = 0; max = bits_mask(size); } range.pos = max; range.neg = min; return range; } static int val_in_range(struct range *range, long long sval, struct symbol *vtype) { unsigned long long uval = sval; if (is_signed_type(vtype) && (sval < 0)) return range->neg <= sval; else return uval <= range->pos; } static void cast_enum_list(struct symbol_list *list, struct symbol *base_type) { struct range irange = type_range(&int_ctype); struct symbol *sym; FOR_EACH_PTR(list, sym) { struct expression *expr = sym->initializer; struct symbol *ctype; long long val; if (expr->type != EXPR_VALUE) continue; ctype = expr->ctype; val = get_expression_value(expr); if (is_int_type(ctype) && val_in_range(&irange, val, ctype)) { expr->ctype = &int_ctype; continue; } cast_value(expr, base_type, expr, ctype); expr->ctype = base_type; } 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; struct range range = { }; int mix_bitwise = 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 decl_state ctx = { }; struct symbol *sym; // FIXME: only 'deprecated' should be accepted next = handle_attributes(next, &ctx); 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 || ctype == &bad_ctype) { base_type = ctype; } else if (ctype == base_type) { /* nothing */ } else if (is_int_type(base_type) && is_int_type(ctype)) { base_type = &int_ctype; } else if (is_restricted_type(base_type) != is_restricted_type(ctype)) { if (!mix_bitwise++) { warning(expr->pos, "mixed bitwiseness"); } } else if (is_restricted_type(base_type) && base_type != ctype) { sparse_error(expr->pos, "incompatible restricted type"); info(expr->pos, " expected: %s", show_typename(base_type)); info(expr->pos, " got: %s", show_typename(ctype)); base_type = &bad_ctype; } else if (base_type != &bad_ctype) { sparse_error(token->pos, "bad enum definition"); base_type = &bad_ctype; } parent->ctype.base_type = base_type; } if (is_int_type(base_type)) { update_range(&range, lastval, ctype); } token = next; sym->endpos = token->pos; if (!match_op(token, ',')) break; token = token->next; } if (!base_type) { sparse_error(token->pos, "empty enum definition"); base_type = &bad_ctype; } else if (!is_int_type(base_type)) ; else if (type_is_ok(&uint_ctype, range)) base_type = &uint_ctype; else if (type_is_ok(&int_ctype, range)) base_type = &int_ctype; else if (type_is_ok(&ulong_ctype, range)) base_type = &ulong_ctype; else if (type_is_ok(&long_ctype, range)) base_type = &long_ctype; else if (type_is_ok(&ullong_ctype, range)) base_type = &ullong_ctype; else if (type_is_ok(&llong_ctype, range)) base_type = &llong_ctype; else base_type = &bad_ctype; parent->ctype.base_type = base_type; parent->ctype.modifiers |= (base_type->ctype.modifiers & MOD_UNSIGNED); parent->examined = 0; if (mix_bitwise) return token; cast_enum_list(parent->symbol_list, base_type); return token; } static struct token *enum_specifier(struct token *token, struct symbol *sym, 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 struct token *typeof_specifier(struct token *token, struct symbol *sym, struct decl_state *ctx) { if (!match_op(token, '(')) { sparse_error(token->pos, "expected '(' after typeof"); return token; } if (lookup_type(token->next)) { struct symbol *sym; token = typename(token->next, &sym, NULL); ctx->ctype.base_type = sym->ctype.base_type; apply_ctype(token->pos, &ctx->ctype, &sym->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 *autotype_specifier(struct token *token, struct symbol *sym, struct decl_state *ctx) { ctx->ctype.base_type = &autotype_ctype; ctx->autotype = 1; return token; } 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; ctx->packed = 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_mod(struct position *pos, unsigned long *mods, unsigned long mod) { if (*mods & mod & ~MOD_DUP_OK) warning(*pos, "duplicate %s", modifier_name(mod)); *mods |= mod; } static void apply_qualifier(struct position *pos, struct ctype *ctx, unsigned long qual) { apply_mod(pos, &ctx->modifiers, qual); } static struct token *attribute_modifier(struct token *token, struct symbol *attr, struct decl_state *ctx) { apply_mod(&token->pos, &ctx->ctype.modifiers, attr->ctype.modifiers); return token; } static struct token *attribute_function(struct token *token, struct symbol *attr, struct decl_state *ctx) { apply_mod(&token->pos, &ctx->f_modifiers, 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 ident *numerical_address_space(int asn) { char buff[32]; if (!asn) return NULL; sprintf(buff, "", asn); return built_in_ident(buff); } static struct token *attribute_address_space(struct token *token, struct symbol *attr, struct decl_state *ctx) { struct expression *expr = NULL; struct ident *as = NULL; struct token *next; token = expect(token, '(', "after address_space attribute"); switch (token_type(token)) { case TOKEN_NUMBER: next = primary_expression(token, &expr); if (expr->type != EXPR_VALUE) goto invalid; as = numerical_address_space(expr->value); break; case TOKEN_IDENT: next = token->next; as = token->ident; break; default: next = token->next; invalid: as = NULL; warning(token->pos, "invalid address space name"); } if (Waddress_space && as) { if (ctx->ctype.as) sparse_error(token->pos, "multiple address spaces given: %s & %s", show_as(ctx->ctype.as), show_as(as)); ctx->ctype.as = as; } token = expect(next, ')', "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 ? &uint128_ctype : &sint128_ctype; } static struct symbol *to_pointer_mode(struct symbol *ctype) { if (ctype->ctype.base_type != &int_type) return NULL; return ctype->ctype.modifiers & MOD_UNSIGNED ? uintptr_ctype : intptr_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", 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 idx = 0; token = expect(token, '(', "after context attribute"); token = conditional_expression(token, &args[0]); token = expect(token, ',', "after context 1st argument"); token = conditional_expression(token, &args[1]); if (match_op(token, ',')) { token = token->next; token = conditional_expression(token, &args[2]); token = expect(token, ')', "after context 3rd argument"); context->context = args[0]; idx++; } else { token = expect(token, ')', "after context 2nd argument"); } context->in = get_expression_value(args[idx++]); context->out = get_expression_value(args[idx++]); add_ptr_list(&ctx->ctype.contexts, context); 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, "unknown attribute '%s'", 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 symbol *sym, struct decl_state *ctx) { token = expect(token, '(', "after attribute"); token = expect(token, '(', "after attribute"); while (token_type(token) == TOKEN_IDENT) { struct symbol *attr = lookup_keyword(token->ident, 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 unsigned long decl_modifiers(struct decl_state *ctx) { unsigned long mods = ctx->ctype.modifiers & MOD_DECLARE; ctx->ctype.modifiers &= ~MOD_DECLARE; return ctx->storage_class | mods; } static struct token *storage_specifier(struct token *next, struct symbol *sym, struct decl_state *ctx) { int is_tls = ctx->ctype.modifiers & MOD_TLS; unsigned long class = sym->ctype.modifiers; const char *storage = modifier_name(class); /* __thread can be used alone, or with extern or static */ if (is_tls && (class & ~(MOD_STATIC|MOD_EXTERN))) sparse_error(next->pos, "__thread cannot be used with '%s'", storage); else if (!ctx->storage_class) ctx->storage_class = class; else if (ctx->storage_class == class) sparse_error(next->pos, "duplicate %s", storage); else sparse_error(next->pos, "multiple storage classes"); return next; } static struct token *thread_specifier(struct token *next, struct symbol *sym, struct decl_state *ctx) { /* This GCC extension can be used alone, or with extern or static */ if (!(ctx->storage_class & ~(MOD_STATIC|MOD_EXTERN))) { apply_qualifier(&next->pos, &ctx->ctype, MOD_TLS); } else { sparse_error(next->pos, "__thread cannot be used with '%s'", modifier_name(ctx->storage_class)); } return next; } static struct token *attribute_force(struct token *token, struct symbol *attr, struct decl_state *ctx) { ctx->forced = 1; return token; } static struct token *alignas_specifier(struct token *token, struct symbol *sym, 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 *generic_qualifier(struct token *next, struct symbol *sym, struct decl_state *ctx) { apply_qualifier(&next->pos, &ctx->ctype, sym->ctype.modifiers); return next; } static void apply_ctype(struct position pos, struct ctype *dst, struct ctype *src) { unsigned long mod = src->modifiers; if (mod) apply_qualifier(&pos, dst, mod); /* Context */ concat_ptr_list((struct ptr_list *)src->contexts, (struct ptr_list **)&dst->contexts); /* Alignment */ if (src->alignment > dst->alignment) dst->alignment = src->alignment; /* Address space */ if (src->as) dst->as = src->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[] = {&char_ctype, &short_ctype, &int_ctype, &long_ctype, &llong_ctype, &int128_ctype}; static struct symbol * const signed_types[] = {&schar_ctype, &sshort_ctype, &sint_ctype, &slong_ctype, &sllong_ctype, &sint128_ctype}; static struct symbol * const unsigned_types[] = {&uchar_ctype, &ushort_ctype, &uint_ctype, &ulong_ctype, &ullong_ctype, &uint128_ctype}; static struct symbol * const real_types[] = {&float_ctype, &double_ctype, &ldouble_ctype}; static struct symbol * const * const types[] = { [CInt] = int_types + 2, [CSInt] = signed_types + 2, [CUInt] = unsigned_types + 2, [CReal] = real_types + 1, }; 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_keyword(t->ident, NS_TYPEDEF); if (!s) break; if (!(s->op->type & (KW_ATTRIBUTE | KW_QUALIFIER))) break; t = t->next; if (s->op->declarator) t = s->op->declarator(t, s, ctx); } return t; } static struct token *declaration_specifiers(struct token *token, struct decl_state *ctx) { int seen = 0; int class = CInt; int rank = 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, &ctx->ctype, &s->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) rank = 3; else if (s->op->set & Set_Char) rank = -2; if (s->op->set & (Set_Short|Set_Float)) { rank = -1; } else if (s->op->set & Set_Long && rank++) { if (class == CReal) { specifier_conflict(token->pos, Set_Vlong, &double_ident); break; } seen |= Set_Vlong; } } token = token->next; if (s->op->declarator) // Note: this eats attributes token = s->op->declarator(token, s, 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][rank]; ctx->ctype.base_type = base; } if (ctx->ctype.modifiers & MOD_BITWISE) { struct symbol *type; ctx->ctype.modifiers &= ~MOD_BITWISE; if (!is_int_type(ctx->ctype.base_type)) { sparse_error(token->pos, "invalid modifier"); return token; } type = alloc_symbol(token->pos, SYM_BASETYPE); *type = *ctx->ctype.base_type; type->ctype.modifiers &= ~MOD_SPECIFIER; type->ctype.base_type = ctx->ctype.base_type; type->type = SYM_RESTRICT; ctx->ctype.base_type = type; create_fouled(type); } return token; } static struct token *abstract_array_declarator(struct token *token, struct symbol *sym) { struct expression *expr = NULL; int has_static = 0; while (token_type(token) == TOKEN_IDENT) { struct symbol *sym = lookup_keyword(token->ident, NS_TYPEDEF); if (!sym || !(sym->op->type & (KW_STATIC|KW_QUALIFIER))) break; if (has_static && (sym->op->type & KW_STATIC)) sparse_error(token->pos, "duplicate array static declarator"); has_static |= (sym->op->type & KW_STATIC); token = token->next; } if (match_op(token, '*') && match_op(token->next, ']')) { // FIXME: '[*]' is treated like '[]' token = token->next; } else { token = assignment_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 *handle_asm_name(struct token *token, struct decl_state *ctx) { struct expression *expr; struct symbol *keyword; if (token_type(token) != TOKEN_IDENT) return token; keyword = lookup_keyword(token->ident, NS_KEYWORD); if (!keyword) return token; if (!(keyword->op->type & KW_ASM)) return token; token = token->next; token = expect(token, '(', "after asm"); token = string_expression(token, &expr, "asm name"); token = expect(token, ')', "after asm"); return token; } /// // test if @token is '__attribute__' (or one of its variant) static bool match_attribute(struct token *token) { struct symbol *sym; if (token_type(token) != TOKEN_IDENT) return false; sym = lookup_keyword(token->ident, NS_TYPEDEF); if (!sym || !sym->op) return false; return sym->op->type & KW_ATTRIBUTE; } 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) { while (match_attribute(token)) { token = expect(token->next, '(', "after attribute"); token = expect(token, '(', "after attribute"); while (token_type(token) == TOKEN_IDENT) { 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) { while (match_attribute(token)) token = attribute_specifier(token->next, NULL, ctx); 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, ';') || match_op(next->next, ',')) return Empty; if (Wstrict_prototypes) 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); 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); ctype->modifiers |= ctx->f_modifiers; 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 = NULL; 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 || (*ctx->ident && width == 0)) { sparse_error(token->pos, "bitfield '%s' has invalid width (%lld)", show_ident(*ctx->ident), width); 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; bitfield->ident = *ctx->ident; 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 = decl_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); 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); apply_modifiers(token->pos, &ctx); sym->ctype = ctx.ctype; sym->ctype.modifiers |= decl_modifiers(&ctx); sym->endpos = token->pos; sym->forced_arg = ctx.forced; return token; } struct token *typename(struct token *token, struct symbol **p, int *forced) { struct decl_state ctx = {.prefer_abstract = 1}; unsigned long 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 = ctx.forced; if (class) warning(sym->pos, "storage class in typename (%s%s)", modifier_string(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 asm_operand_list **inout) { /* Allow empty operands */ if (match_op(token->next, ':') || match_op(token->next, ')')) return token->next; do { struct asm_operand *op = __alloc_asm_operand(0); if (match_op(token->next, '[') && token_type(token->next->next) == TOKEN_IDENT && match_op(token->next->next->next, ']')) { op->name = token->next->next->ident; token = token->next->next->next; } token = token->next; token = string_expression(token, &op->constraint, "asm constraint"); token = parens_expression(token, &op->expr, "in asm parameter"); add_ptr_list(inout, op); } 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, 1); 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) { unsigned long mods = 0; token = token->next; stmt->type = STMT_ASM; while (token_type(token) == TOKEN_IDENT) { struct symbol *s = lookup_keyword(token->ident, NS_TYPEDEF); if (s && s->op->asm_modifier) s->op->asm_modifier(token, &mods, s->ctype.modifiers); else if (token->ident == &goto_ident) asm_modifier(token, &mods, MOD_ASM_GOTO); token = token->next; } token = expect(token, '(', "after asm"); token = string_expression(token, &stmt->asm_string, "inline asm"); 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 (match_op(token, ':') && (mods & MOD_ASM_GOTO)) 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_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"); if (match_op(token, ',')) { token = token->next; token = string_expression(token, &message, "_Static_assert()"); if (!message) 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) { const char *sep = "", *msg = ""; if (message) { sep = ": "; msg = show_string(message->string); } sparse_error(cond->pos, "static assertion failed%s%s", sep, msg); } 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_block_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_block_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_QUALIFIER | MOD_TLS | MOD_ACCESS | MOD_NOCAST | MOD_NODEREF); 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_block_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_block_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 void warn_label_usage(struct position def, struct position use, struct ident *ident) { const char *id = show_ident(ident); sparse_error(use, "label '%s' used outside statement expression", id); info(def, " label '%s' defined here", id); current_fn->bogus_linear = 1; } void check_label_usage(struct symbol *label, struct position use_pos) { struct statement *def = label->stmt; if (def) { if (!is_in_scope(def->label_scope, label_scope)) warn_label_usage(def->pos, use_pos, label->ident); } else if (!label->label_scope) { label->label_scope = label_scope; label->label_pos = use_pos; } } 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) { struct symbol *label = label_symbol(token, 1); stmt->goto_label = label; check_label_usage(label, stmt->pos); 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 = token->next; token = expect(token, '(', "after __context__ statement"); token = assignment_expression(token, &stmt->expression); if (!stmt->expression) unexpected(token, "expression expected after '('"); if (match_op(token, ',')) { token = token->next; stmt->context = stmt->expression; token = assignment_expression(token, &stmt->expression); if (!stmt->expression) unexpected(token, "expression expected after ','"); } token = expect(token, ')', "at end of __context__ statement"); return expect(token, ';', "at end of statement"); } static struct token *parse_range_statement(struct token *token, struct statement *stmt) { stmt->type = STMT_RANGE; token = token->next; token = expect(token, '(', "after __range__ statement"); token = assignment_expression(token, &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); token = expect(token, ')', "after range statement"); return expect(token, ';', "after range statement"); } static struct token *handle_label_attributes(struct token *token, struct symbol *label) { struct decl_state ctx = { }; token = handle_attributes(token, &ctx); label->label_modifiers = ctx.ctype.modifiers; return token; } 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, 0); token = handle_label_attributes(token->next->next, s); if (s->stmt) { sparse_error(stmt->pos, "label '%s' redefined", show_ident(s->ident)); // skip the label to avoid multiple definitions return statement(token, tree); } stmt->type = STMT_LABEL; stmt->label_identifier = s; stmt->label_scope = label_scope; if (s->label_scope) { if (!is_in_scope(label_scope, s->label_scope)) warn_label_usage(stmt->pos, s->label_pos, s->ident); } s->stmt = stmt; if (match_op(token, '}')) { warning(token->pos, "statement expected after label"); stmt->label_statement = alloc_statement(token->pos, STMT_NONE); return token; } return statement(token, &stmt->label_statement); } } if (match_op(token, '{')) { token = compound_statement(token->next, stmt); 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_with_scope(sym, token->ident, NS_LABEL, block_scope); 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) { stmt->type = STMT_COMPOUND; start_block_scope(); token = statement_list(token, &stmt->stmts); end_block_scope(); 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; if (!Wuniversal_initializer) { struct token *next = token->next; // '{ 0 }' is equivalent to '{ }' except for some // warnings, like using 0 to initialize a null-pointer. if (match_token_zero(next)) { if (match_op(next->next, '}')) expr->zero_init = 1; } } 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; } if (sym->ctype.base_type == &incomplete_ctype) { sym->ctype.base_type = &int_ctype; if (Wimplicit_int) { sparse_error(sym->pos, "missing type declaration for parameter '%s'", show_ident(sym->ident)); } } 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|MOD_INLINE)) == MOD_EXTERN) { if (Wexternal_function_has_definition) 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 = statement_list(token->next, &stmt->stmts); 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->rank < 0) { 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); if (Wimplicit_int) { warning(arg->pos, "missing type declaration for parameter '%s'", show_ident(arg->ident)); } type = alloc_symbol(arg->pos, SYM_NODE); type->ident = arg->ident; type->ctype.base_type = &int_ctype; 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; if (Wold_style_definition) 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); // FIXME: 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 = decl_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_asm_name(token, &ctx); token = handle_attributes(token, &ctx); 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 == MOD_USERTYPE; /* 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 == &autotype_ctype) { sparse_error(decl->pos, "'%s()' has __auto_type return type", show_ident(decl->ident)); base_type->ctype.base_type = &int_ctype; } 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; } /* apply attributes placed after the declarator */ decl->ctype.modifiers |= ctx.f_modifiers; /* 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, '=')) { struct token *next = token->next; token = initializer(&decl->initializer, next); if (token == next) sparse_error(token->pos, "expression expected before '%s'", show_token(token)); } 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 (is_typedef) { // TODO: handle -std=c89 --pedantic check_duplicates(decl); } } if (ctx.autotype) { const char *msg = NULL; if (decl->ctype.base_type != &autotype_ctype) msg = "on non-identifier"; else if (match_op(token, ',')) msg = "on declaration list"; else if (!decl->initializer) msg = "without initializer"; else if (decl->initializer->type == EXPR_SYMBOL && decl->initializer->symbol == decl) msg = "on self-init var"; if (msg) { sparse_error(decl->pos, "__auto_type %s", msg); decl->ctype.base_type = &bad_ctype; } } 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); token = declarator(token, &ctx); token = handle_asm_name(token, &ctx); token = handle_attributes(token, &ctx); 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.6.4/parse.dtd000066400000000000000000000031471411531012200146200ustar00rootroot00000000000000 sparse-0.6.4/parse.h000066400000000000000000000105601411531012200142710ustar00rootroot00000000000000#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 scope *label_scope; 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 asm_operand_list *asm_outputs; struct asm_operand_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, int used); extern void check_label_usage(struct symbol *label, struct position use_pos); 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 int inline_function(struct expression *expr, struct symbol *sym); extern void uninline(struct symbol *sym); extern void init_parser(int); struct token *expect(struct token *, int, const char *); #endif /* PARSE_H */ sparse-0.6.4/pre-process.c000066400000000000000000001565251411531012200154300ustar00rootroot00000000000000/* * 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 static int include_level = 0; static int expanding = 0; #define INCLUDEPATHS 300 const char *includepath[INCLUDEPATHS+1] = { "", "/usr/include", "/usr/local/include", NULL }; static const char **quote_includepath = includepath; static const char **angle_includepath = includepath + 1; static const char **isys_includepath = includepath + 1; static const char **sys_includepath = includepath + 1; static const char **dirafter_includepath = includepath + 3; #define dirty_stream(stream) \ do { \ if (!stream->dirty) { \ stream->dirty = 1; \ if (!stream->ifndef) \ stream->protect = NULL; \ } \ } while(0) #define end_group(stream) \ do { \ if (stream->ifndef == stream->top_if) { \ stream->ifndef = NULL; \ if (!stream->dirty) \ stream->protect = NULL; \ else if (stream->protect) \ stream->dirty = 0; \ } \ } while(0) #define nesting_error(stream) \ do { \ stream->dirty = 1; \ stream->ifndef = NULL; \ stream->protect = NULL; \ } while(0) static struct token *alloc_token(struct position *pos) { struct token *token = __alloc_token(0); token->pos.stream = pos->stream; token->pos.line = pos->line; token->pos.pos = pos->pos; token->pos.whitespace = 1; return token; } /* 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_bool(struct token *token, bool val) { static const char *string[] = { "0", "1" }; token_type(token) = TOKEN_NUMBER; token->number = string[val]; } static void replace_with_defined(struct token *token) { replace_with_bool(token, token_defined(token)); } static void expand_line(struct token *token) { replace_with_integer(token, token->pos.line); } static void expand_file(struct token *token) { replace_with_string(token, stream_name(token->pos.stream)); } static void expand_basefile(struct token *token) { replace_with_string(token, base_filename); } static time_t t = 0; static void expand_date(struct token *token) { static char buffer[12]; /* __DATE__: 3 + ' ' + 2 + ' ' + 4 + '\0' */ if (!t) time(&t); strftime(buffer, 12, "%b %e %Y", localtime(&t)); replace_with_string(token, buffer); } static void expand_time(struct token *token) { static char buffer[9]; /* __TIME__: 2 + ':' + 2 + ':' + 2 + '\0' */ if (!t) time(&t); strftime(buffer, 9, "%T", localtime(&t)); replace_with_string(token, buffer); } static void expand_counter(struct token *token) { replace_with_integer(token, counter_macro++); } static void expand_include_level(struct token *token) { replace_with_integer(token, include_level - 1); } static int expand_one_symbol(struct token **list) { struct token *token = *list; struct symbol *sym; if (token->pos.noexpand) return 1; sym = lookup_macro(token->ident); if (!sym) return 1; if (sym->expand_simple) { sym->expand_simple(token); return 1; } else { int rc; sym->used_in = file_scope; expanding = 1; rc = expand(list, sym); expanding = 0; return rc; } } 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) { 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; next->pos.newline = 0; 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); if (eof_token(next)) goto Eclosing; if (p->vararg && wanted == 1 && eof_token(start->next)) break; arglist = arglist->next->next; 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: token_type(left) = TOKEN_NUMBER; /* could be . + num */ left->number = xstrdup(buffer); 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; struct token *expansion = sym->expansion; 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); } if (sym->expand) return sym->expand(token, args) ? 0 : 1; expanding->tainted = 1; last = token->next; tail = substitute(list, 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; /* normalize this path */ while (path[0] == '.' && path[1] == '/') { path += 2; while (path[0] == '/') path++; } } stream->path = path; } includepath[0] = path; } static int try_include(struct position pos, 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 = xmemdup(fullname, plen + flen); *where = tokenize(&pos, 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(token->pos, 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(token->pos, "", 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_define(struct position pos, struct token *token, struct ident *name, struct token *arglist, struct token *expansion, int attr) { struct symbol *sym; int ret = 1; expansion = parse_expansion(expansion, arglist, name); if (!expansion) return 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(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(pos, SYM_NODE); bind_symbol(sym, name, NS_MACRO); add_ident(¯os, name); ret = 0; } if (!ret) { sym->expansion = expansion; sym->arglist = arglist; if (token) /* Free the "define" token, but not the rest of the line */ __free_token(token); } sym->namespace = NS_MACRO; sym->used_in = NULL; sym->attr = attr; out: return ret; } /// // predefine a macro with a printf-formatted value // @name: the name of the macro // @weak: 0/1 for a normal or a weak define // @fmt: the printf format followed by it's arguments. // // The type of the value is automatically infered: // TOKEN_NUMBER if it starts by a digit, TOKEN_IDENT otherwise. // If @fmt is null or empty, the macro is defined with an empty definition. void predefine(const char *name, int weak, const char *fmt, ...) { struct ident *ident = built_in_ident(name); struct token *value = &eof_token_entry; int attr = weak ? SYM_ATTR_WEAK : SYM_ATTR_NORMAL; if (fmt && fmt[0]) { static char buf[256]; va_list ap; va_start(ap, fmt); vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); value = __alloc_token(0); if (isdigit((unsigned char)buf[0])) { token_type(value) = TOKEN_NUMBER; value->number = xstrdup(buf); } else { token_type(value) = TOKEN_IDENT; value->ident = built_in_ident(buf); } value->pos.whitespace = 1; value->next = &eof_token_entry; } do_define(value->pos, NULL, ident, NULL, value, attr); } /// // like predefine() but only if one of the non-standard dialect is chosen void predefine_nostd(const char *name) { if ((standard & STANDARD_GNU) || (standard == STANDARD_NONE)) predefine(name, 1, "1"); } static void predefine_fmt(const char *fmt, int weak, va_list ap) { static char buf[256]; vsnprintf(buf, sizeof(buf), fmt, ap); predefine(buf, weak, "1"); } void predefine_strong(const char *fmt, ...) { va_list ap; va_start(ap, fmt); predefine_fmt(fmt, 0, ap); va_end(ap); } void predefine_weak(const char *fmt, ...) { va_list ap; va_start(ap, fmt); predefine_fmt(fmt, 1, ap); va_end(ap); } 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 ident *name; 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"); } } return do_define(left->pos, token, name, arglist, expansion, attr); } 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 cond) { 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 || cond != 1) false_nesting++; return 0; } static int handle_ifdef(struct stream *stream, struct token **line, struct token *token) { struct token *next = token->next; int arg; if (token_type(next) == TOKEN_IDENT) { arg = token_defined(next); } else { dirty_stream(stream); if (!false_nesting) sparse_error(token->pos, "expected preprocessor identifier"); arg = -1; } return preprocessor_if(stream, token, arg); } static int handle_ifndef(struct stream *stream, struct token **line, struct token *token) { struct token *next = token->next; int arg; if (token_type(next) == TOKEN_IDENT) { if (!stream->dirty && !stream->ifndef) { if (!stream->protect) { stream->ifndef = token; stream->protect = next->ident; } else if (stream->protect == next->ident) { stream->ifndef = token; stream->dirty = 1; } } arg = !token_defined(next); } else { dirty_stream(stream); if (!false_nesting) sparse_error(token->pos, "expected preprocessor identifier"); arg = -1; } return preprocessor_if(stream, token, arg); } /* * Expression handling for #if and #elif; it differs from normal expansion * due to special treatment of "defined". */ static int expression_value(struct token **where) { struct expression *expr; struct token *p; struct token **list = where, **beginning = NULL; long long value; int state = 0; while (!eof_token(p = scan_next(list))) { switch (state) { case 0: if (token_type(p) != TOKEN_IDENT) break; if (p->ident == &defined_ident) { state = 1; beginning = list; break; } if (!expand_one_symbol(list)) continue; if (token_type(p) != TOKEN_IDENT) break; token_type(p) = TOKEN_ZERO_IDENT; break; case 1: if (match_op(p, '(')) { state = 2; } else { state = 0; replace_with_defined(p); *beginning = p; } break; case 2: if (token_type(p) == TOKEN_IDENT) state = 3; else state = 0; replace_with_defined(p); *beginning = p; break; case 3: state = 0; if (!match_op(p, ')')) sparse_error(p->pos, "missing ')' after \"defined\""); *list = p->next; continue; } list = &p->next; } p = constant_expression(*where, &expr); if (!eof_token(p)) sparse_error(p->pos, "garbage at end: %s", show_token_sequence(p, 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_ident(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 bool expand_has_attribute(struct token *token, struct arg *args) { struct token *arg = args[0].expanded; struct symbol *sym; if (token_type(arg) != TOKEN_IDENT) { sparse_error(arg->pos, "identifier expected"); return false; } sym = lookup_symbol(arg->ident, NS_KEYWORD); replace_with_bool(token, sym && sym->op && sym->op->attribute); return true; } static bool expand_has_builtin(struct token *token, struct arg *args) { struct token *arg = args[0].expanded; struct symbol *sym; if (token_type(arg) != TOKEN_IDENT) { sparse_error(arg->pos, "identifier expected"); return false; } sym = lookup_symbol(arg->ident, NS_SYMBOL); replace_with_bool(token, sym && sym->builtin); return true; } static bool expand_has_extension(struct token *token, struct arg *args) { struct token *arg = args[0].expanded; struct ident *ident; bool val = false; if (token_type(arg) != TOKEN_IDENT) { sparse_error(arg->pos, "identifier expected"); return false; } ident = arg->ident; if (ident == &c_alignas_ident) val = true; else if (ident == &c_alignof_ident) val = true; else if (ident == &c_generic_selections_ident) val = true; else if (ident == &c_static_assert_ident) val = true; replace_with_bool(token, val); return 1; } static bool expand_has_feature(struct token *token, struct arg *args) { struct token *arg = args[0].expanded; struct ident *ident; bool val = false; if (token_type(arg) != TOKEN_IDENT) { sparse_error(arg->pos, "identifier expected"); return false; } ident = arg->ident; if (standard >= STANDARD_C11) { if (ident == &c_alignas_ident) val = true; else if (ident == &c_alignof_ident) val = true; else if (ident == &c_generic_selections_ident) val = true; else if (ident == &c_static_assert_ident) val = true; } replace_with_bool(token, val); return 1; } static void create_arglist(struct symbol *sym, int count) { struct token *token; struct token **next; if (!count) return; token = __alloc_token(0); token_type(token) = TOKEN_ARG_COUNT; token->count.normal = count; sym->arglist = token; next = &token->next; while (count--) { struct token *id, *uses; id = __alloc_token(0); token_type(id) = TOKEN_IDENT; uses = __alloc_token(0); token_type(uses) = TOKEN_ARG_COUNT; uses->count.normal = 1; *next = id; id->next = uses; next = &uses->next; } *next = &eof_token_entry; } static void init_preprocessor(void) { int i; int stream = init_stream(NULL, "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 }, { "ident", handle_ident }, // 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 }, }; static struct { const char *name; void (*expand_simple)(struct token *); bool (*expand)(struct token *, struct arg *args); } dynamic[] = { { "__LINE__", expand_line }, { "__FILE__", expand_file }, { "__BASE_FILE__", expand_basefile }, { "__DATE__", expand_date }, { "__TIME__", expand_time }, { "__COUNTER__", expand_counter }, { "__INCLUDE_LEVEL__", expand_include_level }, { "__has_attribute", NULL, expand_has_attribute }, { "__has_builtin", NULL, expand_has_builtin }, { "__has_extension", NULL, expand_has_extension }, { "__has_feature", NULL, expand_has_feature }, }; 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; } for (i = 0; i < ARRAY_SIZE(dynamic); i++) { struct symbol *sym; sym = create_symbol(stream, dynamic[i].name, SYM_NODE, NS_MACRO); sym->expand_simple = dynamic[i].expand_simple; if ((sym->expand = dynamic[i].expand) != NULL) create_arglist(sym, 1); } 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; int is_cond = 0; // is one of {is,ifdef,ifndef,elif,else,endif} 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; is_cond = !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 (expanding) { if (!is_cond || Wpedantic) warning(start->pos, "directive in macro's argument list"); } 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; include_level--; continue; case TOKEN_STREAMBEGIN: *list = next->next; include_level++; 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 int is_VA_ARGS_token(struct token *token) { return (token_type(token) == TOKEN_IDENT) && (token->ident == &__VA_ARGS___ident); } 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; if (is_VA_ARGS_token(token)) printf("%s...", sep); else printf("%s%s", sep, show_token(token)); args[narg++] = token; sep = ","; } putchar(')'); } token = sym->expansion; while (token_type(token) != TOKEN_UNTAINT) { struct token *next = token->next; if (token->pos.whitespace) putchar(' '); switch (token_type(token)) { case TOKEN_CONCAT: printf("##"); break; case TOKEN_STR_ARGUMENT: printf("#"); /* fall-through */ case TOKEN_QUOTED_ARGUMENT: case TOKEN_MACRO_ARGUMENT: token = args[token->argnum]; /* fall-through */ default: printf("%s", show_token(token)); } 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.6.4/predefine.c000066400000000000000000000222711411531012200151150ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // Copyright (C) 2017-2020 Luc Van Oostenryck. #include #include "lib.h" #include "machine.h" #include "symbol.h" #define PTYPE_SIZEOF (1U << 0) #define PTYPE_T (1U << 1) #define PTYPE_MAX (1U << 2) #define PTYPE_MIN (1U << 3) #define PTYPE_WIDTH (1U << 4) #define PTYPE_TYPE (1U << 5) #define PTYPE_ALL (PTYPE_MAX|PTYPE_SIZEOF|PTYPE_WIDTH) #define PTYPE_ALL_T (PTYPE_MAX|PTYPE_SIZEOF|PTYPE_WIDTH|PTYPE_T) static void predefined_sizeof(const char *name, const char *suffix, unsigned bits) { char buf[32]; snprintf(buf, sizeof(buf), "__SIZEOF_%s%s__", name, suffix); predefine(buf, 1, "%d", bits/8); } static void predefined_width(const char *name, struct symbol *type) { char buf[32]; snprintf(buf, sizeof(buf), "__%s_WIDTH__", name); predefine(buf, 1, "%d", type->bit_size); } static void predefined_max(const char *name, struct symbol *type) { const char *suffix = builtin_type_suffix(type); unsigned bits = type->bit_size - is_signed_type(type); unsigned long long max = bits_mask(bits); char buf[32]; snprintf(buf, sizeof(buf), "__%s_MAX__", name); predefine(buf, 1, "%#llx%s", max, suffix); } static void predefined_min(const char *name, struct symbol *type) { const char *suffix = builtin_type_suffix(type); char buf[32]; snprintf(buf, sizeof(buf), "__%s_MIN__", name); if (is_signed_type(type)) add_pre_buffer("#weak_define %s (-__%s_MAX__ - 1)\n", buf, name); else predefine(buf, 1, "0%s", suffix); } static void predefined_type(const char *name, struct symbol *type) { const char *typename = builtin_typename(type); add_pre_buffer("#weak_define __%s_TYPE__ %s\n", name, typename); } static void predefined_ctype(const char *name, struct symbol *type, int flags) { unsigned bits = type->bit_size; if (flags & PTYPE_SIZEOF) { const char *suffix = (flags & PTYPE_T) ? "_T" : ""; predefined_sizeof(name, suffix, bits); } if (flags & PTYPE_MAX) predefined_max(name, type); if (flags & PTYPE_MIN) predefined_min(name, type); if (flags & PTYPE_TYPE) predefined_type(name, type); if (flags & PTYPE_WIDTH) predefined_width(name, type); } void predefined_macros(void) { predefine("__CHECKER__", 0, "1"); predefine("__GNUC__", 1, "%d", gcc_major); predefine("__GNUC_MINOR__", 1, "%d", gcc_minor); predefine("__GNUC_PATCHLEVEL__", 1, "%d", gcc_patchlevel); predefine("__STDC__", 1, "1"); predefine("__STDC_HOSTED__", 0, fhosted ? "1" : "0"); switch (standard) { default: break; case STANDARD_C94: predefine("__STDC_VERSION__", 1, "199409L"); break; case STANDARD_C99: case STANDARD_GNU99: predefine("__STDC_VERSION__", 1, "199901L"); break; case STANDARD_C11: case STANDARD_GNU11: predefine("__STDC_VERSION__", 1, "201112L"); break; case STANDARD_C17: case STANDARD_GNU17: predefine("__STDC_VERSION__", 1, "201710L"); break; } if (!(standard & STANDARD_GNU) && (standard != STANDARD_NONE)) predefine("__STRICT_ANSI__", 1, "1"); if (standard >= STANDARD_C11) { predefine("__STDC_NO_ATOMICS__", 1, "1"); predefine("__STDC_NO_COMPLEX__", 1, "1"); predefine("__STDC_NO_THREADS__", 1, "1"); } predefine("__CHAR_BIT__", 1, "%d", bits_in_char); if (funsigned_char) predefine("__CHAR_UNSIGNED__", 1, "1"); predefined_ctype("SHORT", &short_ctype, PTYPE_SIZEOF); predefined_ctype("SHRT", &short_ctype, PTYPE_MAX|PTYPE_WIDTH); predefined_ctype("SCHAR", &schar_ctype, PTYPE_MAX|PTYPE_WIDTH); predefined_ctype("WCHAR", wchar_ctype, PTYPE_ALL_T|PTYPE_MIN|PTYPE_TYPE); predefined_ctype("WINT", wint_ctype, PTYPE_ALL_T|PTYPE_MIN|PTYPE_TYPE); predefined_ctype("CHAR16", &ushort_ctype, PTYPE_TYPE); predefined_ctype("CHAR32", uint32_ctype, PTYPE_TYPE); predefined_ctype("INT", &int_ctype, PTYPE_ALL); predefined_ctype("LONG", &long_ctype, PTYPE_ALL); predefined_ctype("LONG_LONG", &llong_ctype, PTYPE_ALL); predefined_ctype("INT8", &schar_ctype, PTYPE_MAX|PTYPE_TYPE); predefined_ctype("UINT8", &uchar_ctype, PTYPE_MAX|PTYPE_TYPE); predefined_ctype("INT16", &short_ctype, PTYPE_MAX|PTYPE_TYPE); predefined_ctype("UINT16", &ushort_ctype, PTYPE_MAX|PTYPE_TYPE); predefined_ctype("INT32", int32_ctype, PTYPE_MAX|PTYPE_TYPE); predefined_ctype("UINT32", uint32_ctype, PTYPE_MAX|PTYPE_TYPE); predefined_ctype("INT64", int64_ctype, PTYPE_MAX|PTYPE_TYPE); predefined_ctype("UINT64", uint64_ctype, PTYPE_MAX|PTYPE_TYPE); predefined_ctype("INT_LEAST8", &schar_ctype, PTYPE_MAX|PTYPE_TYPE|PTYPE_WIDTH); predefined_ctype("UINT_LEAST8", &uchar_ctype, PTYPE_MAX|PTYPE_TYPE); predefined_ctype("INT_LEAST16", &short_ctype, PTYPE_MAX|PTYPE_TYPE|PTYPE_WIDTH); predefined_ctype("UINT_LEAST16",&ushort_ctype, PTYPE_MAX|PTYPE_TYPE); predefined_ctype("INT_LEAST32", int32_ctype, PTYPE_MAX|PTYPE_TYPE|PTYPE_WIDTH); predefined_ctype("UINT_LEAST32", uint32_ctype, PTYPE_MAX|PTYPE_TYPE); predefined_ctype("INT_LEAST64", int64_ctype, PTYPE_MAX|PTYPE_TYPE|PTYPE_WIDTH); predefined_ctype("UINT_LEAST64", uint64_ctype, PTYPE_MAX|PTYPE_TYPE); predefined_ctype("INT_FAST8", fast8_ctype, PTYPE_MAX|PTYPE_TYPE|PTYPE_WIDTH); predefined_ctype("UINT_FAST8", ufast8_ctype, PTYPE_MAX|PTYPE_TYPE); predefined_ctype("INT_FAST16", fast16_ctype, PTYPE_MAX|PTYPE_TYPE|PTYPE_WIDTH); predefined_ctype("UINT_FAST16",ufast16_ctype, PTYPE_MAX|PTYPE_TYPE); predefined_ctype("INT_FAST32", fast32_ctype, PTYPE_MAX|PTYPE_TYPE|PTYPE_WIDTH); predefined_ctype("UINT_FAST32",ufast32_ctype, PTYPE_MAX|PTYPE_TYPE); predefined_ctype("INT_FAST64", fast64_ctype, PTYPE_MAX|PTYPE_TYPE|PTYPE_WIDTH); predefined_ctype("UINT_FAST64",ufast64_ctype, PTYPE_MAX|PTYPE_TYPE); predefined_ctype("INTMAX", intmax_ctype, PTYPE_MAX|PTYPE_TYPE|PTYPE_WIDTH); predefined_ctype("UINTMAX", uintmax_ctype, PTYPE_MAX|PTYPE_TYPE); predefined_ctype("INTPTR", intptr_ctype, PTYPE_MAX|PTYPE_TYPE|PTYPE_WIDTH); predefined_ctype("UINTPTR", uintptr_ctype, PTYPE_MAX|PTYPE_TYPE); predefined_ctype("PTRDIFF", ptrdiff_ctype, PTYPE_ALL_T|PTYPE_TYPE); predefined_ctype("SIZE", size_t_ctype, PTYPE_ALL_T|PTYPE_TYPE); predefined_ctype("POINTER", &ptr_ctype, PTYPE_SIZEOF); predefined_ctype("SIG_ATOMIC", sig_atomic_ctype, PTYPE_MAX|PTYPE_MIN|PTYPE_TYPE|PTYPE_WIDTH); predefined_sizeof("FLOAT", "", bits_in_float); predefined_sizeof("DOUBLE", "", bits_in_double); predefined_sizeof("LONG_DOUBLE", "", bits_in_longdouble); if (arch_target->has_int128) predefined_sizeof("INT128", "", 128); predefine("__ATOMIC_RELAXED", 0, "0"); predefine("__ATOMIC_CONSUME", 0, "1"); predefine("__ATOMIC_ACQUIRE", 0, "3"); predefine("__ATOMIC_RELEASE", 0, "4"); predefine("__ATOMIC_ACQ_REL", 0, "7"); predefine("__ATOMIC_SEQ_CST", 0, "8"); predefine("__ORDER_LITTLE_ENDIAN__", 1, "1234"); predefine("__ORDER_BIG_ENDIAN__", 1, "4321"); predefine("__ORDER_PDP_ENDIAN__", 1, "3412"); if (arch_big_endian) { predefine("__BIG_ENDIAN__", 1, "1"); predefine("__BYTE_ORDER__", 1, "__ORDER_BIG_ENDIAN__"); } else { predefine("__LITTLE_ENDIAN__", 1, "1"); predefine("__BYTE_ORDER__", 1, "__ORDER_LITTLE_ENDIAN__"); } if (optimize_level) predefine("__OPTIMIZE__", 0, "1"); if (optimize_size) predefine("__OPTIMIZE_SIZE__", 0, "1"); predefine("__PRAGMA_REDEFINE_EXTNAME", 1, "1"); // Temporary hacks predefine("__extension__", 0, NULL); predefine("__pragma__", 0, NULL); switch (arch_m64) { case ARCH_LP32: break; case ARCH_X32: predefine("__ILP32__", 1, "1"); predefine("_ILP32", 1, "1"); break; case ARCH_LP64: predefine("__LP64__", 1, "1"); predefine("_LP64", 1, "1"); break; case ARCH_LLP64: predefine("__LLP64__", 1, "1"); break; } if (fpic) { predefine("__pic__", 0, "%d", fpic); predefine("__PIC__", 0, "%d", fpic); } if (fpie) { predefine("__pie__", 0, "%d", fpie); predefine("__PIE__", 0, "%d", fpie); } if (arch_target->predefine) arch_target->predefine(arch_target); if (arch_os >= OS_UNIX && arch_os != OS_DARWIN) { predefine("__unix__", 1, "1"); predefine("__unix", 1, "1"); predefine_nostd("unix"); } switch (arch_os) { case OS_CYGWIN: predefine("__CYGWIN__", 1, "1"); if (arch_m64 == ARCH_LP32) predefine("__CYGWIN32__", 1, "1"); add_pre_buffer("#define __cdecl __attribute__((__cdecl__))\n"); add_pre_buffer("#define __declspec(x) __attribute__((x))\n"); add_pre_buffer("#define __fastcall __attribute__((__fastcall__))\n"); add_pre_buffer("#define __stdcall __attribute__((__stdcall__))\n"); add_pre_buffer("#define __thiscall __attribute__((__thiscall__))\n"); add_pre_buffer("#define _cdecl __attribute__((__cdecl__))\n"); add_pre_buffer("#define _fastcall __attribute__((__fastcall__))\n"); add_pre_buffer("#define _stdcall __attribute__((__stdcall__))\n"); add_pre_buffer("#define _thiscall __attribute__((__thiscall__))\n"); break; case OS_DARWIN: predefine("__APPLE__", 1, "1"); predefine("__APPLE_CC__", 1, "1"); predefine("__MACH__", 1, "1"); break; case OS_FREEBSD: predefine("__FreeBSD__", 1, "1"); break; case OS_LINUX: predefine("__linux__", 1, "1"); predefine("__linux", 1, "1"); break; case OS_NETBSD: predefine("__NetBSD__", 1, "1"); break; case OS_OPENBSD: predefine("__OpenBSD__", 1, "1"); break; case OS_SUNOS: predefine("__sun__", 1, "1"); predefine("__sun", 1, "1"); predefine_nostd("sun"); predefine("__svr4__", 1, "1"); break; } } sparse-0.6.4/ptrlist.c000066400000000000000000000262441411531012200146610ustar00rootroot00000000000000/* * ptrlist.c * * (C) Copyright Linus Torvalds 2003-2005 */ /// // Pointer list manipulation // ------------------------- // // The data structure handled here is designed to hold pointers // but two special cases need to be avoided or need special care: // * NULL is used by {PREPARE,NEXT}_PTR_LIST() to indicate the end-of-list. // Thus, NULL can't be stored in lists using this API but is fine to // use with FOR_EACH_PTR() and its variants. // * VOID is used to replace a removed pseudo 'usage'. Since phi-nodes // (OP_PHI) use a list to store their operands, a VOID in a phi-node // list must be ignored since it represents a removed operand. As // consequence, VOIDs must never be used as phi-node operand. // This is fine since phi-nodes make no sense with void values // but VOID is also used for invalid types and in case of errors. #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); /// // get the size of a ptrlist // @head: the head of the list // @return: the size of the list given by @head. 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; } /// // test if a list is empty // @head: the head of the list // @return: ``true`` if the list is empty, ``false`` otherwise. bool ptr_list_empty(const struct ptr_list *head) { const struct ptr_list *list = head; if (!head) return true; do { if (list->nr - list->rm) return false; } while ((list = list->next) != head); return true; } /// // test is a list contains more than one element // @head: the head of the list // @return: ``true`` if the list has more than 1 element, ``false`` otherwise. bool ptr_list_multiple(const struct ptr_list *head) { const struct ptr_list *list = head; int nr = 0; if (!head) return false; do { nr += list->nr - list->rm; if (nr > 1) return true; } while ((list = list->next) != head); return false; } /// // get the first element of a ptrlist // @head: the head of the list // @return: the first element of the list or ``NULL`` if the list is empty void *first_ptr_list(struct ptr_list *head) { struct ptr_list *list = head; if (!head) return NULL; while (list->nr == 0) { list = list->next; if (list == head) return NULL; } return PTR_ENTRY_NOTAG(list, 0); } /// // get the last element of a ptrlist // @head: the head of the list // @return: the last element of the list or ``NULL`` if the list is empty void *last_ptr_list(struct ptr_list *head) { struct ptr_list *list; if (!head) return NULL; list = head->prev; while (list->nr == 0) { if (list == head) return NULL; list = list->prev; } return PTR_ENTRY_NOTAG(list, list->nr-1); } /// // get the nth element of a ptrlist // @head: the head of the list // @return: the nth element of the list or ``NULL`` if the list is too short. void *ptr_list_nth_entry(struct ptr_list *list, unsigned int idx) { struct ptr_list *head = list; if (!head) return NULL; do { unsigned int nr = list->nr; if (idx < nr) return list->list[idx]; else idx -= nr; } while ((list = list->next) != head); return NULL; } /// // linearize the entries of a list // // @head: the list to be linearized // @arr: a ``void*`` array to fill with @head's entries // @max: the maximum number of entries to store into @arr // @return: the number of entries in the list. // // Linearize the entries of a list up to a total of @max, // and return the number of entries in the list. // // The array to linearize into (@arr) 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; nr += i; if (max == 0) continue; if (i > max) i = max; memcpy(arr, list->list, i*sizeof(void *)); arr += i; max -= i; } while ((list = list->next) != head); } return nr; } /// // pack a ptrlist // // @listp: a pointer to the list to be packed. // // 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); } } /// // split a ptrlist block // @head: the ptrlist block to be split // // A new block is inserted just after @head and the entries // at the half end of @head are moved to this new block. // The goal being to create space inside @head for a new entry. 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 *)); } /// // add an entry to a ptrlist // @listp: a pointer to the list // @ptr: the entry to add to the list // @return: the address where the new entry is stored. // // :note: code must not use this function and should use // :func:`add_ptr_list` instead. void **__add_ptr_list(struct ptr_list **listp, void *ptr) { struct ptr_list *list = *listp; struct ptr_list *last = NULL; /* gcc complains needlessly */ void **ret; int nr; 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; } /// // add a tagged entry to a ptrlist // @listp: a pointer to the list // @ptr: the entry to add to the list // @tag: the tag to add to @ptr // @return: the address where the new entry is stored. // // :note: code must not use this function and should use // :func:`add_ptr_list_tag` instead. void **__add_ptr_list_tag(struct ptr_list **listp, void *ptr, unsigned long tag) { /* The low two bits are reserved for tags */ assert((3 & (unsigned long)ptr) == 0); assert((~3 & tag) == 0); ptr = (void *)(tag | (unsigned long)ptr); return __add_ptr_list(listp, ptr); } /// // test if some entry is already present in a ptrlist // @list: the head of the list // @entry: the entry to test // @return: ``true`` if the entry is already present, ``false`` otherwise. bool lookup_ptr_list_entry(const struct ptr_list *head, const void *entry) { const struct ptr_list *list = head; if (!head) return false; do { int nr = list->nr; int i; for (i = 0; i < nr; i++) if (list->list[i] == entry) return true; } while ((list = list->next) != head); return false; } /// // delete an entry from a ptrlist // @list: a pointer to the list // @entry: the item to be deleted // @count: the minimum number of times @entry should be deleted or 0. 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; } /// // replace an entry in a ptrlist // @list: a pointer to the list // @old_ptr: the entry to be replaced // @new_ptr: the new entry // @count: the minimum number of times @entry should be deleted or 0. 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; } /// // remove the last entry of a ptrlist // @head: a pointer to the list // @return: the last element of the list or NULL if the list is empty. // // :note: this doesn't repack the 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; } /// // remove the last entry and repack the list // @head: a pointer to the list // @return: the last element of the list or NULL if the list is empty. 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; } /// // concat two ptrlists // @a: the source list // @b: a pointer to the destination list. // The element of @a are added at the end of @b. 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); } /// // copy the elements of a list at the end of another list. // @listp: a pointer to the destination list. // @src: the head of the source list. void copy_ptr_list(struct ptr_list **listp, struct ptr_list *src) { struct ptr_list *head, *tail; struct ptr_list *cur = src; int idx; if (!src) return; head = *listp; if (!head) { *listp = src; return; } tail = head->prev; idx = tail->nr; do { struct ptr_list *next; int nr = cur->nr; int i; for (i = 0; i < nr;) { void *ptr = cur->list[i++]; if (!ptr) continue; if (idx >= LIST_NODE_NR) { struct ptr_list *prev = tail; tail = __alloc_ptrlist(0); prev->next = tail; tail->prev = prev; prev->nr = idx; idx = 0; } tail->list[idx++] = ptr; } next = cur->next; __free_ptrlist(cur); cur = next; } while (cur != src); tail->nr = idx; head->prev = tail; tail->next = head; } /// // free a ptrlist // @listp: a pointer to the list // Each blocks of the list are freed (but the entries // themselves are not freed). // // :note: code must not use this function and should use // the macro :func:`free_ptr_list` instead. 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.6.4/ptrlist.h000066400000000000000000000230301411531012200146540ustar00rootroot00000000000000#ifndef PTR_LIST_H #define PTR_LIST_H #include #include /* * Generic pointer list manipulation code. * * (C) Copyright Linus Torvalds 2003-2005 */ /* Silly type-safety check ;) */ #define CHECK_TYPE(head,ptr) (void)(&(ptr) == &(head)->list[0]) #define PTRLIST_TYPE(head) __typeof__((head)->list[0]) #define VRFY_PTR_LIST(head) (void)(sizeof((head)->list[0])) #define LIST_NODE_NR (13) #define DECLARE_PTR_LIST(listname, type) \ struct listname { \ int nr:8; \ int rm:8; \ struct listname *prev; \ struct listname *next; \ type *list[LIST_NODE_NR]; \ } DECLARE_PTR_LIST(ptr_list, void); 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); bool lookup_ptr_list_entry(const struct ptr_list *head, const void *entry); extern void sort_list(struct ptr_list **, int (*)(const void *, const void *)); extern void concat_ptr_list(struct ptr_list *a, struct ptr_list **b); extern void copy_ptr_list(struct ptr_list **h, struct ptr_list *t); extern int ptr_list_size(struct ptr_list *); extern bool ptr_list_empty(const struct ptr_list *head); extern bool ptr_list_multiple(const struct ptr_list *head); extern int linearize_ptr_list(struct ptr_list *, void **, int); extern void *first_ptr_list(struct ptr_list *); extern void *last_ptr_list(struct ptr_list *); extern void *ptr_list_nth_entry(struct ptr_list *, unsigned int idx); extern void pack_ptr_list(struct ptr_list **); /* * Hey, who said that you can't do overloading in C? * * You just have to be creative, and use some gcc * extensions.. */ extern void **__add_ptr_list(struct ptr_list **, void *); extern void **__add_ptr_list_tag(struct ptr_list **, void *, unsigned long); #define add_ptr_list(list, ptr) ({ \ struct ptr_list** head = (struct ptr_list**)(list); \ CHECK_TYPE(*(list),ptr); \ (__typeof__(&(ptr))) __add_ptr_list(head, ptr); \ }) #define add_ptr_list_tag(list, ptr, tag) ({ \ struct ptr_list** head = (struct ptr_list**)(list); \ CHECK_TYPE(*(list),ptr); \ (__typeof__(&(ptr))) __add_ptr_list_tag(head, ptr, tag);\ }) #define pop_ptr_list(l) ({ \ PTRLIST_TYPE(*(l)) ptr; \ ptr = delete_ptr_list_last((struct ptr_list**)(l)); \ ptr; \ }) extern void __free_ptr_list(struct ptr_list **); #define free_ptr_list(list) do { \ VRFY_PTR_LIST(*(list)); \ __free_ptr_list((struct ptr_list **)(list)); \ } while (0) #define ptr_list_nth(lst, nth) ({ \ struct ptr_list* head = (struct ptr_list*)(lst); \ (PTRLIST_TYPE(lst)) ptr_list_nth_entry(head, nth);\ }) #define ptr_list_to_array(list, array, size) ({ \ struct ptr_list* head = (struct ptr_list*)(list); \ CHECK_TYPE(list, *array); \ linearize_ptr_list(head, (void**)array, size); \ }) //////////////////////////////////////////////////////////////////////// // API #define PREPARE_PTR_LIST(head, ptr) \ DO_PREPARE(head, ptr, __head##ptr, __list##ptr, __nr##ptr, PTR_ENTRY_UNTAG) #define NEXT_PTR_LIST(ptr) \ DO_NEXT(ptr, __head##ptr, __list##ptr, __nr##ptr, PTR_ENTRY_UNTAG) #define RESET_PTR_LIST(ptr) \ DO_RESET(ptr, __head##ptr, __list##ptr, __nr##ptr, PTR_ENTRY_UNTAG) #define FINISH_PTR_LIST(ptr) \ DO_FINISH(ptr, __head##ptr, __list##ptr, __nr##ptr) #define RECURSE_PTR_REVERSE(ptr, new) \ DO_REVERSE(ptr, __head##ptr, __list##ptr, __nr##ptr, __rname##new, \ new, __head##new, __list##new, __nr##new, PTR_ENTRY_UNTAG) #define FOR_EACH_PTR(head, ptr) \ DO_FOR_EACH(head, ptr, __head##ptr, __list##ptr, __nr##ptr, __name##ptr, PTR_ENTRY_NOTAG) #define FOR_EACH_PTR_TAG(head, ptr) \ DO_FOR_EACH(head, ptr, __head##ptr, __list##ptr, __nr##ptr, __name##ptr, PTR_ENTRY_UNTAG) #define END_FOR_EACH_PTR(ptr) \ DO_END_FOR_EACH(ptr, __head##ptr, __list##ptr, __nr##ptr, __name##ptr) #define FOR_EACH_PTR_REVERSE(head, ptr) \ DO_FOR_EACH_REVERSE(head, ptr, __head##ptr, __list##ptr, __nr##ptr, __rname##ptr, PTR_ENTRY_NOTAG) #define FOR_EACH_PTR_REVERSE_TAG(head, ptr) \ DO_FOR_EACH_REVERSE(head, ptr, __head##ptr, __list##ptr, __nr##ptr, __rname##ptr, PTR_ENTRY_UNTAG) #define END_FOR_EACH_PTR_REVERSE(ptr) \ DO_END_FOR_EACH_REVERSE(ptr, __head##ptr, __list##ptr, __nr##ptr, __rname##ptr) #define THIS_ADDRESS(ptr) \ DO_THIS_ADDRESS(ptr, __head##ptr, __list##ptr, __nr##ptr) #define INSERT_CURRENT(new, ptr) \ DO_INSERT_CURRENT(new, __head##ptr, __list##ptr, __nr##ptr) #define DELETE_CURRENT_PTR(ptr) \ DO_DELETE_CURRENT(__head##ptr, __list##ptr, __nr##ptr) #define REPLACE_CURRENT_PTR(ptr, new_ptr) \ do { *THIS_ADDRESS(ptr) = (new_ptr); } while (0) // This replace the current element by a null-pointer. // It's used when an element of the list must be removed // but the address of the other elements must not be changed. #define MARK_CURRENT_DELETED(ptr) \ DO_MARK_CURRENT_DELETED(ptr, __list##ptr) #define PACK_PTR_LIST(x) \ pack_ptr_list((struct ptr_list **)(x)) #define CURRENT_TAG(ptr) (3 & (unsigned long)*THIS_ADDRESS(ptr)) #define TAG_CURRENT(ptr,val) update_tag(THIS_ADDRESS(ptr),val) // backward compatibility for smatch #define FOR_EACH_PTR_NOTAG(list, ptr) FOR_EACH_PTR(list, ptr) #define END_FOR_EACH_PTR_NOTAG(ptr) END_FOR_EACH_PTR(ptr) //////////////////////////////////////////////////////////////////////// // Implementation #define PTR_UNTAG(p) ((void*)(~3UL & (unsigned long)(p))) #define PTR_ENTRY_NOTAG(h,i) ((h)->list[i]) #define PTR_ENTRY_UNTAG(h,i) PTR_UNTAG((h)->list[i]) #define PTR_NEXT(ptr, __head, __list, __nr, PTR_ENTRY) \ do { \ if (__nr < __list->nr) { \ ptr = PTR_ENTRY(__list,__nr); \ __nr++; \ break; \ } \ ptr = NULL; \ __nr = 0; \ } while ((__list = __list->next) != __head) \ #define DO_PREPARE(head, ptr, __head, __list, __nr, PTR_ENTRY) \ do { \ __typeof__(head) __head = (head); \ __typeof__(head) __list = __head; \ int __nr = 0; \ ptr = NULL; \ if (__head) { \ PTR_NEXT(ptr, __head, __list, __nr, PTR_ENTRY); \ } \ #define DO_NEXT(ptr, __head, __list, __nr, PTR_ENTRY) \ if (ptr) { \ PTR_NEXT(ptr, __head, __list, __nr, PTR_ENTRY); \ } #define DO_RESET(ptr, __head, __list, __nr, PTR_ENTRY) \ do { \ __nr = 0; \ __list = __head; \ if (__head) \ PTR_NEXT(ptr, __head, __list, __nr, PTR_ENTRY); \ } while (0) #define DO_FINISH(ptr, __head, __list, __nr) \ VRFY_PTR_LIST(__head); /* Sanity-check nesting */ \ } while (0) #define DO_FOR_EACH(head, ptr, __head, __list, __nr, __name, PTR_ENTRY) do { \ __typeof__(head) __head = (head); \ __typeof__(head) __list = __head; \ __typeof__(head) __name = __head; \ int __nr; \ if (!__head) \ break; \ do { \ for (__nr = 0; __nr < __list->nr; __nr++) { \ ptr = PTR_ENTRY(__list,__nr); \ if (__list->rm && !ptr) \ continue; \ #define DO_END_FOR_EACH(ptr, __head, __list, __nr, __name) \ } \ } while ((__list = __list->next) != __head); \ (void) __name; \ } while (0) #define DO_FOR_EACH_REVERSE(head, ptr, __head, __list, __nr, __name, PTR_ENTRY) do { \ __typeof__(head) __head = (head); \ __typeof__(head) __list = __head; \ __typeof__(head) __name = __head; \ int __nr; \ if (!head) \ break; \ do { \ __list = __list->prev; \ __nr = __list->nr; \ while (--__nr >= 0) { \ ptr = PTR_ENTRY(__list,__nr); \ if (__list->rm && !ptr) \ continue; \ #define DO_END_FOR_EACH_REVERSE(ptr, __head, __list, __nr, __name) \ } \ } while (__list != __head); \ (void) __name; \ } while (0) #define DO_REVERSE(ptr, __head, __list, __nr, __name, new, __newhead, \ __newlist, __newnr, PTR_ENTRY) do { \ __typeof__(__head) __newhead = __head; \ __typeof__(__head) __newlist = __list; \ __typeof__(__head) __name = __list; \ int __newnr = __nr; \ new = ptr; \ goto __inside##new; \ do { \ __newlist = __newlist->prev; \ __newnr = __newlist->nr; \ __inside##new: \ while (--__newnr >= 0) { \ new = PTR_ENTRY(__newlist,__newnr); \ #define DO_THIS_ADDRESS(ptr, __head, __list, __nr) \ (&__list->list[__nr]) extern void split_ptr_list_head(struct ptr_list *); #define DO_INSERT_CURRENT(new, __head, __list, __nr) do { \ PTRLIST_TYPE(__head) *__this, *__last; \ if (__list->nr == LIST_NODE_NR) { \ split_ptr_list_head((struct ptr_list*)__list); \ if (__nr >= __list->nr) { \ __nr -= __list->nr; \ __list = __list->next; \ } \ } \ __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 DO_DELETE_CURRENT(__head, __list, __nr) do { \ PTRLIST_TYPE(__head) *__this = __list->list + __nr; \ PTRLIST_TYPE(__head) *__last = __list->list + __list->nr - 1; \ while (__this < __last) { \ __this[0] = __this[1]; \ __this++; \ } \ *__this = (void *)0xf0f0f0f0; \ __list->nr--; __nr--; \ } while (0) #define DO_MARK_CURRENT_DELETED(ptr, __list) do { \ REPLACE_CURRENT_PTR(ptr, NULL); \ __list->rm++; \ } while (0) 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); } #endif /* PTR_LIST_H */ sparse-0.6.4/ptrmap.c000066400000000000000000000051501411531012200144540ustar00rootroot00000000000000// SPDX-License-Identifier: MIT /* * Stupid implementation of pointer -> pointer map. * * Copyright (c) 2017 Luc Van Oostenryck. * * 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 "ptrmap.h" #include "allocate.h" #include #define MAP_NR 7 struct ptrpair { void *key; void *val; }; struct ptrmap { struct ptrmap *next; int nr; struct ptrpair pairs[MAP_NR]; }; DECLARE_ALLOCATOR(ptrmap); ALLOCATOR(ptrmap, "ptrmap"); void __ptrmap_add(struct ptrmap **mapp, void *key, void *val) { struct ptrmap *head = *mapp; struct ptrmap *newmap; struct ptrmap *map; struct ptrpair *pair; int nr; if ((map = head)) { struct ptrmap *next = map->next; if (next) // head is full map = next; if ((nr = map->nr) < MAP_NR) goto oldmap; } // need a new block newmap = __alloc_ptrmap(0); if (!head) { *mapp = newmap; } else { newmap->next = head->next; head->next = newmap; } map = newmap; nr = 0; oldmap: pair = &map->pairs[nr]; pair->key = key; pair->val = val; map->nr = ++nr; } void *__ptrmap_lookup(struct ptrmap *map, void *key) { for (; map; map = map->next) { int i, n = map->nr; for (i = 0; i < n; i++) { struct ptrpair *pair = &map->pairs[i]; if (pair->key == key) return pair->val; } } return NULL; } void __ptrmap_update(struct ptrmap **mapp, void *key, void *val) { struct ptrmap *map = *mapp; for (; map; map = map->next) { int i, n = map->nr; for (i = 0; i < n; i++) { struct ptrpair *pair = &map->pairs[i]; if (pair->key == key) { if (pair->val != val) pair->val = val; return; } } } __ptrmap_add(mapp, key, val); } sparse-0.6.4/ptrmap.h000066400000000000000000000015411411531012200144610ustar00rootroot00000000000000#ifndef PTRMAP_H #define PTRMAP_H struct ptrmap; #define DECLARE_PTRMAP(name, ktype, vtype) \ struct name ## _pair { ktype key; vtype val; }; \ struct name { struct name ## _pair block[1]; }; \ static inline \ void name##_add(struct name **map, ktype k, vtype v) { \ __ptrmap_add((struct ptrmap**)map, k, v); \ } \ static inline \ void name##_update(struct name **map, ktype k, vtype v) { \ __ptrmap_update((struct ptrmap**)map, k, v); \ } \ static inline \ vtype name##_lookup(struct name *map, ktype k) { \ vtype val = __ptrmap_lookup((struct ptrmap*)map, k); \ return val; \ } \ /* ptrmap.c */ void __ptrmap_add(struct ptrmap **mapp, void *key, void *val); void __ptrmap_update(struct ptrmap **mapp, void *key, void *val); void *__ptrmap_lookup(struct ptrmap *map, void *key); #endif sparse-0.6.4/scheck.c000066400000000000000000000246111411531012200144140ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // Copyright (C) 2021 Luc Van Oostenryck /// // Symbolic checker for Sparse's IR // -------------------------------- // // This is an example client program with a dual purpose: // # It shows how to translate Sparse's IR into the language // of SMT solvers (only the part concerning integers, // floating-point and memory is ignored). // # It's used as a simple symbolic checker for the IR. // The idea is to create a mini-language that allows to // express some assertions with some pre-conditions. #include #include #include #include #include #include #include #include #include "lib.h" #include "expression.h" #include "linearize.h" #include "symbol.h" #include "builtin.h" #define dyntype incomplete_ctype static const struct builtin_fn builtins_scheck[] = { { "__assume", &void_ctype, 0, { &dyntype }, .op = &generic_int_op }, { "__assert", &void_ctype, 0, { &bool_ctype }, .op = &generic_int_op }, { "__assert_eq", &void_ctype, 0, { &dyntype, &dyntype }, .op = &generic_int_op }, { "__assert_const", &void_ctype, 0, { &dyntype, &dyntype }, .op = &generic_int_op }, {} }; static BoolectorSort get_sort(Btor *btor, struct symbol *type, struct position pos) { if (!is_int_type(type)) { sparse_error(pos, "invalid type"); return NULL; } return boolector_bitvec_sort(btor, type->bit_size); } static BoolectorNode *mkvar(Btor *btor, BoolectorSort s, pseudo_t pseudo) { static char buff[33]; BoolectorNode *n; switch (pseudo->type) { case PSEUDO_VAL: sprintf(buff, "%llx", pseudo->value); return boolector_consth(btor, s, buff); case PSEUDO_ARG: case PSEUDO_REG: if (pseudo->priv) return pseudo->priv; n = boolector_var(btor, s, show_pseudo(pseudo)); break; default: fprintf(stderr, "invalid pseudo: %s\n", show_pseudo(pseudo)); return NULL; } return pseudo->priv = n; } static BoolectorNode *mktvar(Btor *btor, struct instruction *insn, pseudo_t src) { BoolectorSort s = get_sort(btor, insn->type, insn->pos); return mkvar(btor, s, src); } static BoolectorNode *mkivar(Btor *btor, struct instruction *insn, pseudo_t src) { BoolectorSort s = get_sort(btor, insn->itype, insn->pos); return mkvar(btor, s, src); } static BoolectorNode *get_arg(Btor *btor, struct instruction *insn, int idx) { pseudo_t arg = ptr_list_nth(insn->arguments, idx); struct symbol *type = ptr_list_nth(insn->fntypes, idx + 1); BoolectorSort s = get_sort(btor, type, insn->pos); return mkvar(btor, s, arg); } static BoolectorNode *zext(Btor *btor, struct instruction *insn, BoolectorNode *s) { int old = boolector_get_width(btor, s); int new = insn->type->bit_size; return boolector_uext(btor, s, new - old); } static BoolectorNode *sext(Btor *btor, struct instruction *insn, BoolectorNode *s) { int old = boolector_get_width(btor, s); int new = insn->type->bit_size; return boolector_sext(btor, s, new - old); } static BoolectorNode *slice(Btor *btor, struct instruction *insn, BoolectorNode *s) { int old = boolector_get_width(btor, s); int new = insn->type->bit_size; return boolector_slice(btor, s, old - new - 1, 0); } static void binary(Btor *btor, BoolectorSort s, struct instruction *insn) { BoolectorNode *t, *a, *b; a = mkvar(btor, s, insn->src1); b = mkvar(btor, s, insn->src2); if (!a || !b) return; switch (insn->opcode) { case OP_ADD: t = boolector_add(btor, a, b); break; case OP_SUB: t = boolector_sub(btor, a, b); break; case OP_MUL: t = boolector_mul(btor, a, b); break; case OP_AND: t = boolector_and(btor, a, b); break; case OP_OR: t = boolector_or (btor, a, b); break; case OP_XOR: t = boolector_xor(btor, a, b); break; case OP_SHL: t = boolector_sll(btor, a, b); break; case OP_LSR: t = boolector_srl(btor, a, b); break; case OP_ASR: t = boolector_sra(btor, a, b); break; case OP_DIVS: t = boolector_sdiv(btor, a, b); break; case OP_DIVU: t = boolector_udiv(btor, a, b); break; case OP_MODS: t = boolector_srem(btor, a, b); break; case OP_MODU: t = boolector_urem(btor, a, b); break; case OP_SET_EQ: t = zext(btor, insn, boolector_eq(btor, a, b)); break; case OP_SET_NE: t = zext(btor, insn, boolector_ne(btor, a, b)); break; case OP_SET_LT: t = zext(btor, insn, boolector_slt(btor, a, b)); break; case OP_SET_LE: t = zext(btor, insn, boolector_slte(btor, a, b)); break; case OP_SET_GE: t = zext(btor, insn, boolector_sgte(btor, a, b)); break; case OP_SET_GT: t = zext(btor, insn, boolector_sgt(btor, a, b)); break; case OP_SET_B: t = zext(btor, insn, boolector_ult(btor, a, b)); break; case OP_SET_BE: t = zext(btor, insn, boolector_ulte(btor, a, b)); break; case OP_SET_AE: t = zext(btor, insn, boolector_ugte(btor, a, b)); break; case OP_SET_A: t = zext(btor, insn, boolector_ugt(btor, a, b)); break; default: fprintf(stderr, "unsupported insn: %s\n", show_instruction(insn)); return; } insn->target->priv = t; } static void binop(Btor *btor, struct instruction *insn) { BoolectorSort s = get_sort(btor, insn->type, insn->pos); binary(btor, s, insn); } static void icmp(Btor *btor, struct instruction *insn) { BoolectorSort s = get_sort(btor, insn->itype, insn->pos); binary(btor, s, insn); } static void unop(Btor *btor, struct instruction *insn) { pseudo_t src = insn->src; BoolectorNode *t; switch (insn->opcode) { case OP_SEXT: t = sext(btor, insn, mkivar(btor, insn, src)); break; case OP_ZEXT: t = zext(btor, insn, mkivar(btor, insn, src)); break; case OP_TRUNC: t = slice(btor, insn, mkivar(btor, insn, src)); break; case OP_NEG: t = boolector_neg(btor, mktvar(btor, insn, src)); break; case OP_NOT: t = boolector_not(btor, mktvar(btor, insn, src)); break; default: fprintf(stderr, "unsupported insn: %s\n", show_instruction(insn)); return; } insn->target->priv = t; } static void ternop(Btor *btor, struct instruction *insn) { BoolectorSort s = get_sort(btor, insn->type, insn->pos); BoolectorNode *t, *a, *b, *c, *z, *d; a = mkvar(btor, s, insn->src1); b = mkvar(btor, s, insn->src2); c = mkvar(btor, s, insn->src3); if (!a || !b || !c) return; switch (insn->opcode) { case OP_SEL: z = boolector_zero(btor, s); d = boolector_ne(btor, a, z); t = boolector_cond(btor, d, b, c); break; default: fprintf(stderr, "unsupported insn: %s\n", show_instruction(insn)); return; } insn->target->priv = t; } static bool add_precondition(Btor *btor, BoolectorNode **pre, struct instruction *insn) { BoolectorNode *a = get_arg(btor, insn, 0); BoolectorNode *z = boolector_zero(btor, boolector_get_sort(btor, a)); BoolectorNode *n = boolector_ne(btor, a, z); BoolectorNode *p = boolector_and(btor, *pre, n); *pre = p; return true; } static bool check_btor(Btor *btor, BoolectorNode *p, BoolectorNode *n, struct instruction *insn) { char model_format[] = "btor"; int res; n = boolector_implies(btor, p, n); boolector_assert(btor, boolector_not(btor, n)); res = boolector_sat(btor); switch (res) { case BOOLECTOR_UNSAT: return 1; case BOOLECTOR_SAT: sparse_error(insn->pos, "assertion failed"); show_entry(insn->bb->ep); boolector_dump_btor(btor, stdout); boolector_print_model(btor, model_format, stdout); break; default: sparse_error(insn->pos, "SMT failure"); break; } return 0; } static bool check_assert(Btor *btor, BoolectorNode *pre, struct instruction *insn) { BoolectorNode *a = get_arg(btor, insn, 0); BoolectorNode *z = boolector_zero(btor, boolector_get_sort(btor, a)); BoolectorNode *n = boolector_ne(btor, a, z); return check_btor(btor, pre, n, insn); } static bool check_equal(Btor *btor, BoolectorNode *pre, struct instruction *insn) { BoolectorNode *a = get_arg(btor, insn, 0); BoolectorNode *b = get_arg(btor, insn, 1); BoolectorNode *n = boolector_eq(btor, a, b); return check_btor(btor, pre, n, insn); } static bool check_const(Btor *ctxt, struct instruction *insn) { pseudo_t src1 = ptr_list_nth(insn->arguments, 0); pseudo_t src2 = ptr_list_nth(insn->arguments, 1); if (src2->type != PSEUDO_VAL) sparse_error(insn->pos, "should be a constant: %s", show_pseudo(src2)); if (src1 == src2) return 1; if (src1->type != PSEUDO_VAL) sparse_error(insn->pos, "not a constant: %s", show_pseudo(src1)); else sparse_error(insn->pos, "invalid value: %s != %s", show_pseudo(src1), show_pseudo(src2)); return 0; } static bool check_call(Btor *btor, BoolectorNode **pre, struct instruction *insn) { pseudo_t func = insn->func; struct ident *ident = func->ident; if (ident == &__assume_ident) return add_precondition(btor, pre, insn); if (ident == &__assert_ident) return check_assert(btor, *pre, insn); if (ident == &__assert_eq_ident) return check_equal(btor, *pre, insn); if (ident == &__assert_const_ident) return check_const(btor, insn); return 0; } static bool check_function(struct entrypoint *ep) { Btor *btor = boolector_new(); BoolectorNode *pre = boolector_true(btor); struct basic_block *bb; int rc = 0; boolector_set_opt(btor, BTOR_OPT_MODEL_GEN, 1); boolector_set_opt(btor, BTOR_OPT_INCREMENTAL, 1); FOR_EACH_PTR(ep->bbs, bb) { struct instruction *insn; FOR_EACH_PTR(bb->insns, insn) { if (!insn->bb) continue; switch (insn->opcode) { case OP_ENTRY: continue; case OP_BINARY ... OP_BINARY_END: binop(btor, insn); break; case OP_BINCMP ... OP_BINCMP_END: icmp(btor, insn); break; case OP_UNOP ... OP_UNOP_END: unop(btor, insn); break; case OP_SEL: ternop(btor, insn); break; case OP_CALL: rc &= check_call(btor, &pre, insn); break; case OP_RET: goto out; case OP_INLINED_CALL: case OP_DEATHNOTE: case OP_NOP: case OP_CONTEXT: continue; default: fprintf(stderr, "unsupported insn: %s\n", show_instruction(insn)); goto out; } } END_FOR_EACH_PTR(insn); } END_FOR_EACH_PTR(bb); fprintf(stderr, "unterminated function\n"); out: boolector_release_all(btor); boolector_delete(btor); return rc; } static void check_functions(struct symbol_list *list) { struct symbol *sym; FOR_EACH_PTR(list, sym) { struct entrypoint *ep; expand_symbol(sym); ep = linearize_symbol(sym); if (!ep || !ep->entry) continue; check_function(ep); } END_FOR_EACH_PTR(sym); } int main(int argc, char **argv) { struct string_list *filelist = NULL; char *file; Wdecl = 0; sparse_initialize(argc, argv, &filelist); declare_builtins(0, builtins_scheck); predefine_strong("__SYMBOLIC_CHECKER__"); // Expand, linearize and check. FOR_EACH_PTR(filelist, file) { check_functions(sparse(file)); } END_FOR_EACH_PTR(file); return 0; } sparse-0.6.4/scope.c000066400000000000000000000101311411531012200142550ustar00rootroot00000000000000/* * 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 *label_scope = NULL, // expr-stmt labels *function_scope = &builtin_scope, // labels, arguments etc *file_scope = &builtin_scope, // static *global_scope = &builtin_scope; // externally visible void set_current_scope(struct symbol *sym) { sym->scope = block_scope; } 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); scope->next = *s; *s = scope; } void start_file_scope(void) { struct scope *scope = __alloc_scope(0); 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_block_scope(void) { start_scope(&block_scope); } void start_function_scope(void) { start_scope(&block_scope); start_scope(&label_scope); function_scope = label_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_block_scope(void) { end_scope(&block_scope); } void end_function_scope(void) { end_scope(&block_scope); end_label_scope(); function_scope = label_scope; } void start_label_scope(void) { start_scope(&label_scope); } void end_label_scope(void) { struct symbol *sym; FOR_EACH_PTR(label_scope->symbols, sym) { if (!sym->stmt || sym->used) continue; if (sym->label_modifiers & MOD_UNUSED) continue; warning(sym->pos, "unused label '%s'", show_ident(sym->ident)); } END_FOR_EACH_PTR(sym); end_scope(&label_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; } int is_in_scope(struct scope *outer, struct scope *inner) { while (inner != outer) { if (inner == function_scope) return 0; inner = inner->next; } return 1; } sparse-0.6.4/scope.h000066400000000000000000000041761411531012200142760ustar00rootroot00000000000000#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 symbol_list *symbols; /* List of symbols in this scope */ struct scope *next; }; extern struct scope *block_scope, *label_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_block_scope(void); extern void end_block_scope(void); extern void start_function_scope(void); extern void end_function_scope(void); extern void start_label_scope(void); extern void end_label_scope(void); extern void set_current_scope(struct symbol *); extern void bind_scope(struct symbol *, struct scope *); extern void rebind_scope(struct symbol *, struct scope *); extern int is_outer_scope(struct scope *); extern int is_in_scope(struct scope *outer, struct scope *inner); #endif sparse-0.6.4/semind.1000066400000000000000000000056561411531012200143610ustar00rootroot00000000000000.\" Sindex manpage by Alexey Gladkov .TH semind "1" . .SH NAME semind \- Semantic Indexer for C . .SH SYNOPSIS .B semind [\fIoptions\fR] .br .B semind [\fIoptions\fR] \fIadd\fR [\fIcommand options\fR] [\fI--\fR] [\fIcompiler options\fR] [\fIfiles...\fR] .br .B semind [\fIoptions\fR] \fIrm\fR [\fIcommand options\fR] \fIpattern\fR .br .B semind [\fIoptions\fR] \fIsearch\fR [\fIcommand options\fR] [\fIpattern\fR] .br .B semind [\fIoptions\fR] \fIsearch\fR [\fIcommand options\fR] (\fI-e\fR|\fI-l\fR) \fIfilename\fR:\fIlinenr\fR:\fIcolumn\fR .br .SH DESCRIPTION .P semind is the simple to use cscope-like tool based on sparse/dissect. Unlike cscope it runs after pre-processor and thus it can't index the code filtered out by ifdef's, but otoh it understands how the symbol is used and it can track the usage of struct members. . .SH SUBCOMMANDS .TP \fBadd\fR generates or updates semantic index file. .TP \fBrm\fR removes files from the index by \fIpattern\fR. The \fIpattern\fR is a .BR glob (7) wildcard pattern. .TP \fBsearch\fR queries information about symbol by \fIpattern\fR. The \fIpattern\fR is a .BR glob (7) wildcard pattern. . .SH COMMON OPTIONS .TP \fB-D\fR, \fB--database=FILE\fR specify database file (default: ./semind.sqlite). .TP \fB-v\fR, \fB--verbose\fR show information about what is being done. .TP \fB-h\fR, \fB--help\fR show this text and exit. . .SH ADD OPTIONS .TP \fB--include-local-syms\fR include into the index local symbols. . .SH SEARCH OPTIONS .TP \fB-f\fR, \fB--format=STRING\fR specify an output format. Default: '(%m) %f\\t%l\\t%c\\t%C\\t%s' (see .BR FORMAT below). .TP \fB-p\fR, \fB--path=PATTERN\fR search symbols only in specified directories. .TP \fB-m\fR, \fB--mode=MODE\fR search only the specified type of access (see .BR MODE below). .TP \fB-k\fR, \fB--kind=KIND\fR specify a kind of symbol (see .BR KIND below). .TP \fB-e\fR, \fB--explain\fR Show what happens in the specified file position; .TP \fB-l\fR, \fB--location\fR Show usage of symbols from a specific file position; .TP \fB-v\fR, \fB--verbose\fR show information about what is being done; .TP \fB-h\fR, \fB--help\fR show this text and exit. . .SH FORMAT .TP \fB%m\fR access mode in human readable form (see .BR MODE below). .TP \fB%f\fR file name. .TP \fB%l\fR line number. .TP \fB%c\fR column number. .TP \fB%C\fR the name of the function in which the symbol occurs. .TP \fB%n\fR symbol name. .TP \fB%s\fR source code line. Indexer does not save source code lines. They are read from the file during the search. . .SH KIND .TP \fBf\fR function .TP \fBs\fR strict .TP \fBm\fR struct member . .SH MODE The \fBMODE\fR is dumped as a 3-letter string. The first letter denotes address of part, 2-nd - access by value, 3-rd - access by pointer. A special value '\fIdef\fR' means a symbol definition. .TP \fBr\fR read .TP \fBw\fR write .TP \fBm\fR read and write . .SH SEE ALSO .BR sparse (1) . .SH HOMEPAGE https://sparse.docs.kernel.org . .SH MAILING LIST linux-sparse@vger.kernel.org . sparse-0.6.4/semind.c000066400000000000000000000702351411531012200144360ustar00rootroot00000000000000/* * semind - semantic indexer for C. * * Copyright (C) 2020 Alexey Gladkov */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include "dissect.h" #define U_DEF (0x100 << U_SHIFT) #define SINDEX_DATABASE_VERSION 1 #define message(fmt, ...) semind_error(0, 0, (fmt), ##__VA_ARGS__) static const char *progname; static const char *semind_command = NULL; // common options static const char *semind_dbfile = "semind.sqlite"; static int semind_verbose = 0; static char cwd[PATH_MAX]; static size_t n_cwd; // 'add' command options static struct string_list *semind_filelist = NULL; static int semind_include_local_syms = 0; struct semind_streams { sqlite3_int64 id; }; static struct semind_streams *semind_streams = NULL; static int semind_streams_nr = 0; // 'search' command options static int semind_search_modmask; static int semind_search_modmask_defined = 0; static int semind_search_kind = 0; static char *semind_search_path = NULL; static char *semind_search_symbol = NULL; static const char *semind_search_format = "(%m) %f\t%l\t%c\t%C\t%s"; #define EXPLAIN_LOCATION 1 #define USAGE_BY_LOCATION 2 static int semind_search_by_location; static char *semind_search_filename; static int semind_search_line; static int semind_search_column; static sqlite3 *semind_db = NULL; static sqlite3_stmt *lock_stmt = NULL; static sqlite3_stmt *unlock_stmt = NULL; static sqlite3_stmt *insert_rec_stmt = NULL; static sqlite3_stmt *select_file_stmt = NULL; static sqlite3_stmt *insert_file_stmt = NULL; static sqlite3_stmt *delete_file_stmt = NULL; struct command { const char *name; int dbflags; void (*parse_cmdline)(int argc, char **argv); void (*handler)(int argc, char **argv); }; static void show_usage(void) { if (semind_command) printf("Try '%s %s --help' for more information.\n", progname, semind_command); else printf("Try '%s --help' for more information.\n", progname); exit(1); } static void show_help(int ret) { printf( "Usage: %1$s [options]\n" " or: %1$s [options] add [command options] [--] [compiler options] [files...]\n" " or: %1$s [options] rm [command options] pattern\n" " or: %1$s [options] search [command options] pattern\n" "\n" "These are common %1$s commands used in various situations:\n" " add Generate or updates semantic index file for c-source code;\n" " rm Remove files from the index by pattern;\n" " search Make index queries.\n" "\n" "Options:\n" " -D, --database=FILE Specify database file (default: %2$s);\n" " -B, --basedir=DIR Define project top directory (default is the current directory);\n" " -v, --verbose Show information about what is being done;\n" " -h, --help Show this text and exit.\n" "\n" "Environment:\n" " SINDEX_DATABASE Database file location.\n" " SINDEX_BASEDIR Project top directory.\n" "\n" "Report bugs to authors.\n" "\n", progname, semind_dbfile); exit(ret); } static void show_help_add(int ret) { printf( "Usage: %1$s add [options] [--] [compiler options] files...\n" "\n" "Utility creates or updates a symbol index.\n" "\n" "Options:\n" " --include-local-syms Include into the index local symbols;\n" " -v, --verbose Show information about what is being done;\n" " -h, --help Show this text and exit.\n" "\n" "Report bugs to authors.\n" "\n", progname); exit(ret); } static void show_help_rm(int ret) { printf( "Usage: %1$s rm [options] pattern\n" "\n" "Utility removes source files from the index.\n" "The pattern is a glob(7) wildcard pattern.\n" "\n" "Options:\n" " -v, --verbose Show information about what is being done;\n" " -h, --help Show this text and exit.\n" "\n" "Report bugs to authors.\n" "\n", progname); exit(ret); } static void show_help_search(int ret) { printf( "Usage: %1$s search [options] [pattern]\n" " or: %1$s search [options] (-e|-l) filename[:linenr[:column]]\n" "\n" "Utility searches information about symbol by pattern.\n" "The pattern is a glob(7) wildcard pattern.\n" "\n" "Options:\n" " -f, --format=STRING Specify an output format;\n" " -p, --path=PATTERN Search symbols only in specified directories;\n" " -m, --mode=MODE Search only the specified type of access;\n" " -k, --kind=KIND Specify a kind of symbol;\n" " -e, --explain Show what happens in the specified file position;\n" " -l, --location Show usage of symbols from a specific file position;\n" " -v, --verbose Show information about what is being done;\n" " -h, --help Show this text and exit.\n" "\n" "The KIND can be one of the following: `s', `f', `v', `m'.\n" "\n" "Report bugs to authors.\n" "\n", progname); exit(ret); } static void semind_print_progname(void) { fprintf(stderr, "%s: ", progname); if (semind_command) fprintf(stderr, "%s: ", semind_command); } static void semind_error(int status, int errnum, const char *fmt, ...) { va_list ap; semind_print_progname(); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); if (errnum > 0) fprintf(stderr, ": %s", strerror(errnum)); fprintf(stderr, "\n"); if (status) exit(status); } static void set_search_modmask(const char *v) { size_t n = strlen(v); if (n != 1 && n != 3) semind_error(1, 0, "the length of mode value must be 1 or 3: %s", v); semind_search_modmask_defined = 1; semind_search_modmask = 0; if (n == 1) { switch (v[0]) { case 'r': v = "rrr"; break; case 'w': v = "ww-"; break; case 'm': v = "mmm"; break; case '-': v = "---"; break; default: semind_error(1, 0, "unknown modificator: %s", v); } } else if (!strcmp(v, "def")) { semind_search_modmask = U_DEF; return; } static const int modes[] = { U_R_AOF, U_W_AOF, U_R_AOF | U_W_AOF, U_R_VAL, U_W_VAL, U_R_VAL | U_W_VAL, U_R_PTR, U_W_PTR, U_R_PTR | U_W_PTR, }; for (int i = 0; i < 3; i++) { switch (v[i]) { case 'r': semind_search_modmask |= modes[i * 3]; break; case 'w': semind_search_modmask |= modes[i * 3 + 1]; break; case 'm': semind_search_modmask |= modes[i * 3 + 2]; break; case '-': break; default: semind_error(1, 0, "unknown modificator in the mode value" " (`r', `w', `m' or `-' expected): %c", v[i]); } } } static void parse_cmdline(int argc, char **argv) { static const struct option long_options[] = { { "database", required_argument, NULL, 'D' }, { "basedir", required_argument, NULL, 'B' }, { "verbose", no_argument, NULL, 'v' }, { "help", no_argument, NULL, 'h' }, { NULL } }; int c; char *basedir = getenv("SINDEX_BASEDIR"); char *env; if ((env = getenv("SINDEX_DATABASE")) != NULL) semind_dbfile = env; while ((c = getopt_long(argc, argv, "+B:D:vh", long_options, NULL)) != -1) { switch (c) { case 'D': semind_dbfile = optarg; break; case 'B': basedir = optarg; break; case 'v': semind_verbose++; break; case 'h': show_help(0); } } if (optind == argc) { message("command required"); show_usage(); } if (basedir) { if (!realpath(basedir, cwd)) semind_error(1, errno, "unable to get project base directory"); n_cwd = strlen(cwd); } } static void parse_cmdline_add(int argc, char **argv) { static const struct option long_options[] = { { "include-local-syms", no_argument, NULL, 1 }, { "verbose", no_argument, NULL, 'v' }, { "help", no_argument, NULL, 'h' }, { NULL } }; int c; opterr = 0; while ((c = getopt_long(argc, argv, "+vh", long_options, NULL)) != -1) { switch (c) { case 1: semind_include_local_syms = 1; break; case 'v': semind_verbose++; break; case 'h': show_help_add(0); case '?': goto done; } } done: if (optind == argc) { message("more arguments required"); show_usage(); } // enforce tabstop tabstop = 1; // step back since sparse_initialize will ignore argv[0]. optind--; sparse_initialize(argc - optind, argv + optind, &semind_filelist); } static void parse_cmdline_rm(int argc, char **argv) { static const struct option long_options[] = { { "verbose", no_argument, NULL, 'v' }, { "help", no_argument, NULL, 'h' }, { NULL } }; int c; while ((c = getopt_long(argc, argv, "+vh", long_options, NULL)) != -1) { switch (c) { case 'v': semind_verbose++; break; case 'h': show_help_rm(0); } } if (optind == argc) { message("more arguments required"); show_usage(); } } static void parse_cmdline_search(int argc, char **argv) { static const struct option long_options[] = { { "explain", no_argument, NULL, 'e' }, { "format", required_argument, NULL, 'f' }, { "path", required_argument, NULL, 'p' }, { "location", no_argument, NULL, 'l' }, { "mode", required_argument, NULL, 'm' }, { "kind", required_argument, NULL, 'k' }, { "verbose", no_argument, NULL, 'v' }, { "help", no_argument, NULL, 'h' }, { NULL } }; int c; while ((c = getopt_long(argc, argv, "+ef:m:k:p:lvh", long_options, NULL)) != -1) { switch (c) { case 'e': semind_search_by_location = EXPLAIN_LOCATION; break; case 'l': semind_search_by_location = USAGE_BY_LOCATION; break; case 'f': semind_search_format = optarg; break; case 'm': set_search_modmask(optarg); break; case 'k': semind_search_kind = tolower(optarg[0]); break; case 'p': semind_search_path = optarg; break; case 'v': semind_verbose++; break; case 'h': show_help_search(0); } } if (semind_search_by_location) { char *str; if (optind == argc) semind_error(1, 0, "one argument required"); str = argv[optind]; while (str) { char *ptr; if ((ptr = strchr(str, ':')) != NULL) *ptr++ = '\0'; if (*str != '\0') { if (!semind_search_filename) { semind_search_filename = str; } else if (!semind_search_line) { semind_search_line = atoi(str); } else if (!semind_search_column) { semind_search_column = atoi(str); } } str = ptr; } } else if (optind < argc) semind_search_symbol = argv[optind++]; } static int query_appendf(sqlite3_str *query, const char *fmt, ...) { int status; va_list args; va_start(args, fmt); sqlite3_str_vappendf(query, fmt, args); va_end(args); if ((status = sqlite3_str_errcode(query)) == SQLITE_OK) return 0; if (status == SQLITE_NOMEM) message("not enough memory"); if (status == SQLITE_TOOBIG) message("string too big"); return -1; } static inline void sqlite_bind_text(sqlite3_stmt *stmt, const char *field, const char *var, int len) { if (sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, field), var, len, SQLITE_STATIC) != SQLITE_OK) semind_error(1, 0, "unable to bind value for %s: %s", field, sqlite3_errmsg(semind_db)); } static inline void sqlite_bind_int64(sqlite3_stmt *stmt, const char *field, long long var) { if (sqlite3_bind_int64(stmt, sqlite3_bind_parameter_index(stmt, field), var) != SQLITE_OK) semind_error(1, 0, "unable to bind value for %s: %s", field, sqlite3_errmsg(semind_db)); } static inline void sqlite_prepare(const char *sql, sqlite3_stmt **stmt) { int ret; do { ret = sqlite3_prepare_v2(semind_db, sql, -1, stmt, NULL); if (ret != SQLITE_OK && ret != SQLITE_BUSY) semind_error(1, 0, "unable to prepare query: %s: %s", sqlite3_errmsg(semind_db), sql); } while (ret == SQLITE_BUSY); } static inline void sqlite_prepare_persistent(const char *sql, sqlite3_stmt **stmt) { int ret; do { ret = sqlite3_prepare_v3(semind_db, sql, -1, SQLITE_PREPARE_PERSISTENT, stmt, NULL); if (ret != SQLITE_OK && ret != SQLITE_BUSY) semind_error(1, 0, "unable to prepare query: %s: %s", sqlite3_errmsg(semind_db), sql); } while (ret == SQLITE_BUSY); } static inline void sqlite_reset_stmt(sqlite3_stmt *stmt) { // Contrary to the intuition of many, sqlite3_reset() does not reset the // bindings on a prepared statement. Use this routine to reset all host // parameters to NULL. sqlite3_clear_bindings(stmt); sqlite3_reset(stmt); } static int sqlite_run(sqlite3_stmt *stmt) { int ret = sqlite3_step(stmt); if (ret != SQLITE_DONE && ret != SQLITE_ROW) semind_error(1, 0, "unable to process query: %s: %s", sqlite3_errmsg(semind_db), sqlite3_sql(stmt)); return ret; } static void sqlite_command(const char *sql) { sqlite3_stmt *stmt; sqlite_prepare(sql, &stmt); sqlite_run(stmt); sqlite3_finalize(stmt); } static sqlite3_int64 get_db_version(void) { sqlite3_stmt *stmt; sqlite3_int64 dbversion; sqlite_prepare("PRAGMA user_version", &stmt); sqlite_run(stmt); dbversion = sqlite3_column_int64(stmt, 0); sqlite3_finalize(stmt); return dbversion; } static void set_db_version(void) { char *sql; sqlite3_str *query = sqlite3_str_new(semind_db); if (query_appendf(query, "PRAGMA user_version = %d", SINDEX_DATABASE_VERSION) < 0) exit(1); sql = sqlite3_str_finish(query); sqlite_command(sql); sqlite3_free(sql); } static void open_temp_database(void) { static const char *database_schema[] = { "ATTACH ':memory:' AS tempdb", "CREATE TABLE tempdb.semind (" " file INTEGER NOT NULL," " line INTEGER NOT NULL," " column INTEGER NOT NULL," " symbol TEXT NOT NULL," " kind INTEGER NOT NULL," " context TEXT," " mode INTEGER NOT NULL" ")", NULL, }; for (int i = 0; database_schema[i]; i++) sqlite_command(database_schema[i]); } static void open_database(const char *filename, int flags) { static const char *database_schema[] = { "CREATE TABLE file (" " id INTEGER PRIMARY KEY AUTOINCREMENT," " name TEXT UNIQUE NOT NULL," " mtime INTEGER NOT NULL" ")", "CREATE TABLE semind (" " file INTEGER NOT NULL REFERENCES file(id) ON DELETE CASCADE," " line INTEGER NOT NULL," " column INTEGER NOT NULL," " symbol TEXT NOT NULL," " kind INTEGER NOT NULL," " context TEXT," " mode INTEGER NOT NULL" ")", "CREATE UNIQUE INDEX semind_0 ON semind (symbol, kind, mode, file, line, column)", "CREATE INDEX semind_1 ON semind (file)", NULL, }; int exists = !access(filename, R_OK); if (sqlite3_open_v2(filename, &semind_db, flags, NULL) != SQLITE_OK) semind_error(1, 0, "unable to open database: %s: %s", filename, sqlite3_errmsg(semind_db)); sqlite_command("PRAGMA journal_mode = WAL"); sqlite_command("PRAGMA synchronous = OFF"); sqlite_command("PRAGMA secure_delete = FAST"); sqlite_command("PRAGMA busy_timeout = 2147483647"); sqlite_command("PRAGMA foreign_keys = ON"); if (exists) { if (get_db_version() < SINDEX_DATABASE_VERSION) semind_error(1, 0, "%s: Database too old. Please rebuild it.", filename); return; } set_db_version(); for (int i = 0; database_schema[i]; i++) sqlite_command(database_schema[i]); } struct index_record { const char *context; int ctx_len; const char *symbol; int sym_len; int kind; unsigned int mode; long long mtime; sqlite3_int64 file; int line; int col; }; static void insert_record(struct index_record *rec) { sqlite_bind_text(insert_rec_stmt, "@context", rec->context, rec->ctx_len); sqlite_bind_text(insert_rec_stmt, "@symbol", rec->symbol, rec->sym_len); sqlite_bind_int64(insert_rec_stmt, "@kind", rec->kind); sqlite_bind_int64(insert_rec_stmt, "@mode", rec->mode); sqlite_bind_int64(insert_rec_stmt, "@file", rec->file); sqlite_bind_int64(insert_rec_stmt, "@line", rec->line); sqlite_bind_int64(insert_rec_stmt, "@column", rec->col); sqlite_run(insert_rec_stmt); sqlite_reset_stmt(insert_rec_stmt); } static void update_stream(void) { if (semind_streams_nr >= input_stream_nr) return; semind_streams = realloc(semind_streams, input_stream_nr * sizeof(struct semind_streams)); if (!semind_streams) semind_error(1, errno, "realloc"); sqlite_run(lock_stmt); for (int i = semind_streams_nr; i < input_stream_nr; i++) { struct stat st; const char *filename; char fullname[PATH_MAX]; sqlite3_int64 cur_mtime = 0; if (input_streams[i].fd != -1) { /* * FIXME: Files in the input_streams may be duplicated. */ if (stat(input_streams[i].name, &st) < 0) semind_error(1, errno, "stat: %s", input_streams[i].name); cur_mtime = st.st_mtime; if (!realpath(input_streams[i].name, fullname)) semind_error(1, errno, "realpath: %s", input_streams[i].name); if (!strncmp(fullname, cwd, n_cwd) && fullname[n_cwd] == '/') { filename = fullname + n_cwd + 1; semind_streams[i].id = 0; } else { semind_streams[i].id = -1; continue; } } else { semind_streams[i].id = -1; continue; } if (semind_verbose > 1) message("filename: %s", filename); sqlite_bind_text(select_file_stmt, "@name", filename, -1); if (sqlite_run(select_file_stmt) == SQLITE_ROW) { sqlite3_int64 old_mtime; semind_streams[i].id = sqlite3_column_int64(select_file_stmt, 0); old_mtime = sqlite3_column_int64(select_file_stmt, 1); sqlite_reset_stmt(select_file_stmt); if (cur_mtime == old_mtime) continue; sqlite_bind_text(delete_file_stmt, "@name", filename, -1); sqlite_run(delete_file_stmt); sqlite_reset_stmt(delete_file_stmt); } sqlite_reset_stmt(select_file_stmt); sqlite_bind_text(insert_file_stmt, "@name", filename, -1); sqlite_bind_int64(insert_file_stmt, "@mtime", cur_mtime); sqlite_run(insert_file_stmt); sqlite_reset_stmt(insert_file_stmt); semind_streams[i].id = sqlite3_last_insert_rowid(semind_db); } sqlite_run(unlock_stmt); semind_streams_nr = input_stream_nr; } static void r_symbol(unsigned mode, struct position *pos, struct symbol *sym) { static struct ident null; struct ident *ctx = &null; struct index_record rec; update_stream(); if (semind_streams[pos->stream].id == -1) return; if (!semind_include_local_syms && sym_is_local(sym)) return; if (!sym->ident) { warning(*pos, "empty ident"); return; } if (dissect_ctx) ctx = dissect_ctx->ident; rec.context = ctx->name; rec.ctx_len = ctx->len; rec.symbol = sym->ident->name; rec.sym_len = sym->ident->len; rec.kind = sym->kind; rec.mode = mode; rec.file = semind_streams[pos->stream].id; rec.line = pos->line; rec.col = pos->pos; insert_record(&rec); } static void r_member(unsigned mode, struct position *pos, struct symbol *sym, struct symbol *mem) { static struct ident null; static char memname[1024]; struct ident *ni, *si, *mi; struct ident *ctx = &null; struct index_record rec; update_stream(); if (semind_streams[pos->stream].id == -1) return; if (!semind_include_local_syms && sym_is_local(sym)) return; ni = built_in_ident("?"); si = sym->ident ?: ni; /* mem == NULL means entire struct accessed */ mi = mem ? (mem->ident ?: ni) : built_in_ident("*"); if (dissect_ctx) ctx = dissect_ctx->ident; snprintf(memname, sizeof(memname), "%.*s.%.*s", si->len, si->name, mi->len, mi->name); rec.context = ctx->name; rec.ctx_len = ctx->len; rec.symbol = memname; rec.sym_len = si->len + mi->len + 1; rec.kind = 'm'; rec.mode = mode; rec.file = semind_streams[pos->stream].id; rec.line = pos->line; rec.col = pos->pos; insert_record(&rec); } static void r_symdef(struct symbol *sym) { r_symbol(U_DEF, &sym->pos, sym); } static void r_memdef(struct symbol *sym, struct symbol *mem) { r_member(U_DEF, &mem->pos, sym, mem); } static void command_add(int argc, char **argv) { static struct reporter reporter = { .r_symdef = r_symdef, .r_symbol = r_symbol, .r_memdef = r_memdef, .r_member = r_member, }; open_temp_database(); sqlite_prepare_persistent( "BEGIN IMMEDIATE", &lock_stmt); sqlite_prepare_persistent( "COMMIT", &unlock_stmt); sqlite_prepare_persistent( "INSERT OR IGNORE INTO tempdb.semind " "(context, symbol, kind, mode, file, line, column) " "VALUES (@context, @symbol, @kind, @mode, @file, @line, @column)", &insert_rec_stmt); sqlite_prepare_persistent( "SELECT id, mtime FROM file WHERE name == @name", &select_file_stmt); sqlite_prepare_persistent( "INSERT INTO file (name, mtime) VALUES (@name, @mtime)", &insert_file_stmt); sqlite_prepare_persistent( "DELETE FROM file WHERE name == @name", &delete_file_stmt); dissect(&reporter, semind_filelist); sqlite_run(lock_stmt); sqlite_command("INSERT OR IGNORE INTO semind SELECT * FROM tempdb.semind"); sqlite_run(unlock_stmt); sqlite3_finalize(insert_rec_stmt); sqlite3_finalize(select_file_stmt); sqlite3_finalize(insert_file_stmt); sqlite3_finalize(delete_file_stmt); sqlite3_finalize(lock_stmt); sqlite3_finalize(unlock_stmt); free(semind_streams); } static void command_rm(int argc, char **argv) { sqlite3_stmt *stmt; sqlite_command("BEGIN IMMEDIATE"); sqlite_prepare("DELETE FROM file WHERE name GLOB @file", &stmt); if (semind_verbose > 1) message("SQL: %s", sqlite3_sql(stmt)); for (int i = 0; i < argc; i++) { sqlite_bind_text(stmt, "@file", argv[i], -1); sqlite_run(stmt); sqlite_reset_stmt(stmt); } sqlite3_finalize(stmt); sqlite_command("COMMIT"); } static inline void print_mode(char *value) { char str[3]; int v = atoi(value); if (v == U_DEF) { printf("def"); return; } #define U(m) "-rwm"[(v / m) & 3] str[0] = U(U_R_AOF); str[1] = U(U_R_VAL); str[2] = U(U_R_PTR); printf("%.3s", str); #undef U } static char *semind_file_name; static FILE *semind_file_fd; static int semind_file_lnum; static char *semind_line; static size_t semind_line_buflen; static int semind_line_len; static void print_file_line(const char *name, int lnum) { /* * All files are sorted by name and line number. So, we can reopen * the file and read it line by line. */ if (!semind_file_name || strcmp(semind_file_name, name)) { if (semind_file_fd) { fclose(semind_file_fd); free(semind_file_name); } semind_file_name = strdup(name); if (!semind_file_name) semind_error(1, errno, "strdup"); semind_file_fd = fopen(name, "r"); if (!semind_file_fd) semind_error(1, errno, "fopen: %s", name); semind_file_lnum = 0; } do { if (semind_file_lnum == lnum) { if (semind_line[semind_line_len-1] == '\n') semind_line_len--; printf("%.*s", semind_line_len, semind_line); break; } semind_file_lnum++; errno = 0; } while((semind_line_len = getline(&semind_line, &semind_line_buflen, semind_file_fd)) != -1); if (errno && errno != EOF) semind_error(1, errno, "getline"); } static int search_query_callback(void *data, int argc, char **argv, char **colname) { char *fmt = (char *) semind_search_format; char buf[32]; int quote = 0; int n = 0; while (*fmt != '\0') { char c = *fmt; if (quote) { quote = 0; switch (c) { case 't': c = '\t'; break; case 'r': c = '\r'; break; case 'n': c = '\n'; break; } } else if (c == '%') { int colnum = 0; char *pos = ++fmt; c = *fmt; if (c == '\0') semind_error(1, 0, "unexpected end of format string"); switch (c) { case 'f': colnum = 0; goto print_string; case 'l': colnum = 1; goto print_string; case 'c': colnum = 2; goto print_string; case 'C': colnum = 3; goto print_string; case 'n': colnum = 4; goto print_string; case 'm': if (n) { printf("%.*s", n, buf); n = 0; } print_mode(argv[5]); fmt++; break; case 'k': if (n) { printf("%.*s", n, buf); n = 0; } printf("%c", atoi(argv[6])); fmt++; break; case 's': if (n) { printf("%.*s", n, buf); n = 0; } print_file_line(argv[0], atoi(argv[1])); fmt++; break; print_string: if (n) { printf("%.*s", n, buf); n = 0; } printf("%s", argv[colnum]); fmt++; break; default: break; } if (pos == fmt) semind_error(1, 0, "invalid format specification: %%%c", c); continue; } else if (c == '\\') { quote = 1; fmt++; continue; } if (n == sizeof(buf)) { printf("%.*s", n, buf); n = 0; } buf[n++] = c; fmt++; } if (n) printf("%.*s", n, buf); printf("\n"); return 0; } static void command_search(int argc, char **argv) { char *sql; char *dberr = NULL; sqlite3_str *query = sqlite3_str_new(semind_db); if (chdir(cwd) < 0) semind_error(1, errno, "unable to change directory: %s", cwd); if (query_appendf(query, "SELECT" " file.name," " semind.line," " semind.column," " semind.context," " semind.symbol," " semind.mode," " semind.kind " "FROM semind, file " "WHERE semind.file == file.id") < 0) goto fail; if (semind_search_kind) { if (query_appendf(query, " AND semind.kind == %d", semind_search_kind) < 0) goto fail; } if (semind_search_symbol) { int ret; if (query_appendf(query, " AND ") < 0) goto fail; if (strpbrk(semind_search_symbol, "*?[]")) ret = query_appendf(query, "semind.symbol GLOB %Q", semind_search_symbol); else ret = query_appendf(query, "semind.symbol == %Q", semind_search_symbol); if (ret < 0) goto fail; } if (semind_search_modmask_defined) { if (!semind_search_modmask) { if (query_appendf(query, " AND semind.mode == %d", semind_search_modmask) < 0) goto fail; } else if (query_appendf(query, " AND (semind.mode & %d) != 0", semind_search_modmask) < 0) goto fail; } if (semind_search_path) { if (query_appendf(query, " AND file.name GLOB %Q", semind_search_path) < 0) goto fail; } if (semind_search_by_location == EXPLAIN_LOCATION) { if (query_appendf(query, " AND file.name == %Q", semind_search_filename) < 0) goto fail; if (semind_search_line && query_appendf(query, " AND semind.line == %d", semind_search_line) < 0) goto fail; if (semind_search_column && query_appendf(query, " AND semind.column == %d", semind_search_column) < 0) goto fail; } else if (semind_search_by_location == USAGE_BY_LOCATION) { if (query_appendf(query, " AND semind.symbol IN (") < 0) goto fail; if (query_appendf(query, "SELECT semind.symbol FROM semind, file WHERE" " semind.file == file.id AND" " file.name == %Q", semind_search_filename) < 0) goto fail; if (semind_search_line && query_appendf(query, " AND semind.line == %d", semind_search_line) < 0) goto fail; if (semind_search_column && query_appendf(query, " AND semind.column == %d", semind_search_column) < 0) goto fail; if (query_appendf(query, ")") < 0) goto fail; } if (query_appendf(query, " ORDER BY file.name, semind.line, semind.column ASC", semind_search_path) < 0) goto fail; sql = sqlite3_str_value(query); if (semind_verbose > 1) message("SQL: %s", sql); sqlite3_exec(semind_db, sql, search_query_callback, NULL, &dberr); if (dberr) semind_error(1, 0, "sql query failed: %s", dberr); fail: sql = sqlite3_str_finish(query); sqlite3_free(sql); if (semind_file_fd) { fclose(semind_file_fd); free(semind_file_name); } free(semind_line); } int main(int argc, char **argv) { static const struct command commands[] = { { .name = "add", .dbflags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, .parse_cmdline = parse_cmdline_add, .handler = command_add }, { .name = "rm", .dbflags = SQLITE_OPEN_READWRITE, .parse_cmdline = parse_cmdline_rm, .handler = command_rm }, { .name = "search", .dbflags = SQLITE_OPEN_READONLY, .parse_cmdline = parse_cmdline_search, .handler = command_search }, { .name = NULL }, }; const struct command *cmd; if (!(progname = rindex(argv[0], '/'))) progname = argv[0]; else progname++; if (!realpath(".", cwd)) semind_error(1, errno, "unable to get current directory"); n_cwd = strlen(cwd); parse_cmdline(argc, argv); for (cmd = commands; cmd->name && strcmp(argv[optind], cmd->name); cmd++); if (!cmd->name) semind_error(1, 0, "unknown command: %s", argv[optind]); optind++; semind_command = cmd->name; if (cmd->parse_cmdline) cmd->parse_cmdline(argc, argv); open_database(semind_dbfile, cmd->dbflags); cmd->handler(argc - optind, argv + optind); sqlite3_close(semind_db); return 0; } sparse-0.6.4/show-parse.c000066400000000000000000000702311411531012200152430ustar00rootroot00000000000000/* * 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_TYPEOF] = "tpof", [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: %s) %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), show_as(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. */ static const char *show_modifiers(unsigned long mod, int term) { 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_EXTERN, "extern"}, {MOD_REGISTER, "register"}, {MOD_STATIC, "static"}, {MOD_INLINE, "inline"}, {MOD_CONST, "const"}, {MOD_RESTRICT, "restrict"}, {MOD_VOLATILE, "volatile"}, {MOD_ADDRESSABLE, "[addressable]"}, {MOD_ASSIGNED, "[assigned]"}, {MOD_ATOMIC, "[atomic]"}, {MOD_BITWISE, "[bitwise]"}, {MOD_EXPLICITLY_SIGNED, "[explicitly-signed]"}, {MOD_GNU_INLINE, "[gnu_inline]"}, {MOD_NOCAST, "[nocast]"}, {MOD_NODEREF, "[noderef]"}, {MOD_NORETURN, "[noreturn]"}, {MOD_PURE, "[pure]"}, {MOD_SAFE, "[safe]"}, {MOD_SIGNED, "[signed]"}, {MOD_TLS, "[tls]"}, {MOD_TOPLEVEL, "[toplevel]"}, {MOD_UNSIGNED, "[unsigned]"}, {MOD_UNUSED, "[unused]"}, {MOD_USERTYPE, "[usertype]"}, }; 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++] = ' '; } } if (len && !term) // strip the trailing space --len; buffer[len] = 0; return buffer; } /// // show the modifiers, terminated by a space if not empty const char *modifier_string(unsigned long mod) { return show_modifiers(mod, 1); } /// // show the modifiers, without an ending space const char *modifier_name(unsigned long mod) { return show_modifiers(mod, 0); } 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) { struct symbol *sym; const char *prepend = ""; FOR_EACH_PTR(list, sym) { puts(prepend); prepend = ", "; show_symbol(sym); } END_FOR_EACH_PTR(sym); } const char *show_as(struct ident *as) { if (!as) return ""; return show_ident(as); } 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; const char *suffix; } 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", "U" }, { & long_ctype, "long", "L" }, { &slong_ctype, "signed long", "L" }, { &ulong_ctype, "unsigned long", "UL" }, { & llong_ctype, "long long", "LL" }, { &sllong_ctype, "signed long long", "LL" }, { &ullong_ctype, "unsigned long long", "ULL" }, { & int128_ctype, "__int128", "" }, { &sint128_ctype, "signed __int128", "" }, { &uint128_ctype, "unsigned __int128", "" }, { &void_ctype, "void", "" }, { &bool_ctype, "bool", "" }, { &float_ctype, "float", "F" }, { &double_ctype, "double", "" }, { &ldouble_ctype,"long double", "L" }, { &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_type_suffix(struct symbol *sym) { int i; for (i = 0; i < ARRAY_SIZE(typenames); i++) if (typenames[i].sym == sym) return typenames[i].suffix; 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; struct ident *as = NULL; 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, "%s ", show_as(as)); if (sym && (sym->type == SYM_BASETYPE || sym->type == SYM_ENUM)) mod &= ~MOD_SPECIFIER; s = modifier_string(mod); len = strlen(s); name->start -= len; memcpy(name->start, s, len); mod = 0; as = NULL; } 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; examine_pointer_target(sym); 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: if (sym->ident) append(name, "%s", show_ident(sym->ident)); mod |= sym->ctype.modifiers; combine_address_space(sym->pos, &as, sym->ctype.as); break; case SYM_BITFIELD: mod |= sym->ctype.modifiers; combine_address_space(sym->pos, &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; combine_address_space(sym->pos, &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 "); // strip trailing space if (name->end > name->start && name->end[-1] == ' ') name->end--; } 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->ctype->bit_size, 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, 0LL); 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 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 = is_signed_type(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,$%Le\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 valt = show_expression(expr->cond_true); int valf = 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, valt, valf); 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; case EXPR_GENERIC: warning(expr->pos, "unable to show generic expression"); return 0; } return 0; } sparse-0.6.4/simplify.c000066400000000000000000002135551411531012200150170ustar00rootroot00000000000000/* * Simplify - do instruction simplification before CSE * * Copyright (C) 2004 Linus Torvalds */ /// // Instruction simplification // -------------------------- // // Notation // ^^^^^^^^ // The following conventions are used to describe the simplications: // * Uppercase letters are reserved for constants: // * `M` for a constant mask, // * `S` for a constant shift, // * `N` for a constant number of bits (usually other than a shift), // * `C` or 'K' for others constants. // * Lowercase letters `a`, `b`, `x`, `y`, ... are used for non-constants // or when it doesn't matter if the pseudo is a constant or not. // * Primes are used if needed to distinguish symbols (`M`, `M'`, ...). // * Expressions or sub-expressions involving only constants are // understood to be evaluated. // * `$mask(N)` is used for `((1 << N) -1)` // * `$trunc(x, N)` is used for `(x & $mask(N))` // * Expressions like `(-1 << S)`, `(-1 >> S)` and others formulae are // understood to be truncated to the size of the current instruction // (needed, since in general this size is not the same as the one used // by sparse for the evaluation of arithmetic operations). // * `TRUNC(x, N)` is used for a truncation *to* a size of `N` bits // * `ZEXT(x, N)` is used for a zero-extension *from* a size of `N` bits // * `OP(x, C)` is used to represent some generic operation using a constant, // including when the constant is implicit (e.g. `TRUNC(x, N)`). // * `MASK(x, M)` is used to respresent a 'masking' instruction: // - `AND(x, M)` // - `LSR(x, S)`, with `M` = (-1 << S) // - `SHL(x, S)`, with `M` = (-1 >> S) // - `TRUNC(x, N)`, with `M` = $mask(N) // - `ZEXT(x, N)`, with `M` = $mask(N) // * `SHIFT(x, S)` is used for `LSR(x, S)` or `SHL(x, S)`. #include #include "parse.h" #include "expression.h" #include "linearize.h" #include "simplify.h" #include "flow.h" #include "symbol.h" #include "flowgraph.h" /// // Utilities // ^^^^^^^^^ /// // check if a pseudo is a power of 2 static inline bool is_pow2(pseudo_t src) { if (src->type != PSEUDO_VAL) return false; return is_power_of_2(src->value); } /// // 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 // @return: 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 ptr_list_to_array() for the phi-sources // 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[2]; 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 (ptr_list_to_array(bb->parents, parents, 2) != 2) return 0; p1 = array[0]->phi_src; bb1 = array[0]->bb; p2 = array[1]->phi_src; 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; } /// // detect trivial phi-nodes // @insn: the phi-node // @pseudo: the candidate resulting pseudo (NULL when starting) // @return: the unique result if the phi-node is trivial, NULL otherwise // // A phi-node is trivial if it has a single possible result: // * all operands are the same // * the operands are themselves defined by a chain or cycle of phi-nodes // and the set of all operands involved contains a single value // not defined by these phi-nodes // // Since the result is unique, these phi-nodes can be removed. static pseudo_t trivial_phi(pseudo_t pseudo, struct instruction *insn, struct pseudo_list **list) { pseudo_t target = insn->target; pseudo_t phi; add_pseudo(list, target); FOR_EACH_PTR(insn->phi_list, phi) { struct instruction *def; pseudo_t src; if (phi == VOID) continue; def = phi->def; if (!def->bb) continue; src = def->phi_src; // bypass OP_PHISRC & get the real source if (src == VOID) continue; if (src == target) continue; if (!pseudo) { pseudo = src; continue; } if (src == pseudo) continue; if (DEF_OPCODE(def, src) == OP_PHI) { if (pseudo_in_list(*list, src)) continue; if ((pseudo = trivial_phi(pseudo, def, list))) continue; } return NULL; } END_FOR_EACH_PTR(phi); return pseudo ? pseudo : VOID; } static int clean_up_phi(struct instruction *insn) { struct pseudo_list *list = NULL; pseudo_t pseudo; if ((pseudo = trivial_phi(NULL, insn, &list))) { 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 (pseudo_user_list_empty(*list)) *list = NULL; return count; } static inline void rem_usage(pseudo_t p, pseudo_t *usep, int kill) { if (has_use_list(p)) { delete_pseudo_user_list_entry(&p->users, usep, 1); if (kill && !p->users && has_definition(p)) kill_instruction(p->def); } } static inline void remove_usage(pseudo_t p, pseudo_t *usep) { rem_usage(p, usep, 1); } void kill_use(pseudo_t *usep) { if (usep) { pseudo_t p = *usep; *usep = VOID; rem_usage(p, usep, 1); } } // Like kill_use() but do not (recursively) kill dead instructions void remove_use(pseudo_t *usep) { pseudo_t p = *usep; *usep = VOID; rem_usage(p, usep, 0); } 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); } static void kill_asm(struct instruction *insn) { struct asm_constraint *con; FOR_EACH_PTR(insn->asm_rules->inputs, con) { kill_use(&con->pseudo); } END_FOR_EACH_PTR(con); } /// // kill an instruction // @insn: the instruction to be killed // @force: if unset, the normal case, the instruction is not killed // if not free of possible side-effect; if set the instruction // is unconditionally killed. // // The killed instruction is removed from its BB and the usage // of all its operands are removed. The instruction is also // marked as killed by setting its ->bb to NULL. int kill_insn(struct instruction *insn, int force) { if (!insn || !insn->bb) return 0; 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_UNOP ... OP_UNOP_END: case OP_SLICE: case OP_PHISOURCE: case OP_SYMADDR: case OP_CBR: case OP_SWITCH: case OP_COMPUTEDGOTO: kill_use(&insn->src1); break; case OP_PHI: kill_use_list(insn->phi_list); break; case OP_CALL: if (!force) { /* a "pure" function can be killed too */ struct symbol *fntype = first_symbol(insn->fntypes); if (!(fntype->ctype.modifiers & MOD_PURE)) return 0; } kill_use_list(insn->arguments); if (insn->func->type == PSEUDO_REG) kill_use(&insn->func); break; case OP_LOAD: if (!force && insn->is_volatile) return 0; kill_use(&insn->src); break; case OP_STORE: if (!force) return 0; kill_use(&insn->src); kill_use(&insn->target); break; case OP_ASM: if (!force) return 0; kill_asm(insn); break; case OP_ENTRY: /* ignore */ return 0; case OP_BR: case OP_LABEL: case OP_SETVAL: case OP_SETFVAL: default: break; } insn->bb = NULL; return repeat_phase |= REPEAT_CSE; } static inline bool has_target(struct instruction *insn) { return opcode_table[insn->opcode].flags & OPF_TARGET; } void remove_dead_insns(struct entrypoint *ep) { struct basic_block *bb; FOR_EACH_PTR_REVERSE(ep->bbs, bb) { struct instruction *insn; FOR_EACH_PTR_REVERSE(bb->insns, insn) { if (!insn->bb) continue; if (!has_target(insn)) continue; if (!has_users(insn->target)) kill_instruction(insn); } END_FOR_EACH_PTR_REVERSE(insn); } END_FOR_EACH_PTR_REVERSE(bb); } static inline int constant(pseudo_t pseudo) { return pseudo->type == PSEUDO_VAL; } /// // is this same signed value when interpreted with both size? static inline bool is_signed_constant(long long val, unsigned osize, unsigned nsize) { return bits_extend(val, osize, 1) == bits_extend(val, nsize, 1); } /// // is @src generated by an instruction with the given opcode and size? static inline pseudo_t is_same_op(pseudo_t src, int op, unsigned osize) { struct instruction *def; if (src->type != PSEUDO_REG) return NULL; def = src->def; if (def->opcode != op) return NULL; if (def->orig_type->bit_size != osize) return NULL; return def->src; } static bool is_negate_of(pseudo_t p, pseudo_t ref) { struct instruction *def; return (DEF_OPCODE(def, p) == OP_NEG) && (def->src == ref); } /// // replace the operand of an instruction // @insn: the instruction // @pp: the address of the instruction's operand // @new: the new value for the operand // @return: REPEAT_CSE. static inline int replace_pseudo(struct instruction *insn, pseudo_t *pp, pseudo_t new) { pseudo_t old = *pp; use_pseudo(insn, new, pp); remove_usage(old, pp); return REPEAT_CSE; } int replace_with_pseudo(struct instruction *insn, pseudo_t pseudo) { convert_instruction_target(insn, pseudo); return kill_instruction(insn); } static inline int replace_with_value(struct instruction *insn, long long val) { return replace_with_pseudo(insn, value_pseudo(val)); } /// // replace a binop with an unop // @insn: the instruction to be replaced // @op: the instruction's new opcode // @src: the instruction's new operand // @return: REPEAT_CSE static inline int replace_with_unop(struct instruction *insn, int op, pseudo_t src) { insn->opcode = op; replace_pseudo(insn, &insn->src1, src); remove_usage(insn->src2, &insn->src2); return REPEAT_CSE; } /// // replace rightside's value // @insn: the instruction to be replaced // @op: the instruction's new opcode // @src: the instruction's new operand // @return: REPEAT_CSE static inline int replace_binop_value(struct instruction *insn, int op, long long val) { insn->opcode = op; insn->src2 = value_pseudo(val); return REPEAT_CSE; } /// // replace binop's opcode and values // @insn: the instruction to be replaced // @op: the instruction's new opcode // @return: REPEAT_CSE static inline int replace_binop(struct instruction *insn, int op, pseudo_t *pa, pseudo_t a, pseudo_t *pb, pseudo_t b) { pseudo_t olda = *pa; pseudo_t oldb = *pb; insn->opcode = op; use_pseudo(insn, a, pa); use_pseudo(insn, b, pb); remove_usage(olda, pa); remove_usage(oldb, pb); return REPEAT_CSE; } /// // replace the opcode of an instruction // @return: REPEAT_CSE static inline int replace_opcode(struct instruction *insn, int op) { insn->opcode = op; return REPEAT_CSE; } /// // create an instruction pair OUT(IN(a, b), c) static int replace_insn_pair(struct instruction *out, int op_out, struct instruction *in, int op_in, pseudo_t a, pseudo_t b, pseudo_t c) { pseudo_t old_a = in->src1; pseudo_t old_b = in->src2; pseudo_t old_1 = out->src1; pseudo_t old_2 = out->src2; use_pseudo(in, a, &in->src1); use_pseudo(in, b, &in->src2); use_pseudo(out, in->target, &out->src1); use_pseudo(out, c, &out->src2); remove_usage(old_a, &in->src1); remove_usage(old_b, &in->src2); remove_usage(old_1, &out->src1); remove_usage(old_2, &out->src2); out->opcode = op_out; in->opcode = op_in; return REPEAT_CSE; } /// // create an instruction pair OUT(IN(a, b), c) with swapped opcodes static inline int swap_insn(struct instruction *out, struct instruction *in, pseudo_t a, pseudo_t b, pseudo_t c) { return replace_insn_pair(out, in->opcode, in, out->opcode, a, b, c); } /// // create an instruction pair OUT(SELECT(a, b, c), d) static int swap_select(struct instruction *out, struct instruction *in, pseudo_t a, pseudo_t b, pseudo_t c, pseudo_t d) { use_pseudo(in, c, &in->src3); swap_insn(out, in, a, b, d); kill_use(&out->src3); return REPEAT_CSE; } static inline int def_opcode(pseudo_t p) { if (p->type != PSEUDO_REG) return OP_BADOP; return p->def->opcode; } 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 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_ZEXT && 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 pseudo_t eval_op(int op, unsigned size, pseudo_t src1, pseudo_t src2) { /* FIXME! Verify signs and sizes!! */ long long left = src1->value; long long right = src2->value; unsigned long long ul, ur; long long res, mask, bits; mask = 1ULL << (size-1); bits = mask | (mask-1); if (left & mask) left |= ~bits; if (right & mask) right |= ~bits; ul = left & bits; ur = right & bits; switch (op) { case OP_NEG: res = -left; break; case OP_NOT: res = ~ul; break; case OP_ADD: res = left + right; break; case OP_SUB: res = left - right; break; case OP_MUL: res = ul * ur; break; case OP_DIVU: if (!ur) goto undef; res = ul / ur; break; case OP_DIVS: if (!right) goto undef; if (left == mask && right == -1) goto undef; res = left / right; break; case OP_MODU: if (!ur) goto undef; res = ul % ur; break; case OP_MODS: if (!right) goto undef; if (left == mask && right == -1) goto undef; res = left % right; break; case OP_SHL: if (ur >= size) goto undef; res = left << right; break; case OP_LSR: if (ur >= size) goto undef; res = ul >> ur; break; case OP_ASR: if (ur >= size) goto undef; 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; /* 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 NULL; } // Warning: this should be done with the output size which may // be different than the input size used here. But it differs // only for compares which are not concerned since only returning // 0 or 1 and for casts which are not handled here. res &= bits; return value_pseudo(res); undef: return NULL; } static inline pseudo_t eval_unop(int op, unsigned size, pseudo_t src) { return eval_op(op, size, src, VOID); } /// // Simplifications // ^^^^^^^^^^^^^^^ /// // try to simplify MASK(OR(AND(x, M'), b), M) // @insn: the masking instruction // @mask: the associated mask (M) // @ora: one of the OR's operands, guaranteed to be PSEUDO_REG // @orb: the other OR's operand // @return: 0 if no changes have been made, one or more REPEAT_* flags otherwise. static int simplify_mask_or_and(struct instruction *insn, unsigned long long mask, pseudo_t ora, pseudo_t orb) { unsigned long long omask, nmask; struct instruction *and = ora->def; pseudo_t src2 = and->src2; if (and->opcode != OP_AND) return 0; if (!constant(src2)) return 0; omask = src2->value; nmask = omask & mask; if (nmask == 0) { // if (M' & M) == 0: ((a & M') | b) -> b return replace_pseudo(insn, &insn->src1, orb); } if (!one_use(insn->src1)) return 0; // can't modify anything inside the OR if (nmask == mask) { struct instruction *or = insn->src1->def; pseudo_t *arg = (ora == or->src1) ? &or->src1 : &or->src2; // if (M' & M) == M: ((a & M') | b) -> (a | b) return replace_pseudo(or, arg, and->src1); } if (nmask != omask && one_use(ora)) { // if (M' & M) != M': AND(a, M') -> AND(a, (M' & M)) and->src2 = value_pseudo(nmask); return REPEAT_CSE; } return 0; } /// // try to simplify MASK(OR(a, b), M) // @insn: the masking instruction // @mask: the associated mask (M) // @or: the OR instruction // @return: 0 if no changes have been made, one or more REPEAT_* flags otherwise. static int simplify_mask_or(struct instruction *insn, unsigned long long mask, struct instruction *or) { pseudo_t src1 = or->src1; pseudo_t src2 = or->src2; int rc; if (src1->type == PSEUDO_REG) { if ((rc = simplify_mask_or_and(insn, mask, src1, src2))) return rc; } if (src2->type == PSEUDO_REG) { if ((rc = simplify_mask_or_and(insn, mask, src2, src1))) return rc; } else if (src2->type == PSEUDO_VAL) { unsigned long long oval = src2->value; unsigned long long nval = oval & mask; // Try to simplify: // MASK(OR(x, C), M) if (nval == 0) { // if (C & M) == 0: OR(x, C) -> x return replace_pseudo(insn, &insn->src1, src1); } if (nval == mask) { // if (C & M) == M: OR(x, C) -> M return replace_pseudo(insn, &insn->src1, value_pseudo(mask)); } if (nval != oval && one_use(or->target)) { // if (C & M) != C: OR(x, C) -> OR(x, (C & M)) return replace_pseudo(or, &or->src2, value_pseudo(nval)); } } return 0; } /// // try to simplify MASK(SHIFT(OR(a, b), S), M) // @sh: the shift instruction // @or: the OR instruction // @mask: the mask associated to MASK (M): // @return: 0 if no changes have been made, one or more REPEAT_* flags otherwise. static int simplify_mask_shift_or(struct instruction *sh, struct instruction *or, unsigned long long mask) { unsigned long long smask = bits_mask(sh->size); int shift = sh->src2->value; if (sh->opcode == OP_LSR) mask <<= shift; else mask >>= shift; return simplify_mask_or(sh, smask & mask, or); } static int simplify_mask_shift(struct instruction *sh, unsigned long long mask) { struct instruction *inner; if (!constant(sh->src2) || sh->tainted) return 0; switch (DEF_OPCODE(inner, sh->src1)) { case OP_OR: if (one_use(sh->target)) return simplify_mask_shift_or(sh, inner, mask); break; } return 0; } static pseudo_t eval_insn(struct instruction *insn) { unsigned size = insn->size; if (opcode_table[insn->opcode].flags & OPF_COMPARE) size = insn->itype->bit_size; return eval_op(insn->opcode, size, insn->src1, insn->src2); } static long long check_shift_count(struct instruction *insn, unsigned long long uval) { unsigned int size = insn->size; long long sval = uval; if (insn->tainted) return -1; if (uval < size) return uval; insn->tainted = 1; sval = sign_extend_safe(sval, size); sval = sign_extend_safe(sval, bits_in_int); if (sval < 0) insn->src2 = value_pseudo(sval); return -1; } static int simplify_shift(struct instruction *insn, pseudo_t pseudo, long long value) { struct instruction *def; unsigned long long mask, omask, nmask; unsigned long long nval; unsigned int size; pseudo_t src2; if (!value) return replace_with_pseudo(insn, pseudo); value = check_shift_count(insn, value); if (value < 0) return 0; size = insn->size; switch (insn->opcode) { case OP_ASR: if (value >= size) return 0; if (pseudo->type != PSEUDO_REG) break; def = pseudo->def; switch (def->opcode) { case OP_LSR: case OP_ASR: if (def == insn) // cyclic DAG! break; src2 = def->src2; if (src2->type != PSEUDO_VAL) break; nval = src2->value; if (nval > insn->size || nval == 0) break; value += nval; if (def->opcode == OP_LSR) insn->opcode = OP_LSR; else if (value >= size) value = size - 1; goto new_value; case OP_ZEXT: // transform: // zext.N %t <- (O) %a // asr.N %r <- %t, C // into // zext.N %t <- (O) %a // lsr.N %r <- %t, C insn->opcode = OP_LSR; return REPEAT_CSE; } break; case OP_LSR: size = operand_size(insn, pseudo); if (value >= size) goto zero; switch(DEF_OPCODE(def, pseudo)) { case OP_AND: // replace (A & M) >> S // by (A >> S) & (M >> S) if (!constant(def->src2)) break; mask = bits_mask(insn->size - value) << value; omask = def->src2->value; nmask = omask & mask; if (nmask == 0) return replace_with_value(insn, 0); if (nmask == mask) return replace_pseudo(insn, &insn->src1, def->src1); if (!one_use(pseudo)) break; def->opcode = OP_LSR; def->src2 = insn->src2; insn->opcode = OP_AND; insn->src2 = value_pseudo(omask >> value); return REPEAT_CSE; case OP_LSR: goto case_shift_shift; case OP_OR: mask = bits_mask(size); return simplify_mask_shift_or(insn, def, mask); case OP_SHL: // replace ((x << S) >> S) // by (x & (-1 >> S)) if (def->src2 != insn->src2) break; mask = bits_mask(insn->size - value); goto replace_mask; } break; case OP_SHL: if (value >= size) goto zero; switch(DEF_OPCODE(def, pseudo)) { case OP_AND: // simplify (A & M) << S if (!constant(def->src2)) break; mask = bits_mask(insn->size) >> value; omask = def->src2->value; nmask = omask & mask; if (nmask == 0) return replace_with_value(insn, 0); if (nmask == mask) return replace_pseudo(insn, &insn->src1, def->src1); // do not simplify into ((A << S) & (M << S)) break; case OP_LSR: // replace ((x >> S) << S) // by (x & (-1 << S)) if (def->src2 != insn->src2) break; mask = bits_mask(insn->size - value) << value; goto replace_mask; case OP_OR: mask = bits_mask(size); return simplify_mask_shift_or(insn, def, mask); case OP_SHL: case_shift_shift: // also for LSR - LSR if (def == insn) // cyclic DAG! break; src2 = def->src2; if (src2->type != PSEUDO_VAL) break; nval = src2->value; if (nval > insn->size) break; value += nval; goto new_value; } break; } return 0; new_value: if (value < size) { insn->src2 = value_pseudo(value); return replace_pseudo(insn, &insn->src1, pseudo->def->src1); } zero: return replace_with_value(insn, 0); replace_mask: insn->opcode = OP_AND; insn->src2 = value_pseudo(mask); return replace_pseudo(insn, &insn->src1, def->src1); } 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_MUL: 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 simplify_seteq_setne(struct instruction *insn, long long value) { pseudo_t old = insn->src1; struct instruction *def; unsigned osize; int inverse; int opcode; if (value != 0 && value != 1) return 0; if (old->type != PSEUDO_REG) return 0; def = old->def; if (!def) return 0; inverse = (insn->opcode == OP_SET_NE) == value; if (!inverse && def->size == 1 && insn->size == 1) { // Replace: // setne %r <- %s, $0 // or: // seteq %r <- %s, $1 // by %s when boolean return replace_with_pseudo(insn, old); } opcode = def->opcode; switch (opcode) { case OP_AND: if (inverse) break; if (def->size != insn->size) break; if (def->src2->type != PSEUDO_VAL) break; if (def->src2->value != 1) break; return replace_with_pseudo(insn, old); case OP_FPCMP ... 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 insn->opcode = inverse ? opcode_table[opcode].negate : opcode; insn->itype = def->itype; use_pseudo(insn, def->src1, &insn->src1); use_pseudo(insn, def->src2, &insn->src2); remove_usage(old, &insn->src1); return REPEAT_CSE; case OP_SEXT: if (value && (def->orig_type->bit_size == 1)) break; /* Fall through */ case OP_ZEXT: // Convert: // *ext.m %s <- (1) %a // setne.1 %r <- %s, $0 // into: // setne.1 %s <- %a, $0 // and same for setne/eq ... 0/1 insn->itype = def->orig_type; return replace_pseudo(insn, &insn->src1, def->src); case OP_TRUNC: if (!one_use(old)) break; // convert // trunc.n %s <- (o) %a // setne.m %r <- %s, $0 // into: // and.o %s <- %a, $((1 << o) - 1) // setne.m %r <- %s, $0 // and same for setne/eq ... 0/1 osize = def->size; def->opcode = OP_AND; def->type = def->orig_type; def->size = def->type->bit_size; def->src2 = value_pseudo(bits_mask(osize)); return REPEAT_CSE; } return 0; } static int simplify_compare_constant(struct instruction *insn, long long value) { unsigned size = insn->itype->bit_size; unsigned long long bits = bits_mask(size); struct instruction *def; pseudo_t src1, src2; unsigned int osize; int changed = 0; switch (insn->opcode) { case OP_SET_LT: if (!value) break; if (value == sign_bit(size)) // (x < SMIN) --> 0 return replace_with_pseudo(insn, value_pseudo(0)); if (value == sign_mask(size)) // (x < SMAX) --> (x != SMAX) return replace_opcode(insn, OP_SET_NE); if (value == sign_bit(size) + 1)// (x < SMIN + 1) --> (x == SMIN) return replace_binop_value(insn, OP_SET_EQ, sign_bit(size)); if (!(value & sign_bit(size))) changed |= replace_binop_value(insn, OP_SET_LE, (value - 1) & bits); break; case OP_SET_LE: if (!value) break; if (value == sign_mask(size)) // (x <= SMAX) --> 1 return replace_with_pseudo(insn, value_pseudo(1)); if (value == sign_bit(size)) // (x <= SMIN) --> (x == SMIN) return replace_opcode(insn, OP_SET_EQ); if (value == sign_mask(size) - 1) // (x <= SMAX - 1) --> (x != SMAX) return replace_binop_value(insn, OP_SET_NE, sign_mask(size)); if (value & sign_bit(size)) changed |= replace_binop_value(insn, OP_SET_LT, (value + 1) & bits); break; case OP_SET_GE: if (!value) break; if (value == sign_bit(size)) // (x >= SMIN) --> 1 return replace_with_pseudo(insn, value_pseudo(1)); if (value == sign_mask(size)) // (x >= SMAX) --> (x == SMAX) return replace_opcode(insn, OP_SET_EQ); if (value == sign_bit(size) + 1)// (x >= SMIN + 1) --> (x != SMIN) return replace_binop_value(insn, OP_SET_NE, sign_bit(size)); if (!(value & sign_bit(size))) changed |= replace_binop_value(insn, OP_SET_GT, (value - 1) & bits); break; case OP_SET_GT: if (!value) break; if (value == sign_mask(size)) // (x > SMAX) --> 0 return replace_with_pseudo(insn, value_pseudo(0)); if (value == sign_bit(size)) // (x > SMIN) --> (x != SMIN) return replace_opcode(insn, OP_SET_NE); if (value == sign_mask(size) - 1) // (x > SMAX - 1) --> (x == SMAX) return replace_binop_value(insn, OP_SET_EQ, sign_mask(size)); if (value & sign_bit(size)) changed |= replace_binop_value(insn, OP_SET_GE, (value + 1) & bits); break; case OP_SET_B: if (!value) // (x < 0) --> 0 return replace_with_pseudo(insn, value_pseudo(0)); if (value == 1) // (x < 1) --> (x == 0) return replace_binop_value(insn, OP_SET_EQ, 0); else if (value == bits) // (x < ~0) --> (x != ~0) return replace_binop_value(insn, OP_SET_NE, value); else // (x < y) --> (x <= (y-1)) changed |= replace_binop_value(insn, OP_SET_BE, (value - 1) & bits); break; case OP_SET_AE: if (!value) // (x >= 0) --> 1 return replace_with_pseudo(insn, value_pseudo(1)); if (value == 1) // (x >= 1) --> (x != 0) return replace_binop_value(insn, OP_SET_NE, 0); else if (value == bits) // (x >= ~0) --> (x == ~0) return replace_binop_value(insn, OP_SET_EQ, value); else // (x >= y) --> (x > (y-1) changed |= replace_binop_value(insn, OP_SET_A, (value - 1) & bits); break; case OP_SET_BE: if (!value) // (x <= 0) --> (x == 0) return replace_opcode(insn, OP_SET_EQ); if (value == bits) // (x <= ~0) --> 1 return replace_with_pseudo(insn, value_pseudo(1)); if (value == (bits - 1)) // (x <= ~1) --> (x != ~0) return replace_binop_value(insn, OP_SET_NE, bits); if (value == (bits >> 1)) // (x u<= SMAX) --> (x s>= 0) changed |= replace_binop_value(insn, OP_SET_GE, 0); break; case OP_SET_A: if (!value) // (x > 0) --> (x != 0) return replace_opcode(insn, OP_SET_NE); if (value == bits) // (x > ~0) --> 0 return replace_with_pseudo(insn, value_pseudo(0)); if (value == (bits - 1)) // (x > ~1) --> (x == ~0) return replace_binop_value(insn, OP_SET_EQ, bits); if (value == (bits >> 1)) // (x u> SMAX) --> (x s< 0) changed |= replace_binop_value(insn, OP_SET_LT, 0); break; } src1 = insn->src1; src2 = insn->src2; value = src2->value; switch (DEF_OPCODE(def, src1)) { case OP_AND: if (!constant(def->src2)) break; bits = def->src2->value; switch (insn->opcode) { case OP_SET_EQ: if ((value & bits) != value) return replace_with_value(insn, 0); if (value == bits && is_power_of_2(bits)) return replace_binop_value(insn, OP_SET_NE, 0); break; case OP_SET_NE: if ((value & bits) != value) return replace_with_value(insn, 1); if (value == bits && is_power_of_2(bits)) return replace_binop_value(insn, OP_SET_EQ, 0); break; case OP_SET_LE: case OP_SET_LT: value = sign_extend(value, def->size); if (insn->opcode == OP_SET_LT) value -= 1; if (bits & sign_bit(def->size)) break; if (value < 0) return replace_with_value(insn, 0); if (value >= (long long)bits) return replace_with_value(insn, 1); if (value == 0) return replace_opcode(insn, OP_SET_EQ); break; case OP_SET_GT: case OP_SET_GE: value = sign_extend(value, def->size); if (insn->opcode == OP_SET_GE) value -= 1; if (bits & sign_bit(def->size)) break; if (value < 0) return replace_with_value(insn, 1); if (value >= (long long)bits) return replace_with_value(insn, 0); if (value == 0) return replace_opcode(insn, OP_SET_NE); break; case OP_SET_B: if (value > bits) return replace_with_value(insn, 1); break; case OP_SET_BE: if (value >= bits) return replace_with_value(insn, 1); break; case OP_SET_AE: if (value > bits) return replace_with_value(insn, 0); break; case OP_SET_A: if (value >= bits) return replace_with_value(insn, 0); break; } break; case OP_OR: if (!constant(def->src2)) break; bits = def->src2->value; switch (insn->opcode) { case OP_SET_EQ: if ((value & bits) != bits) return replace_with_value(insn, 0); break; case OP_SET_NE: if ((value & bits) != bits) return replace_with_value(insn, 1); break; case OP_SET_B: if (bits >= value) return replace_with_value(insn, 0); break; case OP_SET_BE: if (bits > value) return replace_with_value(insn, 0); break; case OP_SET_AE: if (bits > value) return replace_with_value(insn, 1); break; case OP_SET_A: if (bits >= value) return replace_with_value(insn, 1); break; case OP_SET_LT: value -= 1; case OP_SET_LE: if (bits & sign_bit(def->size)) { value = sign_extend(value, def->size); if (value >= -1) return replace_with_value(insn, 1); } break; case OP_SET_GE: value -= 1; case OP_SET_GT: if (bits & sign_bit(def->size)) { value = sign_extend(value, def->size); if (value >= -1) return replace_with_value(insn, 0); } break; } break; case OP_SEXT: // sext(x) cmp C --> x cmp trunc(C) osize = def->orig_type->bit_size; if (is_signed_constant(value, osize, size)) { insn->itype = def->orig_type; insn->src2 = value_pseudo(zero_extend(value, osize)); return replace_pseudo(insn, &insn->src1, def->src); } switch (insn->opcode) { case OP_SET_BE: if (value >= sign_bit(osize)) { insn->itype = def->orig_type; replace_binop_value(insn, OP_SET_GE, 0); return replace_pseudo(insn, &insn->src1, def->src); } break; case OP_SET_A: if (value >= sign_bit(osize)) { insn->itype = def->orig_type; replace_binop_value(insn, OP_SET_LT, 0); return replace_pseudo(insn, &insn->src1, def->src); } break; case OP_SET_LT: case OP_SET_LE: if (value < sign_bit(size)) return replace_with_value(insn, 1); else return replace_with_value(insn, 0); break; case OP_SET_GE: case OP_SET_GT: if (value < sign_bit(size)) return replace_with_value(insn, 0); else return replace_with_value(insn, 1); break; } break; case OP_TRUNC: osize = def->orig_type->bit_size; switch (insn->opcode) { case OP_SET_EQ: case OP_SET_NE: if (one_use(def->target)) { insn->itype = def->orig_type; def->type = def->orig_type; def->size = osize; def->src2 = value_pseudo(bits); return replace_opcode(def, OP_AND); } break; } break; case OP_ZEXT: osize = def->orig_type->bit_size; bits = bits_mask(osize); if (value <= bits) { const struct opcode_table *op = &opcode_table[insn->opcode]; if (op->flags & OPF_SIGNED) insn->opcode = op->sign; insn->itype = def->orig_type; return replace_pseudo(insn, &insn->src1, def->src); } switch (insn->opcode) { case OP_SET_LT: case OP_SET_LE: if (sign_extend(value, size) > (long long)bits) return replace_with_value(insn, 1); else return replace_with_value(insn, 0); break; case OP_SET_GE: case OP_SET_GT: if (sign_extend(value, size) > (long long)bits) return replace_with_value(insn, 0); else return replace_with_value(insn, 1); break; case OP_SET_B: case OP_SET_BE: return replace_with_value(insn, 1); case OP_SET_AE: case OP_SET_A: return replace_with_value(insn, 0); } break; } return changed; } static int simplify_constant_mask(struct instruction *insn, unsigned long long mask) { pseudo_t old = insn->src1; unsigned long long omask; unsigned long long nmask; struct instruction *def; int osize; switch (DEF_OPCODE(def, old)) { case OP_FPCMP ... OP_BINCMP_END: osize = 1; goto oldsize; case OP_OR: return simplify_mask_or(insn, mask, def); case OP_LSR: case OP_SHL: return simplify_mask_shift(def, mask); case OP_ZEXT: osize = def->orig_type->bit_size; /* fall through */ oldsize: omask = (1ULL << osize) - 1; nmask = mask & omask; if (nmask == omask) // the AND mask is redundant return replace_with_pseudo(insn, old); if (nmask != mask) { // can use a smaller mask insn->src2 = value_pseudo(nmask); return REPEAT_CSE; } break; } return 0; } static int simplify_const_rightadd(struct instruction *def, struct instruction *insn) { unsigned size = insn->size; pseudo_t src2 = insn->src2; switch (def->opcode) { case OP_SUB: if (constant(def->src1)) { // (C - y) + D --> eval(C+D) - y pseudo_t val = eval_op(OP_ADD, size, def->src1, src2); insn->opcode = OP_SUB; use_pseudo(insn, def->src2, &insn->src2); return replace_pseudo(insn, &insn->src1, val); } break; } return 0; } static int simplify_constant_rightside(struct instruction *insn) { long long value = insn->src2->value; long long sbit = 1ULL << (insn->size - 1); long long bits = sbit | (sbit - 1); int changed = 0; switch (insn->opcode) { case OP_OR: if ((value & bits) == bits) return replace_with_pseudo(insn, insn->src2); goto case_neutral_zero; case OP_XOR: if ((value & bits) == bits) { insn->opcode = OP_NOT; return REPEAT_CSE; } /* fallthrough */ case_neutral_zero: if (!value) return replace_with_pseudo(insn, insn->src1); return 0; case OP_SUB: insn->opcode = OP_ADD; insn->src2 = eval_unop(OP_NEG, insn->size, insn->src2); changed = REPEAT_CSE; /* fallthrough */ case OP_ADD: if (!value) return replace_with_pseudo(insn, insn->src1); if (insn->src1->type == PSEUDO_REG) // (x # y) + z changed |= simplify_const_rightadd(insn->src1->def, insn); return changed; case OP_ASR: case OP_SHL: case OP_LSR: return simplify_shift(insn, insn->src1, value); case OP_MODU: case OP_MODS: if (value == 1) return replace_with_value(insn, 0); return 0; case OP_DIVU: case OP_DIVS: case OP_MUL: return simplify_mul_div(insn, value); case OP_AND: if (!value) return replace_with_pseudo(insn, insn->src2); if ((value & bits) == bits) return replace_with_pseudo(insn, insn->src1); return simplify_constant_mask(insn, value); case OP_SET_NE: case OP_SET_EQ: if ((changed = simplify_seteq_setne(insn, value))) return changed; /* fallthrough */ case OP_SET_LT: case OP_SET_LE: case OP_SET_GE: case OP_SET_GT: case OP_SET_B: case OP_SET_BE: case OP_SET_AE: case OP_SET_A: return simplify_compare_constant(insn, value); } return 0; } static int simplify_const_leftsub(struct instruction *insn, struct instruction *def) { unsigned size = insn->size; pseudo_t src1 = insn->src1; switch (def->opcode) { case OP_ADD: if (constant(def->src2)) { // C - (y + D) --> eval(C-D) - y insn->src1 = eval_op(OP_SUB, size, src1, def->src2); return replace_pseudo(insn, &insn->src2, def->src1); } break; case OP_SUB: if (constant(def->src1)) { // C - (D - z) --> z + eval(C-D) pseudo_t val = eval_op(OP_SUB, size, src1, def->src1); insn->opcode = OP_ADD; use_pseudo(insn, def->src2, &insn->src1); return replace_pseudo(insn, &insn->src2, val); } break; } 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_MUL: if (!value) return replace_with_pseudo(insn, insn->src1); return 0; case OP_SUB: if (!value) // (0 - x) --> -x return replace_with_unop(insn, OP_NEG, insn->src2); if (insn->src2->type == PSEUDO_REG) return simplify_const_leftsub(insn, insn->src2->def); break; } return 0; } static int simplify_constant_binop(struct instruction *insn) { pseudo_t res = eval_insn(insn); if (!res) return 0; return replace_with_pseudo(insn, res); } 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_value(insn, 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_value(insn, 1); case OP_AND: case OP_OR: return replace_with_pseudo(insn, arg); default: break; } return 0; } static int simplify_binop(struct instruction *insn) { 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 int 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); return REPEAT_CSE; } /// // check if the given pseudos are in canonical order // // The canonical order is VOID < UNDEF < PHI < REG < ARG < SYM < VAL // The rationale is: // * VALs at right (they don't need a definition) // * REGs at left (they need a defining instruction) // * SYMs & ARGs between REGs & VALs // * REGs & ARGs are ordered between themselves by their internal number // * SYMs are ordered between themselves by address // * VOID, UNDEF and PHI are uninteresting (but VOID should have type 0) static int canonical_order(pseudo_t p1, pseudo_t p2) { int t1 = p1->type; int t2 = p2->type; /* symbol/constants on the right */ if (t1 < t2) return 1; if (t1 > t2) return 0; switch (t1) { case PSEUDO_SYM: return p1->sym <= p2->sym; case PSEUDO_REG: case PSEUDO_ARG: return p1->nr <= p2->nr; default: return 1; } } static int canonicalize_commutative(struct instruction *insn) { if (canonical_order(insn->src1, insn->src2)) return 0; switch_pseudo(insn, &insn->src1, insn, &insn->src2); return repeat_phase |= REPEAT_CSE; } static int canonicalize_compare(struct instruction *insn) { if (canonical_order(insn->src1, insn->src2)) return 0; switch_pseudo(insn, &insn->src1, insn, &insn->src2); insn->opcode = opcode_table[insn->opcode].swap; return repeat_phase |= REPEAT_CSE; } static inline int simple_pseudo(pseudo_t pseudo) { return pseudo->type == PSEUDO_VAL || pseudo->type == PSEUDO_SYM; } /// // test if, in the given BB, the ordering of 2 instructions static bool insn_before(struct basic_block *bb, struct instruction *x, struct instruction *y) { struct instruction *insn; FOR_EACH_PTR(bb->insns, insn) { if (insn == x) return true; if (insn == y) return false; } END_FOR_EACH_PTR(insn); return false; } /// // check if it safe for a pseudo to be used by an instruction static inline bool can_move_to(pseudo_t src, struct instruction *dst) { struct basic_block *bbs, *bbd; struct instruction *def; if (!one_use(dst->target)) return false; if (src->type != PSEUDO_REG) return true; def = src->def; if (dst == def) return false; bbs = def->bb; bbd = dst->bb; if (bbs == bbd) return insn_before(bbs, def, dst); else return domtree_dominates(bbs, bbd); } 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 (constant(def->src2) && constant(insn->src2)) { // (x # C) # K --> x # eval(C # K) insn->src2 = eval_op(insn->opcode, insn->size, insn->src2, def->src2); return replace_pseudo(insn, &insn->src1, def->src1); } if (!one_use(def->target)) return 0; switch_pseudo(def, &def->src1, insn, &insn->src2); return REPEAT_CSE; } static int simplify_add_one_side(struct instruction *insn, pseudo_t *p1, pseudo_t *p2) { struct instruction *defr = NULL; struct instruction *def; pseudo_t src1 = *p1; pseudo_t src2 = *p2; switch (DEF_OPCODE(def, src1)) { case OP_MUL: if (DEF_OPCODE(defr, *p2) == OP_MUL) { if (defr->src2 == def->src2 && can_move_to(def->src2, defr)) { // ((x * z) + (y * z)) into ((x + y) * z) swap_insn(insn, defr, def->src1, defr->src1, def->src2); return REPEAT_CSE; } if (defr->src1 == def->src1 && can_move_to(def->src1, defr)) { // ((z * x) + (z * y)) into ((x + y) * z) swap_insn(insn, defr, def->src2, defr->src2, def->src1); return REPEAT_CSE; } if (defr->src1 == def->src2 && can_move_to(def->src1, defr)) { // ((x * z) + (z * y)) into ((x + y) * z) swap_insn(insn, defr, def->src1, defr->src2, def->src2); return REPEAT_CSE; } } break; case OP_NEG: // (-x + y) --> (y - x) return replace_binop(insn, OP_SUB, &insn->src1, src2, &insn->src2, def->src); case OP_SUB: if (def->src2 == src2) // (x - y) + y --> x return replace_with_pseudo(insn, def->src1); break; } return 0; } static int simplify_add(struct instruction *insn) { return simplify_add_one_side(insn, &insn->src1, &insn->src2) || simplify_add_one_side(insn, &insn->src2, &insn->src1); } static int simplify_sub(struct instruction *insn) { pseudo_t src1 = insn->src1; pseudo_t src2 = insn->src2; struct instruction *def; switch (DEF_OPCODE(def, src1)) { case OP_ADD: if (def->src1 == src2) // (x + y) - x --> y return replace_with_pseudo(insn, def->src2); if (def->src2 == src2) // (x + y) - y --> x return replace_with_pseudo(insn, def->src1); break; } switch (DEF_OPCODE(def, src2)) { case OP_ADD: if (src1 == def->src1) // x - (x + z) --> -z return replace_with_unop(insn, OP_NEG, def->src2); if (src1 == def->src2) // x - (y + x) --> -y return replace_with_unop(insn, OP_NEG, def->src1); break; case OP_NEG: // (x - -y) --> (x + y) insn->opcode = OP_ADD; return replace_pseudo(insn, &insn->src2, def->src); } return 0; } static int simplify_compare(struct instruction *insn) { pseudo_t src1 = insn->src1; pseudo_t src2 = insn->src2; struct instruction *def = NULL; unsigned int osize; pseudo_t src; switch (DEF_OPCODE(def, src1)) { case OP_SEXT: case OP_ZEXT: osize = def->orig_type->bit_size; if ((src = is_same_op(src2, def->opcode, osize))) { const struct opcode_table *op = &opcode_table[insn->opcode]; if ((def->opcode == OP_ZEXT) && (op->flags & OPF_SIGNED)) insn->opcode = op->sign; insn->itype = def->orig_type; replace_pseudo(insn, &insn->src1, def->src); return replace_pseudo(insn, &insn->src2, src); } break; } return 0; } static int simplify_and_one_side(struct instruction *insn, pseudo_t *p1, pseudo_t *p2) { struct instruction *def, *defr = NULL; pseudo_t src1 = *p1; switch (DEF_OPCODE(def, src1)) { case OP_NOT: if (def->src == *p2) return replace_with_value(insn, 0); break; case OP_BINCMP ... OP_BINCMP_END: if (DEF_OPCODE(defr, *p2) == opcode_negate(def->opcode)) { if (def->src1 == defr->src1 && def->src2 == defr->src2) return replace_with_value(insn, 0); } if (def->opcode == OP_SET_GE && is_zero(def->src2)) { switch (DEF_OPCODE(defr, *p2)) { case OP_SET_LE: if (!is_positive(defr->src2, defr->itype->bit_size)) break; // (x >= 0) && (x <= C) --> (x u<= C) insn->itype = defr->itype; replace_binop(insn, OP_SET_BE, &insn->src1, defr->src1, &insn->src2, defr->src2); return REPEAT_CSE; } } break; case OP_OR: if (DEF_OPCODE(defr, *p2) == OP_OR) { if (defr->src2 == def->src2 && can_move_to(def->src2, defr)) { // ((x | z) & (y | z)) into ((x & y) | z) swap_insn(insn, defr, def->src1, defr->src1, def->src2); return REPEAT_CSE; } if (defr->src1 == def->src1 && can_move_to(def->src1, defr)) { // ((z | x) & (z | y)) into ((x & y) | z) swap_insn(insn, defr, def->src2, defr->src2, def->src1); return REPEAT_CSE; } if (defr->src1 == def->src2 && can_move_to(def->src1, defr)) { // ((x | z) & (z | y)) into ((x & y) | z) swap_insn(insn, defr, def->src1, defr->src2, def->src2); return REPEAT_CSE; } } break; case OP_SHL: case OP_LSR: case OP_ASR: if (DEF_OPCODE(defr, *p2) == def->opcode && defr->src2 == def->src2) { if (can_move_to(def->src1, defr)) { // SHIFT(x, s) & SHIFT(y, s) --> SHIFT((x & y), s) swap_insn(insn, defr, def->src1, defr->src1, def->src2); return REPEAT_CSE; } } break; } return 0; } static int simplify_and(struct instruction *insn) { return simplify_and_one_side(insn, &insn->src1, &insn->src2) || simplify_and_one_side(insn, &insn->src2, &insn->src1); } static int simplify_ior_one_side(struct instruction *insn, pseudo_t *p1, pseudo_t *p2) { struct instruction *def, *defr = NULL; pseudo_t src1 = *p1; switch (DEF_OPCODE(def, src1)) { case OP_AND: if (DEF_OPCODE(defr, *p2) == OP_AND) { if (defr->src2 == def->src2 && can_move_to(def->src2, defr)) { // ((x & z) | (y & z)) into ((x | y) & z) swap_insn(insn, defr, def->src1, defr->src1, def->src2); return REPEAT_CSE; } if (defr->src1 == def->src1 && can_move_to(def->src1, defr)) { // ((z & x) | (z & y)) into ((x | y) & z) swap_insn(insn, defr, def->src2, defr->src2, def->src1); return REPEAT_CSE; } if (defr->src1 == def->src2 && can_move_to(def->src1, defr)) { // ((x & z) | (z & y)) into ((x | y) & z) swap_insn(insn, defr, def->src1, defr->src2, def->src2); return REPEAT_CSE; } } break; case OP_NOT: if (def->src == *p2) return replace_with_value(insn, bits_mask(insn->size)); break; case OP_BINCMP ... OP_BINCMP_END: if (DEF_OPCODE(defr, *p2) == opcode_negate(def->opcode)) { if (def->src1 == defr->src1 && def->src2 == defr->src2) return replace_with_value(insn, 1); } break; case OP_SHL: case OP_LSR: case OP_ASR: if (DEF_OPCODE(defr, *p2) == def->opcode && defr->src2 == def->src2) { if (can_move_to(def->src1, defr)) { // SHIFT(x, s) | SHIFT(y, s) --> SHIFT((x | y), s) swap_insn(insn, defr, def->src1, defr->src1, def->src2); return REPEAT_CSE; } } break; } return 0; } static int simplify_ior(struct instruction *insn) { return simplify_ior_one_side(insn, &insn->src1, &insn->src2) || simplify_ior_one_side(insn, &insn->src2, &insn->src1); } static int simplify_xor_one_side(struct instruction *insn, pseudo_t *p1, pseudo_t *p2) { struct instruction *def, *defr = NULL; pseudo_t src1 = *p1; switch (DEF_OPCODE(def, src1)) { case OP_AND: if (DEF_OPCODE(defr, *p2) == OP_AND) { if (defr->src2 == def->src2 && can_move_to(def->src2, defr)) { // ((x & z) ^ (y & z)) into ((x ^ y) & z) swap_insn(insn, defr, def->src1, defr->src1, def->src2); return REPEAT_CSE; } if (defr->src1 == def->src1 && can_move_to(def->src1, defr)) { // ((z & x) ^ (z & y)) into ((x ^ y) & z) swap_insn(insn, defr, def->src2, defr->src2, def->src1); return REPEAT_CSE; } if (defr->src1 == def->src2 && can_move_to(def->src1, defr)) { // ((x & z) ^ (z & y)) into ((x ^ y) & z) swap_insn(insn, defr, def->src1, defr->src2, def->src2); return REPEAT_CSE; } } break; case OP_NOT: if (def->src == *p2) return replace_with_value(insn, bits_mask(insn->size)); break; case OP_BINCMP ... OP_BINCMP_END: if (DEF_OPCODE(defr, *p2) == opcode_negate(def->opcode)) { if (def->src1 == defr->src1 && def->src2 == defr->src2) return replace_with_value(insn, 1); } break; case OP_SHL: case OP_LSR: case OP_ASR: if (DEF_OPCODE(defr, *p2) == def->opcode && defr->src2 == def->src2) { if (can_move_to(def->src1, defr)) { // SHIFT(x, s) ^ SHIFT(y, s) --> SHIFT((x ^ y), s) swap_insn(insn, defr, def->src1, defr->src1, def->src2); return REPEAT_CSE; } } break; } return 0; } static int simplify_xor(struct instruction *insn) { return simplify_xor_one_side(insn, &insn->src1, &insn->src2) || simplify_xor_one_side(insn, &insn->src2, &insn->src1); } 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; case OP_SEXT: mask = 1ULL << (insn->orig_type->bit_size-1); if (val & mask) val |= ~(mask | (mask-1)); /* fall through */ case OP_ZEXT: case OP_TRUNC: res = val; break; default: return 0; } mask = 1ULL << (insn->size-1); res &= mask | (mask-1); return replace_with_value(insn, res); } static int simplify_unop(struct instruction *insn) { struct instruction *def; pseudo_t src = insn->src; if (constant(src)) return simplify_constant_unop(insn); switch (insn->opcode) { case OP_NOT: switch (DEF_OPCODE(def, src)) { case OP_ADD: if (!constant(def->src2)) break; insn->opcode = OP_SUB; // ~(x + C) --> ~C - x src = eval_unop(OP_NOT, insn->size, def->src2); use_pseudo(insn, def->src1, &insn->src2); return replace_pseudo(insn, &insn->src1, src); case OP_NEG: insn->opcode = OP_SUB; // ~(-x) --> x - 1 insn->src2 = value_pseudo(1); return replace_pseudo(insn, &insn->src1, def->src); case OP_NOT: // ~(~x) --> x return replace_with_pseudo(insn, def->src); case OP_SUB: if (!constant(def->src1)) break; insn->opcode = OP_ADD; // ~(C - x) --> x + ~C insn->src2 = eval_unop(OP_NOT, insn->size, def->src1); return replace_pseudo(insn, &insn->src1, def->src2); case OP_XOR: if (!constant(def->src2)) break; insn->opcode = OP_XOR; // ~(x ^ C) --> x ^ ~C insn->src2 = eval_unop(OP_NOT, insn->size, def->src2); return replace_pseudo(insn, &insn->src1, def->src1); } break; case OP_NEG: switch (DEF_OPCODE(def, src)) { case OP_ADD: if (!constant(def->src2)) break; insn->opcode = OP_SUB; // -(x + C) --> (-C - x) src = eval_unop(OP_NEG, insn->size, def->src2); use_pseudo(insn, def->src1, &insn->src2); return replace_pseudo(insn, &insn->src1, src); case OP_NEG: // -(-x) --> x return replace_with_pseudo(insn, def->src); case OP_NOT: insn->opcode = OP_ADD; // -(~x) --> x + 1 insn->src2 = value_pseudo(1); return replace_pseudo(insn, &insn->src1, def->src); case OP_SUB: insn->opcode = OP_SUB; // -(x - y) --> y - x use_pseudo(insn, def->src1, &insn->src2); return replace_pseudo(insn, &insn->src1, def->src2); } 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; } 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 || new == addr) { 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; warning(insn->pos, "crazy programmer"); replace_pseudo(insn, &insn->src, VOID); return 0; } insn->offset += off->value; replace_pseudo(insn, &insn->src, new); return REPEAT_CSE; } /// // simplify memops instructions // // :note: 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 int simplify_cast(struct instruction *insn) { unsigned long long mask; struct instruction *def, *def2; pseudo_t src = insn->src; pseudo_t val; int osize; int size; /* A cast of a constant? */ if (constant(src)) return simplify_constant_unop(insn); // can merge with the previous instruction? size = insn->size; def = src->def; switch (def_opcode(src)) { case OP_AND: val = def->src2; if (val->type != PSEUDO_VAL) break; /* A cast of a AND might be a no-op.. */ switch (insn->opcode) { case OP_TRUNC: if (!one_use(src)) break; def->opcode = OP_TRUNC; def->orig_type = def->type; def->type = insn->type; def->size = size; insn->opcode = OP_AND; mask = val->value; mask &= (1ULL << size) - 1; insn->src2 = value_pseudo(mask); return REPEAT_CSE; case OP_SEXT: if (val->value & (1 << (def->size - 1))) break; // OK, sign bit is 0 case OP_ZEXT: if (!one_use(src)) break; // transform: // and.n %b <- %a, M // *ext.m %c <- (n) %b // into: // zext.m %b <- %a // and.m %c <- %b, M // For ZEXT, the mask will always be small // enough. For SEXT, it can only be done if // the mask force the sign bit to 0. def->opcode = OP_ZEXT; def->orig_type = insn->orig_type; def->type = insn->type; def->size = insn->size; insn->opcode = OP_AND; insn->src2 = val; return REPEAT_CSE; } break; case OP_FPCMP ... OP_BINCMP_END: switch (insn->opcode) { case OP_SEXT: if (insn->size == 1) break; /* fall through */ case OP_ZEXT: case OP_TRUNC: // simplify: // setcc.n %t <- %a, %b // zext.m %r <- (n) %t // into: // setcc.m %r <- %a, %b // and same for s/zext/trunc/ insn->opcode = def->opcode; insn->itype = def->itype; use_pseudo(insn, def->src2, &insn->src2); return replace_pseudo(insn, &insn->src1, def->src1); } break; case OP_NOT: switch (insn->opcode) { case OP_TRUNC: if (one_use(src)) { // TRUNC(NOT(x)) --> NOT(TRUNC(x)) insn->opcode = OP_NOT; def->orig_type = def->type; def->opcode = OP_TRUNC; def->type = insn->type; def->size = insn->size; return REPEAT_CSE; } break; } break; case OP_OR: switch (insn->opcode) { case OP_TRUNC: mask = bits_mask(insn->size); return simplify_mask_or(insn, mask, def); } break; case OP_LSR: case OP_SHL: if (insn->opcode != OP_TRUNC) break; mask = bits_mask(insn->size); return simplify_mask_shift(def, mask); case OP_TRUNC: switch (insn->opcode) { case OP_TRUNC: insn->orig_type = def->orig_type; return replace_pseudo(insn, &insn->src1, def->src); case OP_SEXT: if (size != def->orig_type->bit_size) break; if (DEF_OPCODE(def2, def->src) != OP_LSR) break; if (def2->src2 != value_pseudo(size - def->size)) break; // SEXT(TRUNC(LSR(x, N))) --> ASR(x, N) insn->opcode = OP_ASR; insn->src2 = def2->src2; return replace_pseudo(insn, &insn->src1, def2->src1); case OP_ZEXT: if (size != def->orig_type->bit_size) break; insn->opcode = OP_AND; insn->src2 = value_pseudo((1ULL << def->size) - 1); return replace_pseudo(insn, &insn->src1, def->src); } break; case OP_ZEXT: switch (insn->opcode) { case OP_SEXT: insn->opcode = OP_ZEXT; /* fall through */ case OP_ZEXT: insn->orig_type = def->orig_type; return replace_pseudo(insn, &insn->src, def->src); } /* fall through */ case OP_SEXT: switch (insn->opcode) { case OP_TRUNC: osize = def->orig_type->bit_size; if (size == osize) return replace_with_pseudo(insn, def->src); if (size > osize) insn->opcode = def->opcode; insn->orig_type = def->orig_type; return replace_pseudo(insn, &insn->src, def->src); } switch (insn->opcode) { case OP_SEXT: insn->orig_type = def->orig_type; return replace_pseudo(insn, &insn->src, def->src); } break; } return 0; } static int simplify_select(struct instruction *insn) { pseudo_t cond, src1, src2; struct instruction *def; cond = insn->src1; src1 = insn->src2; src2 = insn->src3; if (constant(cond)) return replace_with_pseudo(insn, cond->value ? src1 : src2); if (src1 == src2) return replace_with_pseudo(insn, src1); 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->itype = insn->type; /* insn->src1 is already cond */ insn->src2 = src1; /* Zero */ return REPEAT_CSE; } } if (cond == src2 && is_zero(src1)) // SEL(x, 0, x) --> 0 return replace_with_pseudo(insn, src1); if (cond == src1 && is_zero(src2)) // SEL(x, x, 0) --> x return replace_with_pseudo(insn, cond); switch (DEF_OPCODE(def, cond)) { case OP_SET_EQ: if (src1 == def->src1 && src2 == def->src2) return replace_with_pseudo(insn, src2); // SEL(x==y,x,y) --> y if (src2 == def->src1 && src1 == def->src2) return replace_with_pseudo(insn, src2); // SEL(y==x,x,y) --> y break; case OP_SET_NE: if (src1 == def->src1 && src2 == def->src2) return replace_with_pseudo(insn, src1); // SEL(x!=y,x,y) --> x if (src2 == def->src1 && src1 == def->src2) return replace_with_pseudo(insn, src1); // SEL(y!=x,x,y) --> x break; case OP_SET_LE: case OP_SET_LT: case OP_SET_BE: case OP_SET_B: if (!one_use(cond)) break; // SEL(x {<,<=} y, a, b) --> SEL(x {>=,>} y, b, a) def->opcode = opcode_negate(def->opcode); return switch_pseudo(insn, &insn->src2, insn, &insn->src3); case OP_SET_GT: if (one_use(cond) && is_zero(def->src2)) { if (is_negate_of(src2, src1)) // SEL(x > 0, a, -a) --> SEL(x >= 0, a, -a) return replace_opcode(def, OP_SET_GE); } break; case OP_SEL: if (constant(def->src2) && constant(def->src3)) { // Is the def of the conditional another select? // And if that one results in a "zero or not", use the // original conditional instead. // SEL(SEL(x, C, 0), y, z) --> SEL(x, y, z) // SEL(SEL(x, C, 0), C, 0) --> SEL(x, C, 0) == cond // SEL(SEL(x, 0, C), y, z) --> SEL(x, z, y) // SEL(SEL(x, C1, C2), y, z) --> y if (!def->src3->value) { if ((src1 == def->src2) && (src2 == def->src3)) return replace_with_pseudo(insn, cond); return replace_pseudo(insn, &insn->cond, def->cond); } if (!def->src2->value) { switch_pseudo(insn, &insn->src2, insn, &insn->src3); return replace_pseudo(insn, &insn->cond, def->cond); } // both values must be non-zero return replace_with_pseudo(insn, src1); } case OP_AND: if (is_pow2(def->src2) && is_pow2(src1) && is_zero(src2) && insn->size == def->size && one_use(cond)) { unsigned s1 = log2_exact(def->src2->value); unsigned s2 = log2_exact(insn->src2->value); unsigned shift; if (s1 == s2) return replace_with_pseudo(insn, cond); // SEL(x & A, B, 0) --> SHIFT(x & A, S) insn->opcode = (s1 < s2) ? OP_SHL : OP_LSR; shift = (s1 < s2) ? (s2 - s1) : (s1 - s2); insn->src2 = value_pseudo(shift); return REPEAT_CSE; } break; } switch (DEF_OPCODE(def, src1)) { case OP_ADD: case OP_OR: case OP_XOR: if ((def->src1 == src2) && can_move_to(cond, def)) { // SEL(x, OP(y,z), y) --> OP(SEL(x, z, 0), y) swap_select(insn, def, cond, def->src2, value_pseudo(0), src2); return REPEAT_CSE; } if ((def->src2 == src2) && can_move_to(cond, def)) { // SEL(x, OP(z,y), y) --> OP(SEL(x, z, 0), y) swap_select(insn, def, cond, def->src1, value_pseudo(0), src2); return REPEAT_CSE; } break; } switch (DEF_OPCODE(def, src2)) { case OP_ADD: case OP_OR: case OP_XOR: if ((def->src1 == src1) && can_move_to(cond, def)) { // SEL(x, y, OP(y,z)) --> OP(SEL(x, 0, z), y) swap_select(insn, def, cond, value_pseudo(0), def->src2, src1); return REPEAT_CSE; } if ((def->src2 == src1) && can_move_to(cond, def)) { // SEL(x, y, OP(z,y)) --> OP(SEL(x, 0, z), y) swap_select(insn, def, cond, value_pseudo(0), def->src1, src1); return REPEAT_CSE; } break; } 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, struct instruction *def, pseudo_t newcond) { replace_pseudo(br, &br->cond, newcond); if (def->opcode == OP_SET_EQ) { struct basic_block *tmp = br->bb_true; br->bb_true = br->bb_false; br->bb_false = tmp; } return REPEAT_CSE; } static int simplify_branch(struct instruction *insn) { pseudo_t cond = insn->cond; /* Constant conditional */ if (constant(cond)) return convert_to_jump(insn, cond->value ? insn->bb_true : insn->bb_false); /* Same target? */ if (insn->bb_true == insn->bb_false) return convert_to_jump(insn, insn->bb_true); /* 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, def, def->src2); if (constant(def->src2) && !def->src2->value) return simplify_cond_branch(insn, 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) return convert_to_jump(insn, insn->bb_false); if (val1 && val2) return convert_to_jump(insn, insn->bb_true); if (val2) { struct basic_block *tmp = insn->bb_true; insn->bb_true = insn->bb_false; insn->bb_false = tmp; } return replace_pseudo(insn, &insn->cond, def->src1); } } if (def->opcode == OP_SEXT || def->opcode == OP_ZEXT) return replace_pseudo(insn, &insn->cond, def->src); } 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: return convert_to_jump(insn, jmp->target); } static struct basic_block *is_label(pseudo_t pseudo) { struct instruction *def; if (DEF_OPCODE(def, pseudo) != OP_LABEL) return NULL; return def->bb_true; } static int simplify_cgoto(struct instruction *insn) { struct basic_block *target, *bb = insn->bb; struct basic_block *bbt, *bbf; struct instruction *def; struct multijmp *jmp; switch (DEF_OPCODE(def, insn->src)) { case OP_SEL: // CGOTO(SEL(x, L1, L2)) --> CBR x, L1, L2 if ((bbt = is_label(def->src2)) && (bbf = is_label(def->src3))) { insn->opcode = OP_CBR; insn->bb_true = bbt; insn->bb_false = bbf; return replace_pseudo(insn, &insn->src1, def->cond); } break; case OP_LABEL: target = def->bb_true; if (!target->ep) return 0; FOR_EACH_PTR(insn->multijmp_list, jmp) { if (jmp->target == target) continue; remove_bb_from_list(&jmp->target->parents, bb, 1); remove_bb_from_list(&bb->children, jmp->target, 1); DELETE_CURRENT_PTR(jmp); } END_FOR_EACH_PTR(jmp); kill_use(&insn->src); insn->opcode = OP_BR; insn->bb_true = target; return REPEAT_CSE|REPEAT_CFG_CLEANUP; } return 0; } static int simplify_setval(struct instruction *insn) { struct expression *val = insn->val; switch (val->type) { case EXPR_LABEL: insn->opcode = OP_LABEL; insn->bb_true = val->symbol->bb_target; return REPEAT_CSE; default: break; } return 0; } int simplify_instruction(struct instruction *insn) { unsigned flags; int changed = 0; flags = opcode_table[insn->opcode].flags; if (flags & OPF_TARGET) { if (!has_users(insn->target)) return kill_instruction(insn); } if (flags & OPF_COMMU) canonicalize_commutative(insn) ; if (flags & OPF_COMPARE) canonicalize_compare(insn) ; if (flags & OPF_BINOP) { if ((changed = simplify_binop(insn))) return changed; } if (flags & OPF_ASSOC) { if ((changed = simplify_associative_binop(insn))) return changed; } if (flags & OPF_UNOP) { if ((changed = simplify_unop(insn))) return changed; } switch (insn->opcode) { case OP_ADD: return simplify_add(insn); case OP_SUB: return simplify_sub(insn); case OP_AND: return simplify_and(insn); case OP_OR: return simplify_ior(insn); case OP_XOR: return simplify_xor(insn); case OP_MUL: case OP_SHL: case OP_LSR: case OP_ASR: case OP_NOT: case OP_NEG: case OP_DIVU: case OP_DIVS: case OP_MODU: case OP_MODS: break; case OP_BINCMP ... OP_BINCMP_END: return simplify_compare(insn); case OP_LOAD: case OP_STORE: return simplify_memop(insn); case OP_SYMADDR: return replace_with_pseudo(insn, insn->src); case OP_SEXT: case OP_ZEXT: case OP_TRUNC: return simplify_cast(insn); case OP_FNEG: case OP_FCVTU: case OP_FCVTS: case OP_UCVTF: case OP_SCVTF: case OP_FCVTF: case OP_PTRCAST: break; case OP_UTPTR: case OP_PTRTU: return replace_with_pseudo(insn, insn->src); case OP_SLICE: break; case OP_SETVAL: return simplify_setval(insn); case OP_LABEL: case OP_SETFVAL: break; case OP_PHI: return clean_up_phi(insn); case OP_PHISOURCE: break; case OP_SEL: return simplify_select(insn); case OP_CBR: return simplify_branch(insn); case OP_SWITCH: return simplify_switch(insn); case OP_COMPUTEDGOTO: return simplify_cgoto(insn); case OP_RANGE: return simplify_range(insn); case OP_FADD: case OP_FSUB: case OP_FMUL: case OP_FDIV: break; } return 0; } sparse-0.6.4/simplify.h000066400000000000000000000003001411531012200150020ustar00rootroot00000000000000#ifndef SIMPLIFY_H #define SIMPLIFY_H #include "linearize.h" int simplify_instruction(struct instruction *insn); int replace_with_pseudo(struct instruction *insn, pseudo_t pseudo); #endif sparse-0.6.4/sort.c000066400000000000000000000131331411531012200141400ustar00rootroot00000000000000/* * 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_NOTAG(b1, b1->nr - 1), PTR_ENTRY_NOTAG(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_NOTAG(b1,i1); const void *d2 = PTR_ENTRY_NOTAG(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.6.4/sparse-llvm-dis000077500000000000000000000004171411531012200157560ustar00rootroot00000000000000#!/bin/sh # # For testing sparse-llvm emitted bytecode set +e DIS=$("${LLVM_CONFIG:-llvm-config}" --bindir)/llvm-dis if [ $# -eq 0 ]; then echo "$(basename $0): no input files" exit 1 fi DIRNAME=$(dirname $0) $DIRNAME/sparse-llvm "$@" | "$DIS" | grep -v '^target ' sparse-0.6.4/sparse-llvm.c000066400000000000000000000761571411531012200154350ustar00rootroot00000000000000/* * 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; LLVMValueRef fn; LLVMModuleRef module; }; static LLVMTypeRef symbol_type(struct symbol *sym); static LLVMTypeRef func_return_type(struct symbol *sym) { return symbol_type(sym->ctype.base_type); } static LLVMTypeRef sym_func_type(struct symbol *sym) { int n_arg = symbol_list_size(sym->arguments); LLVMTypeRef *arg_type = calloc(n_arg, sizeof(LLVMTypeRef)); LLVMTypeRef ret_type = func_return_type(sym); struct symbol *arg; int idx = 0; FOR_EACH_PTR(sym->arguments, arg) { struct symbol *arg_sym = arg->ctype.base_type; arg_type[idx++] = symbol_type(arg_sym); } END_FOR_EACH_PTR(arg); return LLVMFunctionType(ret_type, arg_type, n_arg, sym->variadic); } static LLVMTypeRef sym_array_type(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(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(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(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(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(struct symbol *sym) { LLVMTypeRef type; /* 'void *' is treated like 'char *' */ if (is_void_type(sym->ctype.base_type)) type = LLVMInt8Type(); else type = symbol_type(sym->ctype.base_type); return LLVMPointerType(type, 0); } static LLVMTypeRef sym_basetype_type(struct symbol *sym) { LLVMTypeRef ret = NULL; if (is_float_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(struct symbol *sym) { LLVMTypeRef ret = NULL; /* don't cache the result for SYM_NODE */ if (sym->type == SYM_NODE) return symbol_type(sym->ctype.base_type); if (sym->aux) return sym->aux; switch (sym->type) { case SYM_BITFIELD: ret = LLVMIntType(sym->bit_size); break; case SYM_RESTRICT: case SYM_ENUM: ret = symbol_type(sym->ctype.base_type); break; case SYM_BASETYPE: ret = sym_basetype_type(sym); break; case SYM_PTR: ret = sym_ptr_type(sym); break; case SYM_UNION: ret = sym_union_type(sym); break; case SYM_STRUCT: ret = sym_struct_type(sym); break; case SYM_ARRAY: ret = sym_array_type(sym); break; case SYM_FN: ret = sym_func_type(sym); break; default: assert(0); } /* cache the result */ sym->aux = ret; return ret; } static LLVMTypeRef insn_symbol_type(struct instruction *insn) { if (insn->type) return symbol_type(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 const char *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_PHI: snprintf(buf, MAX_PSEUDO_NAME, "PHI%d.", pseudo->nr); break; case PSEUDO_SYM: case PSEUDO_VAL: case PSEUDO_ARG: case PSEUDO_VOID: buf[0] = '\0'; break; case PSEUDO_UNDEF: assert(0); break; default: assert(0); } return buf; } static LLVMValueRef get_sym_value(LLVMModuleRef module, struct symbol *sym) { const char *name = show_ident(sym->ident); LLVMTypeRef type = symbol_type(sym); LLVMValueRef result = NULL; struct expression *expr; assert(sym->bb_target == NULL); expr = sym->initializer; if (expr && !sym->ident) { 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(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)); return result; } default: break; } } if (LLVMGetTypeKind(type) == LLVMFunctionTypeKind) { result = LLVMGetNamedFunction(module, name); if (!result) result = LLVMAddFunction(module, name, type); } else { result = LLVMGetNamedGlobal(module, name); if (!result) result = LLVMAddGlobal(module, type, name); } return result; } static LLVMValueRef constant_value(unsigned long long val, LLVMTypeRef dtype) { LLVMValueRef result; switch (LLVMGetTypeKind(dtype)) { case LLVMPointerTypeKind: if (val != 0) { // for example: ... = (void*) 0x123; LLVMTypeRef itype = LLVMIntType(bits_in_pointer); result = LLVMConstInt(itype, val, 1); result = LLVMConstIntToPtr(result, dtype); } else { result = LLVMConstPointerNull(dtype); } break; case LLVMIntegerTypeKind: result = LLVMConstInt(dtype, val, 1); break; case LLVMArrayTypeKind: case LLVMStructTypeKind: if (val != 0) return NULL; result = LLVMConstNull(dtype); break; default: return NULL; } return result; } static LLVMValueRef val_to_value(unsigned long long val, struct symbol *ctype) { LLVMValueRef result; LLVMTypeRef dtype; assert(ctype); dtype = symbol_type(ctype); result = constant_value(val, dtype); if (result) return result; sparse_error(ctype->pos, "no value possible for %s", show_typename(ctype)); return LLVMGetUndef(symbol_type(ctype)); } static LLVMValueRef pseudo_to_value(struct function *fn, struct symbol *ctype, pseudo_t pseudo) { LLVMValueRef result = NULL; switch (pseudo->type) { case PSEUDO_REG: result = pseudo->priv; break; case PSEUDO_SYM: result = get_sym_value(fn->module, pseudo->sym); break; case PSEUDO_VAL: result = val_to_value(pseudo->value, ctype); 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; case PSEUDO_UNDEF: result = LLVMGetUndef(symbol_type(ctype)); break; default: assert(0); } return result; } static LLVMValueRef pseudo_to_rvalue(struct function *fn, struct symbol *ctype, pseudo_t pseudo) { LLVMValueRef val = pseudo_to_value(fn, ctype, pseudo); LLVMTypeRef dtype = symbol_type(ctype); char name[MAX_PSEUDO_NAME]; pseudo_name(pseudo, name); return LLVMBuildBitCast(fn->builder, val, dtype, name); } static LLVMValueRef value_to_ivalue(struct function *fn, struct symbol *ctype, LLVMValueRef val) { const char *name = LLVMGetValueName(val); LLVMTypeRef dtype = symbol_type(ctype); if (LLVMGetTypeKind(LLVMTypeOf(val)) == LLVMPointerTypeKind) { LLVMTypeRef dtype = LLVMIntType(bits_in_pointer); val = LLVMBuildPtrToInt(fn->builder, val, dtype, name); } if (ctype && is_int_type(ctype)) { val = LLVMBuildIntCast(fn->builder, val, dtype, name); } return val; } static LLVMValueRef value_to_pvalue(struct function *fn, struct symbol *ctype, LLVMValueRef val) { const char *name = LLVMGetValueName(val); LLVMTypeRef dtype = symbol_type(ctype); assert(is_ptr_type(ctype)); switch (LLVMGetTypeKind(LLVMTypeOf(val))) { case LLVMIntegerTypeKind: val = LLVMBuildIntToPtr(fn->builder, val, dtype, name); break; case LLVMPointerTypeKind: val = LLVMBuildBitCast(fn->builder, val, dtype, name); break; default: break; } return val; } static LLVMValueRef adjust_type(struct function *fn, struct symbol *ctype, LLVMValueRef val) { if (is_int_type(ctype)) return value_to_ivalue(fn, ctype, val); if (is_ptr_type(ctype)) return value_to_pvalue(fn, ctype, val); return val; } /* * Get the LLVMValue corresponding to the pseudo * and force the type corresponding to ctype. */ static LLVMValueRef get_operand(struct function *fn, struct symbol *ctype, pseudo_t pseudo) { LLVMValueRef target = pseudo_to_value(fn, ctype, pseudo); return adjust_type(fn, ctype, target); } /* * Get the LLVMValue corresponding to the pseudo * and force the type corresponding to ctype but * map all pointers to intptr_t. */ static LLVMValueRef get_ioperand(struct function *fn, struct symbol *ctype, pseudo_t pseudo) { LLVMValueRef target = pseudo_to_value(fn, ctype, pseudo); return value_to_ivalue(fn, ctype, target); } 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; const char *name = LLVMGetValueName(off); /* convert base to char* type */ base = LLVMBuildPointerCast(builder, base, bytep, name); /* addr = base + off */ addr = LLVMBuildInBoundsGEP(builder, base, &off, 1, name); /* convert back to the actual pointer type */ addr = LLVMBuildPointerCast(builder, addr, type, name); return addr; } static LLVMRealPredicate translate_fop(int opcode) { static const LLVMRealPredicate trans_tbl[] = { [OP_FCMP_ORD] = LLVMRealORD, [OP_FCMP_OEQ] = LLVMRealOEQ, [OP_FCMP_ONE] = LLVMRealONE, [OP_FCMP_OLE] = LLVMRealOLE, [OP_FCMP_OGE] = LLVMRealOGE, [OP_FCMP_OLT] = LLVMRealOLT, [OP_FCMP_OGT] = LLVMRealOGT, [OP_FCMP_UEQ] = LLVMRealUEQ, [OP_FCMP_UNE] = LLVMRealUNE, [OP_FCMP_ULE] = LLVMRealULE, [OP_FCMP_UGE] = LLVMRealUGE, [OP_FCMP_ULT] = LLVMRealULT, [OP_FCMP_UGT] = LLVMRealUGT, [OP_FCMP_UNO] = LLVMRealUNO, }; 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 = get_ioperand(fn, insn->type, insn->src1); rhs = get_ioperand(fn, insn->type, insn->src2); pseudo_name(insn->target, target_name); switch (insn->opcode) { /* Binary */ case OP_ADD: target = LLVMBuildAdd(fn->builder, lhs, rhs, target_name); break; case OP_SUB: target = LLVMBuildSub(fn->builder, lhs, rhs, target_name); break; case OP_MUL: target = LLVMBuildMul(fn->builder, lhs, rhs, target_name); break; case OP_DIVU: target = LLVMBuildUDiv(fn->builder, lhs, rhs, target_name); break; case OP_DIVS: assert(!is_float_type(insn->type)); target = LLVMBuildSDiv(fn->builder, lhs, rhs, target_name); break; case OP_MODU: assert(!is_float_type(insn->type)); target = LLVMBuildURem(fn->builder, lhs, rhs, target_name); break; case OP_MODS: assert(!is_float_type(insn->type)); target = LLVMBuildSRem(fn->builder, lhs, rhs, target_name); break; case OP_SHL: assert(!is_float_type(insn->type)); target = LLVMBuildShl(fn->builder, lhs, rhs, target_name); break; case OP_LSR: assert(!is_float_type(insn->type)); target = LLVMBuildLShr(fn->builder, lhs, rhs, target_name); break; case OP_ASR: assert(!is_float_type(insn->type)); target = LLVMBuildAShr(fn->builder, lhs, rhs, target_name); break; /* floating-point */ case OP_FADD: target = LLVMBuildFAdd(fn->builder, lhs, rhs, target_name); break; case OP_FSUB: target = LLVMBuildFSub(fn->builder, lhs, rhs, target_name); break; case OP_FMUL: target = LLVMBuildFMul(fn->builder, lhs, rhs, target_name); break; case OP_FDIV: target = LLVMBuildFDiv(fn->builder, lhs, rhs, target_name); break; /* Logical */ case OP_AND: assert(!is_float_type(insn->type)); target = LLVMBuildAnd(fn->builder, lhs, rhs, target_name); break; case OP_OR: assert(!is_float_type(insn->type)); target = LLVMBuildOr(fn->builder, lhs, rhs, target_name); break; case OP_XOR: assert(!is_float_type(insn->type)); target = LLVMBuildXor(fn->builder, lhs, rhs, target_name); break; default: assert(0); break; } target = adjust_type(fn, insn->type, target); 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, NULL, insn->src1); if (insn->src2->type == PSEUDO_VAL) rhs = constant_value(insn->src2->value, LLVMTypeOf(lhs)); else rhs = pseudo_to_value(fn, NULL, insn->src2); if (!rhs) rhs = LLVMGetUndef(symbol_type(insn->type)); pseudo_name(insn->target, target_name); LLVMTypeRef dst_type = insn_symbol_type(insn); switch (LLVMGetTypeKind(LLVMTypeOf(lhs))) { case LLVMPointerTypeKind: lhs = value_to_pvalue(fn, &ptr_ctype, lhs); rhs = value_to_pvalue(fn, &ptr_ctype, rhs); /* fall through */ case LLVMIntegerTypeKind: { LLVMIntPredicate op = translate_op(insn->opcode); if (LLVMGetTypeKind(LLVMTypeOf(rhs)) == LLVMPointerTypeKind) { LLVMTypeRef ltype = LLVMTypeOf(lhs); rhs = LLVMBuildPtrToInt(fn->builder, rhs, ltype, ""); } target = LLVMBuildICmp(fn->builder, op, lhs, rhs, target_name); break; } case LLVMHalfTypeKind: case LLVMFloatTypeKind: case LLVMDoubleTypeKind: case LLVMX86_FP80TypeKind: case LLVMFP128TypeKind: case LLVMPPC_FP128TypeKind: { LLVMRealPredicate op = translate_fop(insn->opcode); target = LLVMBuildFCmp(fn->builder, op, lhs, rhs, target_name); break; } default: assert(0); } 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 = get_operand(fn, insn->type, 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->type, insn->src); as = LLVMGetPointerAddressSpace(LLVMTypeOf(src)); addr_type = LLVMPointerType(insn_symbol_type(insn), as); src = LLVMBuildPointerCast(fn->builder, src, addr_type, LLVMGetValueName(src)); /* 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; char name[MAX_PSEUDO_NAME]; addr = calc_memop_addr(fn, insn); /* perform load */ pseudo_name(insn->target, name); target = LLVMBuildLoad(fn->builder, addr, name); insn->target->priv = target; } static void output_op_store(struct function *fn, struct instruction *insn) { LLVMValueRef addr, target_in; addr = calc_memop_addr(fn, insn); target_in = pseudo_to_rvalue(fn, insn->type, insn->target); /* perform store */ LLVMBuildStore(fn->builder, target_in, addr); } static LLVMValueRef bool_value(struct function *fn, LLVMValueRef value) { if (LLVMTypeOf(value) != LLVMInt1Type()) value = LLVMBuildIsNotNull(fn->builder, value, LLVMGetValueName(value)); return value; } static void output_op_cbr(struct function *fn, struct instruction *br) { LLVMValueRef cond = bool_value(fn, pseudo_to_value(fn, NULL, 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; char name[MAX_PSEUDO_NAME]; src1 = bool_value(fn, pseudo_to_value(fn, NULL, insn->src1)); src2 = get_operand(fn, insn->type, insn->src2); src3 = get_operand(fn, insn->type, insn->src3); pseudo_name(insn->target, name); target = LLVMBuildSelect(fn->builder, src1, src2, src3, name); insn->target->priv = adjust_type(fn, insn->type, 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) { n_jmp += (jmp->end - jmp->begin) + 1; } else /* default case */ def = jmp->target; } END_FOR_EACH_PTR(jmp); sw_val = get_ioperand(fn, insn->type, insn->cond); target = LLVMBuildSwitch(fn->builder, sw_val, def ? def->priv : NULL, n_jmp); FOR_EACH_PTR(insn->multijmp_list, jmp) { long long val; for (val = jmp->begin; val <= jmp->end; val++) { LLVMValueRef Val = val_to_value(val, insn->type); LLVMAddCase(target, Val, jmp->target->priv); } } END_FOR_EACH_PTR(jmp); } static void output_op_call(struct function *fn, struct instruction *insn) { LLVMValueRef target, func; struct symbol *ctype; int n_arg = 0, i; struct pseudo *arg; LLVMValueRef *args; char name[64]; n_arg = pseudo_list_size(insn->arguments); args = calloc(n_arg, sizeof(LLVMValueRef)); PREPARE_PTR_LIST(insn->fntypes, ctype); if (insn->func->type == PSEUDO_REG || insn->func->type == PSEUDO_PHI) func = get_operand(fn, ctype, insn->func); else func = pseudo_to_value(fn, ctype, insn->func); i = 0; FOR_EACH_PTR(insn->arguments, arg) { NEXT_PTR_LIST(ctype); args[i++] = pseudo_to_rvalue(fn, ctype, arg); } END_FOR_EACH_PTR(arg); FINISH_PTR_LIST(ctype); pseudo_name(insn->target, name); target = LLVMBuildCall(fn->builder, func, args, n_arg, name); insn->target->priv = target; } static void output_op_phisrc(struct function *fn, struct instruction *insn) { insn->src->priv = get_operand(fn, insn->type, insn->src); } static void output_op_phi(struct function *fn, struct instruction *insn) { LLVMTypeRef dst_type = insn_symbol_type(insn); insn->target->priv = LLVMBuildPhi(fn->builder, dst_type, ""); } static void output_op_ptrcast(struct function *fn, struct instruction *insn) { LLVMValueRef src, target; LLVMTypeRef dtype; struct symbol *otype = insn->orig_type; LLVMOpcode op; char target_name[64]; src = get_operand(fn, otype, insn->src); pseudo_name(insn->target, target_name); dtype = symbol_type(insn->type); switch (insn->opcode) { case OP_UTPTR: case OP_SEXT: // FIXME assert(is_int_type(otype)); assert(is_ptr_type(insn->type)); op = LLVMIntToPtr; break; case OP_PTRTU: assert(is_ptr_type(otype)); assert(is_int_type(insn->type)); op = LLVMPtrToInt; break; case OP_PTRCAST: case OP_ZEXT: // FIXME assert(is_ptr_type(otype)); assert(is_ptr_type(insn->type)); op = LLVMBitCast; break; default: assert(0); } target = LLVMBuildCast(fn->builder, op, src, dtype, target_name); insn->target->priv = target; } static void output_op_cast(struct function *fn, struct instruction *insn, LLVMOpcode op) { LLVMValueRef src, target; LLVMTypeRef dtype; struct symbol *otype = insn->orig_type; char target_name[64]; if (is_ptr_type(insn->type)) // cast to void* is OP_CAST ... return output_op_ptrcast(fn, insn); assert(is_int_type(insn->type)); src = get_operand(fn, otype, insn->src); pseudo_name(insn->target, target_name); dtype = symbol_type(insn->type); if (is_ptr_type(otype)) { op = LLVMPtrToInt; } else if (is_float_type(otype)) { assert(op == LLVMFPToUI || op == LLVMFPToSI); } else if (is_int_type(otype)) { unsigned int width = otype->bit_size; if (insn->size < width) op = LLVMTrunc; else if (insn->size == width) op = LLVMBitCast; } else { assert(0); } target = LLVMBuildCast(fn->builder, op, src, dtype, target_name); insn->target->priv = target; } static void output_op_fpcast(struct function *fn, struct instruction *insn) { LLVMTypeRef dtype = symbol_type(insn->type); LLVMValueRef src, target; struct symbol *otype = insn->orig_type; char name[64]; assert(is_float_type(insn->type)); pseudo_name(insn->target, name); src = get_operand(fn, otype, insn->src); switch (insn->opcode) { case OP_FCVTF: target = LLVMBuildFPCast(fn->builder, src, dtype, name); break; case OP_SCVTF: target = LLVMBuildSIToFP(fn->builder, src, dtype, name); break; case OP_UCVTF: target = LLVMBuildUIToFP(fn->builder, src, dtype, name); break; default: assert(0); } insn->target->priv = target; } static void output_op_label(struct function *fn, struct instruction *insn) { insn->target->priv = LLVMBlockAddress(fn->fn, insn->bb_true->priv); } static void output_op_setval(struct function *fn, struct instruction *insn) { struct expression *val = insn->val; LLVMValueRef target; switch (val->type) { case EXPR_LABEL: target = LLVMBlockAddress(fn->fn, val->symbol->bb_target->priv); break; default: assert(0); } insn->target->priv = target; } static void output_op_setfval(struct function *fn, struct instruction *insn) { LLVMTypeRef dtype = symbol_type(insn->type); LLVMValueRef target; target = LLVMConstReal(dtype, insn->fvalue); 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_LABEL: output_op_label(fn, insn); break; case OP_SETVAL: output_op_setval(fn, insn); break; case OP_SETFVAL: output_op_setfval(fn, insn); 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_STORE: output_op_store(fn, insn); break; case OP_INLINED_CALL: break; case OP_CALL: output_op_call(fn, insn); break; case OP_ZEXT: output_op_cast(fn, insn, LLVMZExt); break; case OP_SEXT: output_op_cast(fn, insn, LLVMSExt); break; case OP_TRUNC: output_op_cast(fn, insn, LLVMTrunc); break; case OP_FCVTU: output_op_cast(fn, insn, LLVMFPToUI); break; case OP_FCVTS: output_op_cast(fn, insn, LLVMFPToSI); break; case OP_UCVTF: case OP_SCVTF: case OP_FCVTF: output_op_fpcast(fn, insn); break; case OP_UTPTR: case OP_PTRTU: case OP_PTRCAST: output_op_ptrcast(fn, insn); break; case OP_BINARY ... OP_BINARY_END: output_op_binary(fn, insn); break; case OP_FPCMP ... 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->type, insn->src); pseudo_name(insn->target, target_name); target = LLVMBuildNot(fn->builder, src, target_name); insn->target->priv = target; break; } case OP_FNEG: case OP_NEG: { LLVMValueRef src, target; char target_name[64]; src = pseudo_to_value(fn, insn->type, insn->src); pseudo_name(insn->target, target_name); if (insn->opcode == OP_FNEG) target = LLVMBuildFNeg(fn->builder, src, target_name); else target = LLVMBuildNeg(fn->builder, src, target_name); insn->target->priv = target; 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) { struct instruction *insn; 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) { struct symbol *sym = ep->name; struct symbol *base_type = sym->ctype.base_type; struct function function = { .module = module }; struct basic_block *bb; int nr_args = 0; int i; function.fn = get_sym_value(module, sym); LLVMSetFunctionCallConv(function.fn, LLVMCCallConv); LLVMSetLinkage(function.fn, function_linkage(sym)); function.builder = LLVMCreateBuilder(); /* give a name to each argument */ nr_args = symbol_list_size(base_type->arguments); for (i = 0; i < nr_args; i++) { char name[MAX_PSEUDO_NAME]; LLVMValueRef arg; arg = LLVMGetParam(function.fn, i); snprintf(name, sizeof(name), "ARG%d.", i+1); LLVMSetValueName(arg, name); } /* create the BBs */ FOR_EACH_PTR(ep->bbs, bb) { static int nr_bb; LLVMBasicBlockRef bbr; char bbname[32]; sprintf(bbname, "L%d", nr_bb++); bbr = LLVMAppendBasicBlock(function.fn, bbname); bb->priv = bbr; } END_FOR_EACH_PTR(bb); FOR_EACH_PTR(ep->bbs, bb) { LLVMPositionBuilderAtEnd(function.builder, bb->priv); output_bb(&function, bb); } END_FOR_EACH_PTR(bb); FOR_EACH_PTR(ep->bbs, bb) { // complete the OP_PHIs struct instruction *insn; FOR_EACH_PTR(bb->insns, insn) { pseudo_t phi; if (!insn->bb || insn->opcode != OP_PHI) continue; FOR_EACH_PTR(insn->phi_list, phi) { struct instruction *phisrc; LLVMBasicBlockRef bref; LLVMValueRef vref; if (phi == VOID) continue; phisrc = phi->def; bref = phisrc->bb->priv; vref = phisrc->src->priv; LLVMAddIncoming(insn->target->priv, &vref, &bref, 1); } END_FOR_EACH_PTR(phi); } END_FOR_EACH_PTR(insn); } 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(sym), initializer->value, 1); break; case EXPR_FVALUE: initial_value = LLVMConstReal(symbol_type(sym), initializer->fvalue); 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: warning(initializer->pos, "can't initialize type: %s", show_typename(sym)); initial_value = NULL; break; } } else { LLVMTypeRef type = symbol_type(sym); initial_value = LLVMConstNull(type); } if (!initial_value) return NULL; name = sym->ident ? 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)) { // this will do the LLVMAddFunction() we want get_sym_value(module, 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); FOR_EACH_PTR(filelist, file) { symlist = sparse(file); if (die_if_error) return 1; compile(module, symlist); } END_FOR_EACH_PTR(file); LLVMVerifyModule(module, LLVMPrintMessageAction, NULL); LLVMWriteBitcodeToFD(module, STDOUT_FILENO, 0, 0); LLVMDisposeModule(module); report_stats(); return 0; } sparse-0.6.4/sparse.1000066400000000000000000000471321411531012200143720ustar00rootroot00000000000000.\" 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 \-fmax-errors=COUNT Set the maximum number of displayed errors to COUNT, which should be a numerical value or 'unlimited'. The default limit is 100. . .TP .B \-fmax-warnings=COUNT Set the maximum number of displayed warnings to COUNT, which should be a numerical value or 'unlimited'. The default limit is 100. . .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( id ))) on pointers, which designates a pointer target in address space \fIid\fR (an identifier or a constant integer). With \fB\-Waddress\-space\fR, Sparse treats pointers with identical target types but different address spaces as distinct types and will warn accordingly. Sparse will also warn on casts which remove the address space (casts to an integer type or to a plain pointer type). An exception to this is when the destination type is \fBuintptr_t\fR (or \fBunsigned long\fR) since such casts are often used to "get a pointer value representation in an integer type" and such values are independent of the address space. To override these warnings, 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", one that 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. Sparse issues these warnings by default. To turn them off, use \fB\-Wno\-bitwise\fR. . .TP .B \-Wbitwise\-pointer Same as \fB\-Wbitwise\fR but for casts to or from pointers to bitwise types. Sparse does not issue these warnings by default. . .TP .B \-Wcast\-from\-as Warn about casts which remove an address space from a pointer type. This is similar to \fB\-Waddress\-space\fR but will also warn on casts to \fBunsigned long\fR. Sparse does not issues 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. No warning is generated if the original type is \fBuintptr_t\fR (or \fBunsigned long\fR). 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 \-Wconstant\-suffix Warn if an integer constant is larger than the maximum representable value of the type indicated by its type suffix (if any). For example, on a system where ints are 32-bit and longs 64-bit, the constant \fB0x100000000U\fR is larger than can be represented by an \fBunsigned int\fR but fits in an \fBunsigned long\fR. So its type is \fBunsigned long\fR but this is not indicated by its suffix. In this case, the warning could be suppressed by using the suffix \fBUL\fR: \fB0x100000000UL\fR. Sparse does not issue these warnings by default. . .TP .B \-Wconstexpr-not-const Warn if a non-constant expression is encountered when really expecting a constant expression instead. Currently, this warns when initializing an object of static storage duration with an initializer which is not a constant expression. Sparse does not issue these warnings by default. . .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 \-Wexternal\-function\-has\-definition Warn about function definitions that are declared with external linkage. Sparse issues these warnings by default. To turn them off, use \fB\-Wno\-external\-function\-has\-definition\fR. . .TP .B -Wflexible-array-array Warn about arrays of structures containing a flexible array. Sparse issues these warnings by default. To turn them off, use \fB-Wno-flexible-array-array\fR. . .TP .B -Wflexible-array-nested Warn about structures containing a flexible array being contained into another structure, union or array. Sparse does not issue these warnings by default. . .TP .B -Wflexible-array-sizeof Warn about using the sizeof operator on a structure containing a flexible array, possibly recursively. Sparse does not issue these warnings by default. . .TP .B -Wflexible-array-union Enable the warnings regarding flexible arrays and unions. To have any effect, at least one of \fB-Wflexible-array-array\fR, \fB-Wflexible-array-nested\fR or \fB-Wflexible-array-sizeof\fR must also be enabled. Sparse does issue these warnings by default. . .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 are 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 \-Wnewline\-eof Warn if the input file doesn't end with a newline. Sparse issues these warnings by default. To turn them off, use \fB\-Wno\-newline\-eof\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 -Wpast-deep-designator Warn when, in a initializer-list, a initializer with a deep (nested) designator is followed by a non-designated one. Sparse does not issue these warnings by default. . .TP .B \-Wpointer\-arith Warn about anything that depends on the \fBsizeof\fR a void or function type. C99 does not allow the \fBsizeof\fR operator to be applied to function types or to incomplete types such as void. GCC allows \fBsizeof\fR to be applied to these types as an extension and assigns these types a size of \fI1\fR. With \fB\-pointer\-arith\fR, Sparse will warn about pointer arithmetic on void or function pointers, as well as expressions which directly apply the \fBsizeof\fR operator to void or function types. 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 \-Wshift-count-negative Warn if a shift count is negative. Sparse issues these warnings by default. . .TP .B \-Wshift-count-overflow Warn if a shift count is bigger than the operand's width. Sparse issues these warnings by default. . .TP .B \-Wsizeof-bool Warn when checking the sizeof a _Bool. C99 does not specify the size of a _Bool. GCC, by default, uses \fI1\fR. 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 a value of 0. However, this behavior can lead to subtle errors. Sparse does not issue these warnings by default. . .TP .B \-Wuniversal\-initializer Do not suppress warnings caused by using '{\ 0\ }' instead of '{\ }' on aggregate types, ignoring its special status as universal initializer. The concerned warnings are, for example, those triggered by \fB\-Wdesignated\-init\fR or \fB\-Wnon\-pointer\-null\fR. Sparse does not issue these warnings by default, processing '{\ 0\ }' the same as '{\ }'. . .TP .B -Wunion-cast Warn on casts to union types. Sparse does not issues these warnings by default. . .SH MISC OPTIONS .TP .B \-\-arch=\fIARCH\fR Specify the target architecture. For architectures having both a 32-bit and a 64-bit variant (mips, powerpc, riscv and sparc) the architecture name can be suffixed with \fI32\fR or \fI64\fR. The default architecture and size is the one of the machine used to build Sparse. . .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). . .TP .B --os=\fIOS\fR Specify the target Operating System. This only makes a few differences with the predefined types. The accepted values are: linux, unix, freebsd, netbsd, opensd, sunos, darwin and cygwin. The default OS is the one of the machine used to build Sparse if it can be detected, otherwise some generic settings are used. . .SH DEBUG OPTIONS .TP .B \-fmem-report Report some statistics about memory allocation used by the tool. . .SH OTHER OPTIONS .TP .B \-fdiagnostic-prefix[=PREFIX] Prefix all diagnostics by the given PREFIX, followed by ": ". If no one is given "sparse" is used. The default is to not use a prefix at all. . .TP .B \-fmemcpy-max-count=COUNT Set the limit for the warnings given by \fB-Wmemcpy-max-count\fR. A COUNT of 'unlimited' or '0' 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. . .TP .B \-f[no-]unsigned-bitfields, \-f[no-]signed-bitfields Determine the signedness of bitfields declared without an explicit sign ('signed' or 'unsigned'). By default such bitfields are signed, like others plain integers. . .TP .B \-f[no-]unsigned-char, \-f[no-]signed-char Let plain 'char' be unsigned or signed. By default chars are signed. . .SH SEE ALSO .BR cgcc (1) . .SH HOMEPAGE https://sparse.docs.kernel.org . .SH MAILING LIST linux-sparse@vger.kernel.org . .SH CONTRIBUTING AND REPORTING BUGS Submission of patches and reporting of bugs, as well as discussions related to Sparse, should be done via the mailing list (linux-sparse@vger.kernel.org) where the development and maintenance is primarily done. You do not have to be subscribed to the list to send a message there. Bugs can also be reported and tracked via the Linux kernel's bugzilla: http://bugzilla.kernel.org/enter_bug.cgi?component=Sparse&product=Tools . . .SH DOCUMENTATION More documentation about Sparse can be found at https://sparse.docs.kernel.org . .SH AUTHORS Sparse was started by Linus Torvalds. The complete list of contributors can be find at https://www.openhub.net/p/sparse/contributors . Luc Van Oostenryck is Sparse's current maintainer. sparse-0.6.4/sparse.c000066400000000000000000000200601411531012200144430ustar00rootroot00000000000000/* * 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->bb) continue; 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_SEXT; 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 void check_memset(struct instruction *insn) { check_byte_count(insn, ptr_list_nth(insn->arguments, 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_SEXT: case OP_ZEXT: case OP_TRUNC: 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) { bb->context = -1; 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); } /* list_compound_symbol - symbol info for arrays, structures, unions */ static void list_compound_symbol(struct symbol *sym) { struct symbol *base; /* Only show symbols that have a positive size */ if (sym->bit_size <= 0) return; if (!sym->ctype.base_type) return; /* Don't show unnamed types */ if (!sym->ident) return; if (sym->type == SYM_NODE) base = sym->ctype.base_type; else base = sym; switch (base->type) { case SYM_STRUCT: case SYM_UNION: case SYM_ARRAY: break; default: return; } info(sym->pos, "%s: compound size %u, alignment %lu", show_typename(sym), bits_to_bytes(sym->bit_size), sym->ctype.alignment); } 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 && ep->entry) { if (dbg_entry) show_entry(ep); check_context(ep); } if (dbg_compound) list_compound_symbol(sym); } 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; // by default ignore -o do_output = 0; // Expand, linearize and show it. check_symbols(sparse_initialize(argc, argv, &filelist)); FOR_EACH_PTR(filelist, file) { check_symbols(sparse(file)); } END_FOR_EACH_PTR(file); report_stats(); return 0; } sparse-0.6.4/sparsec000077500000000000000000000016761411531012200144040ustar00rootroot00000000000000#!/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 TMPFILE=`mktemp -t tmp.XXXXXX` LLC=`"${LLVM_CONFIG:-llvm-config}" --bindir`/llc LLC_ARCH_OPTS= case "$(uname -s)" in *CYGWIN*) # cygwin uses the sjlj (setjmp-longjmp) exception model LLC_ARCH_OPTS="-exception-model=sjlj" LLC_ARCH_OPTS="$LLC_ARCH_OPTS -mtriple=$(llvm-config --host-target)" ;; *) ;; esac $DIRNAME/sparse-llvm $SPARSEOPTS | $LLC ${LLC_ARCH_OPTS} | as -o $TMPFILE if [ $NEED_LINK -eq 1 ]; then if [ -z $OUTFILE ]; then OUTFILE=a.out fi gcc $TMPFILE -o $OUTFILE rm -f $TMPFILE else if [ -z $OUTFILE ]; then echo "`basename $0`: no output file" exit 1 fi mv $TMPFILE $OUTFILE fi sparse-0.6.4/sparsei000077500000000000000000000006241411531012200144020ustar00rootroot00000000000000#!/bin/sh set +e SPARSEOPTS= JIT_OPT= DIRNAME=`dirname $0` LLI=`"${LLVM_CONFIG:-llvm-config}" --bindir`/lli if [ $# -eq 0 ]; then echo "`basename $0`: no input files" exit 1 fi while [ $# -gt 0 ]; do case $1 in --jit) JIT_OPT= ;; --no-jit) JIT_OPT="-force-interpreter" ;; *) SPARSEOPTS="$SPARSEOPTS $1 " ;; esac shift done $DIRNAME/sparse-llvm ${SPARSEOPTS} | $LLI ${JIT_OPT} sparse-0.6.4/ssa.c000066400000000000000000000211341411531012200137370ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // SSA conversion // Copyright (C) 2005 Luc Van Oostenryck // #include #include "ssa.h" #include "lib.h" #include "dominate.h" #include "flowgraph.h" #include "linearize.h" #include "simplify.h" #include "flow.h" // Is it possible and desirable for this to be promoted to a pseudo? static inline bool is_promotable(struct symbol *type) { struct symbol *member; int bf_seen; int nbr; 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; case SYM_STRUCT: // we allow a single scalar field // but a run of bitfields count for 1 // (and packed bifields are excluded). if (type->packed) return 0; nbr = 0; bf_seen = 0; FOR_EACH_PTR(type->symbol_list, member) { if (is_bitfield_type(member)) { if (bf_seen) continue; bf_seen = 1; } else { bf_seen = 0; } if (!is_scalar_type(member)) return 0; if (nbr++) return 0; } END_FOR_EACH_PTR(member); if (bf_seen && (type->bit_size > long_ctype.bit_size)) return 0; return 1; case SYM_UNION: // FIXME: should be like struct but has problem // when used with/for type cohercion // -----> OK if only same sized integral types FOR_EACH_PTR(type->symbol_list, member) { if (member->bit_size != type->bit_size) return 0; if (!is_integral_type(member)) return 0; } END_FOR_EACH_PTR(member); 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 void kill_store(struct instruction *insn) { remove_use(&insn->src); remove_use(&insn->target); insn->bb = NULL; } static bool same_memop(struct instruction *a, struct instruction *b) { if (a->size != b->size || a->offset != b->offset) return false; if (is_integral_type(a->type) && is_float_type(b->type)) return false; if (is_float_type(a->type) && is_integral_type(b->type)) return false; return true; } static void rewrite_local_var(struct basic_block *bb, pseudo_t addr, int nbr_stores, int nbr_uses) { struct instruction *store = NULL; struct instruction *insn; bool remove = false; if (!bb) return; FOR_EACH_PTR(bb->insns, insn) { if (!insn->bb || insn->src != addr) continue; switch (insn->opcode) { case OP_LOAD: if (!store) replace_with_pseudo(insn, undef_pseudo()); else if (same_memop(store, insn)) replace_with_pseudo(insn, store->target); else remove = false; break; case OP_STORE: store = insn; remove = true; break; } } END_FOR_EACH_PTR(insn); if (remove) kill_store(store); } // we would like to know: // is there one or more stores? // are all loads & stores local/done in a single block? static void ssa_convert_one_var(struct entrypoint *ep, struct symbol *var) { unsigned long generation = ++bb_generation; struct basic_block_list *alpha = NULL; struct basic_block_list *idf = NULL; struct basic_block *samebb = NULL; struct basic_block *bb; struct pseudo_user *pu; unsigned long mod = var->ctype.modifiers; bool local = true; int nbr_stores = 0; int nbr_uses = 0; pseudo_t addr; /* Never used as a symbol? */ addr = var->pseudo; if (!addr) return; /* We don't do coverage analysis of volatiles.. */ if (mod & MOD_VOLATILE) return; /* ..and symbols with external visibility need more care */ mod &= (MOD_NONLOCAL | MOD_STATIC | MOD_ADDRESSABLE); if (mod) goto external_visibility; if (!is_promotable(var)) return; // 1) insert in the worklist all BBs that may modify var FOR_EACH_PTR(addr->users, pu) { struct instruction *insn = pu->insn; struct basic_block *bb = insn->bb; switch (insn->opcode) { case OP_STORE: nbr_stores++; if (bb->generation != generation) { bb->generation = generation; add_bb(&alpha, bb); } /* fall through */ case OP_LOAD: if (local) { if (!samebb) samebb = bb; else if (samebb != bb) local = false; } nbr_uses++; break; case OP_SYMADDR: mod |= MOD_ADDRESSABLE; goto external_visibility; default: warning(var->pos, "symbol '%s' pseudo used in unexpected way", show_ident(var->ident)); } } END_FOR_EACH_PTR(pu); // if all uses are local to a single block // they can easily be rewritten and doesn't need phi-nodes // FIXME: could be done for extended BB too if (local) { rewrite_local_var(samebb, addr, nbr_stores, nbr_uses); return; } idf_compute(ep, &idf, alpha); FOR_EACH_PTR(idf, bb) { struct instruction *node = insert_phi_node(bb, var); node->phi_var = var->pseudo; } END_FOR_EACH_PTR(bb); var->torename = 1; external_visibility: if (mod & (MOD_NONLOCAL | MOD_STATIC)) return; kill_dead_stores(ep, addr, !mod); } static struct instruction *lookup_var(struct basic_block *bb, struct symbol *var) { do { struct instruction *insn = phi_map_lookup(bb->phi_map, var); if (insn) return insn; } while ((bb = bb->idom)); return NULL; } static struct instruction_list *phis_all; static struct instruction_list *phis_used; static struct instruction_list *stores; static bool matching_load(struct instruction *def, struct instruction *insn) { if (insn->size != def->size) return false; switch (def->opcode) { case OP_STORE: case OP_LOAD: if (insn->offset != def->offset) return false; case OP_PHI: break; default: return false; } return true; } static void ssa_rename_insn(struct basic_block *bb, struct instruction *insn) { struct instruction *def; struct symbol *var; pseudo_t addr; pseudo_t val; switch (insn->opcode) { case OP_STORE: addr = insn->src; if (addr->type != PSEUDO_SYM) break; var = addr->sym; if (!var || !var->torename) break; phi_map_update(&bb->phi_map, var, insn); add_instruction(&stores, insn); break; case OP_LOAD: addr = insn->src; if (addr->type != PSEUDO_SYM) break; var = addr->sym; if (!var || !var->torename) break; def = lookup_var(bb, var); if (!def) { val = undef_pseudo(); } else if (!matching_load(def, insn)) { var->torename = false; break; } else { val = def->target; } replace_with_pseudo(insn, val); break; case OP_PHI: var = insn->type; if (!var || !var->torename) break; phi_map_update(&bb->phi_map, var, insn); add_instruction(&phis_all, insn); break; } } static void ssa_rename_insns(struct entrypoint *ep) { struct basic_block *bb; FOR_EACH_PTR(ep->bbs, bb) { struct instruction *insn; FOR_EACH_PTR(bb->insns, insn) { if (!insn->bb) continue; ssa_rename_insn(bb, insn); } END_FOR_EACH_PTR(insn); } END_FOR_EACH_PTR(bb); } static void mark_phi_used(pseudo_t val) { struct instruction *node; if (val->type != PSEUDO_REG) return; node = val->def; if (node->opcode != OP_PHI) return; if (node->used) return; node->used = 1; add_instruction(&phis_used, node); } static void ssa_rename_phi(struct instruction *insn) { struct basic_block *par; struct symbol *var; if (!insn->phi_var) return; var = insn->phi_var->sym; if (!var->torename) return; FOR_EACH_PTR(insn->bb->parents, par) { struct instruction *def = lookup_var(par, var); pseudo_t val = def ? def->target : undef_pseudo(); struct instruction *phisrc = alloc_phisrc(val, var); pseudo_t phi = phisrc->target; phi->ident = var->ident; insert_last_instruction(par, phisrc); link_phi(insn, phi); mark_phi_used(val); } END_FOR_EACH_PTR(par); } static void ssa_rename_phis(struct entrypoint *ep) { struct instruction *phi; phis_used = NULL; FOR_EACH_PTR(phis_all, phi) { if (has_users(phi->target)) { phi->used = 1; add_instruction(&phis_used, phi); } } END_FOR_EACH_PTR(phi); FOR_EACH_PTR(phis_used, phi) { if (!phi->bb) continue; ssa_rename_phi(phi); } END_FOR_EACH_PTR(phi); } static void remove_dead_stores(struct instruction_list *stores) { struct instruction *store; FOR_EACH_PTR(stores, store) { struct symbol *var = store->addr->sym; if (var->torename) kill_store(store); } END_FOR_EACH_PTR(store); } void ssa_convert(struct entrypoint *ep) { struct basic_block *bb; pseudo_t pseudo; int first, last; // calculate the number of BBs first = ep->entry->bb->nr; last = first; FOR_EACH_PTR(ep->bbs, bb) { int nr = bb->nr; if (nr > last) last = nr; bb->phi_map = NULL; } END_FOR_EACH_PTR(bb); // try to promote memory accesses to pseudos stores = NULL; FOR_EACH_PTR(ep->accesses, pseudo) { ssa_convert_one_var(ep, pseudo->sym); } END_FOR_EACH_PTR(pseudo); // rename the converted accesses phis_all = phis_used = NULL; ssa_rename_insns(ep); ssa_rename_phis(ep); // remove now dead stores remove_dead_stores(stores); } sparse-0.6.4/ssa.h000066400000000000000000000001421411531012200137400ustar00rootroot00000000000000#ifndef SSA_H #define SSA_H struct entrypoint; void ssa_convert(struct entrypoint *ep); #endif sparse-0.6.4/stats.c000066400000000000000000000032621411531012200143110ustar00rootroot00000000000000#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.6.4/storage.c000066400000000000000000000152221411531012200146160ustar00rootroot00000000000000/* * 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); } 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); } END_FOR_EACH_PTR(bb); name_storage(); } sparse-0.6.4/storage.h000066400000000000000000000033251411531012200146240ustar00rootroot00000000000000#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.6.4/symbol.c000066400000000000000000000672461411531012200144740ustar00rootroot00000000000000/* * 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 #include "lib.h" #include "allocate.h" #include "token.h" #include "parse.h" #include "symbol.h" #include "scope.h" #include "expression.h" #include "evaluate.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->accessed) { add_symbol(&translation_unit_used_list, sym); sym->accessed = 1; } } } 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; char has_flex_array; bool packed; struct symbol *flex_array; }; /* * Unions are fairly easy to lay out ;) */ static void lay_out_union(struct symbol *sym, struct struct_union_info *info) { if (sym->bit_size < 0 && is_array_type(sym)) sparse_error(sym->pos, "flexible array member '%s' in a union", show_ident(sym->ident)); 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; unsigned long alignment; int base_size; bit_size = info->bit_size; base_size = sym->bit_size; /* * If the member is unsized, either it's a flexible array or * it's invalid and a warning has already been issued. */ if (base_size < 0) { if (!is_array_type(sym)) return; base_size = 0; info->flex_array = sym; } alignment = info->packed ? 1 : sym->ctype.alignment; align_bit_mask = bytes_to_bits(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 && !info->packed) { 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); if (info->packed && sym->type == SYM_NODE) sym->packed = 1; 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); } /// // propagate properties of anonymous structs or unions into their members. // // :note: GCC seems to only propagate the qualifiers. // :note: clang doesn't propagate anything at all. static void examine_anonymous_member(struct symbol *sym) { unsigned long mod = sym->ctype.modifiers & MOD_QUALIFIER; struct symbol *sub; if (sym->type == SYM_NODE) sym = sym->ctype.base_type; if (sym->type != SYM_STRUCT && sym->type != SYM_UNION) return; FOR_EACH_PTR(sym->symbol_list, sub) { assert(sub->type == SYM_NODE); sub->ctype.modifiers |= mod; // if nested, propagate all the way down if (!sub->ident) examine_anonymous_member(sub); } END_FOR_EACH_PTR(sub); } static struct symbol * examine_struct_union_type(struct symbol *sym, int advance) { struct struct_union_info info = { .packed = sym->packed, .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) { if (member->ctype.base_type == &autotype_ctype) { sparse_error(member->pos, "member '%s' has __auto_type", show_ident(member->ident)); member->ctype.base_type = &incomplete_ctype; } if (info.flex_array) sparse_error(info.flex_array->pos, "flexible array member '%s' is not last", show_ident(info.flex_array->ident)); examine_symbol_type(member); if (!member->ident) examine_anonymous_member(member); if (member->ctype.alignment > info.max_align && !sym->packed) { // Unnamed bitfields do not affect alignment. if (member->ident || !is_bitfield_type(member)) info.max_align = member->ctype.alignment; } if (has_flexible_array(member)) info.has_flex_array = 1; if (has_flexible_array(member) && Wflexible_array_nested) warning(member->pos, "nested flexible array"); 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; } if (info.flex_array) { info.has_flex_array = 1; } if (info.has_flex_array && (!is_union_type(sym) || Wflexible_array_union)) sym->has_flex_array = 1; sym->bit_size = bit_size; return sym; } static struct symbol *examine_base_type(struct symbol *sym) { struct symbol *base_type; if (sym->ctype.base_type == &autotype_ctype) { struct symbol *type = evaluate_expression(sym->initializer); if (!type) type = &bad_ctype; if (is_bitfield_type(type)) { warning(sym->pos, "__auto_type on bitfield"); if (type->type == SYM_NODE) type = type->ctype.base_type; type = type->ctype.base_type; } sym->ctype.base_type = 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; combine_address_space(sym->pos, &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; sym->rank = base_type->rank; } 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; } } if (has_flexible_array(base_type) && Wflexible_array_array) warning(sym->pos, "array of flexible structures"); 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 alignment, modifiers; if (!base_type) return sym; if (sym->bit_size > base_type->bit_size) { sparse_error(sym->pos, "bitfield '%s' is wider (%d) than its type (%s)", show_ident(sym->ident), sym->bit_size, show_typename(base_type)); sym->bit_size = -1; } alignment = base_type->ctype.alignment; if (!sym->ctype.alignment) sym->ctype.alignment = alignment; modifiers = base_type->ctype.modifiers; /* use -funsigned-bitfields to determine the sign if not explicit */ if (!(modifiers & MOD_EXPLICITLY_SIGNED) && funsigned_bitfields) 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) { combine_address_space(sym->pos, &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 bool is_wstring_expr(struct expression *expr) { while (expr) { switch (expr->type) { case EXPR_STRING: return 1; case EXPR_INITIALIZER: if (expression_list_size(expr->expr_list) != 1) return 0; expr = first_expression(expr->expr_list); break; case EXPR_PREOP: if (expr->op == '(') { expr = expr->unop; break; } default: return 0; } } return 0; } 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->rank == -2) is_char = 1; else if (t == wchar_ctype && is_wstring_expr(expr)) 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 unsigned int implicit_array_size(struct symbol *node, unsigned int count) { struct symbol *arr_ori = node->ctype.base_type; struct symbol *arr_new = alloc_symbol(node->pos, SYM_ARRAY); struct symbol *elem_type = arr_ori->ctype.base_type; struct expression *size = alloc_const_expression(node->pos, count); unsigned int bit_size = array_element_offset(elem_type->bit_size, count); *arr_new = *arr_ori; arr_new->bit_size = bit_size; arr_new->array_size = size; node->array_size = size; node->ctype.base_type = arr_new; return bit_size; } 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 = implicit_array_size(sym, count); } } sym->bit_size = bit_size; sym->rank = base_type->rank; 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) { /* * Since pointers to incomplete types can be used, * for example in a struct-declaration-list, * the base type must *not* be examined here. * It thus means that it needs to be done later, * when the base type of the pointer is looked at. */ if (!sym->bit_size) sym->bit_size = bits_in_pointer; if (!sym->ctype.alignment) sym->ctype.alignment = pointer_alignment; return sym; } static struct symbol *examine_typeof(struct symbol *sym) { struct symbol *base = evaluate_expression(sym->initializer); unsigned long mod = 0; if (!base) base = &bad_ctype; if (base->type == SYM_NODE) { mod |= base->ctype.modifiers & MOD_TYPEOF; base = base->ctype.base_type; } if (base->type == SYM_BITFIELD) warning(base->pos, "typeof applied to bitfield type"); sym->type = SYM_NODE; sym->ctype.modifiers = mod; sym->ctype.base_type = base; return examine_node_type(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: return examine_typeof(sym); 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 '%s'", show_typename(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_TYPEOF] = "typeof", [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->rank = 0; 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; } static void inherit_declaration(struct symbol *sym, struct symbol *prev) { unsigned long mods = prev->ctype.modifiers; // inherit function attributes sym->ctype.modifiers |= mods & MOD_FUN_ATTR; } 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; inherit_declaration(sym, 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"); } } static void inherit_static(struct symbol *sym) { struct symbol *prev; // only 'plain' symbols are concerned if (sym->ctype.modifiers & (MOD_STATIC|MOD_EXTERN)) return; for (prev = sym->next_id; prev; prev = prev->next_id) { if (prev->namespace != NS_SYMBOL) continue; if (prev->scope != file_scope) continue; sym->ctype.modifiers |= prev->ctype.modifiers & MOD_STATIC; // previous declarations are already converted return; } } void bind_symbol_with_scope(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; if (ns == NS_SYMBOL && toplevel(scope)) { unsigned mod = MOD_ADDRESSABLE | MOD_TOPLEVEL; inherit_static(sym); scope = global_scope; if (sym->ctype.modifiers & MOD_STATIC || is_extern_inline(sym)) { scope = file_scope; mod = MOD_TOPLEVEL; } sym->ctype.modifiers |= mod; } bind_scope(sym, scope); } void bind_symbol(struct symbol *sym, struct ident *ident, enum namespace ns) { struct scope *scope = block_scope;; if (ns == NS_MACRO) scope = file_scope; if (ns == NS_LABEL) scope = function_scope; bind_symbol_with_scope(sym, ident, ns, 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, int128_ctype, sint128_ctype, uint128_ctype, float_ctype, double_ctype, ldouble_ctype, string_ctype, ptr_ctype, lazy_ptr_ctype, incomplete_ctype, label_ctype, bad_ctype, null_ctype; struct symbol autotype_ctype; struct symbol schar_ptr_ctype, short_ptr_ctype; struct symbol int_ptr_ctype, uint_ptr_ctype; struct symbol long_ptr_ctype, ulong_ptr_ctype; struct symbol llong_ptr_ctype, ullong_ptr_ctype; struct symbol size_t_ptr_ctype, intmax_ptr_ctype, ptrdiff_ptr_ctype; struct symbol float32_ctype, float32x_ctype; struct symbol float64_ctype, float64x_ctype; struct symbol float128_ctype; struct symbol const_void_ctype, const_char_ctype; struct symbol const_ptr_ctype, const_string_ctype; struct symbol const_wchar_ctype, const_wstring_ctype; struct symbol volatile_void_ctype, volatile_ptr_ctype; struct symbol volatile_bool_ctype, volatile_bool_ptr_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(NULL, "builtin", -1, includepath); #define __IDENT(n,str,res) \ hash_ident(&n) #include "ident-list.h" init_parser(stream); } // For fix-sized types static int bits_in_type32 = 32; static int bits_in_type64 = 64; static int bits_in_type128 = 128; #define T_BASETYPE SYM_BASETYPE, 0, 0, NULL, NULL, NULL #define T_INT(R, S, M) SYM_BASETYPE, M, R, &bits_in_##S, &max_int_alignment, &int_type #define T__INT(R, S) T_INT(R, S, MOD_SIGNED) #define T_SINT(R, S) T_INT(R, S, MOD_ESIGNED) #define T_UINT(R,S) T_INT(R, S, MOD_UNSIGNED) #define T_FLOAT_(R,S,A) SYM_BASETYPE, 0, R, &bits_in_##S, A, &fp_type #define T_FLOAT(R, S) T_FLOAT_(R, S, &max_fp_alignment) #define T_PTR(B) SYM_PTR, 0, 0, &bits_in_pointer, &pointer_alignment, B #define T_NODE(M,B,S,A) SYM_NODE, M, 0, S, A, B #define T_CONST(B,S,A) T_NODE(MOD_CONST, B, S, A) static const struct ctype_declare { struct symbol *ptr; enum type type; unsigned long modifiers; int rank; int *bit_size; int *maxalign; struct symbol *base_type; } ctype_declaration[] = { { &bool_ctype, T_INT(-3, bool, MOD_UNSIGNED) }, { &void_ctype, T_BASETYPE }, { &type_ctype, T_BASETYPE }, { &incomplete_ctype, T_BASETYPE }, { &autotype_ctype, T_BASETYPE }, { &bad_ctype, T_BASETYPE }, { &char_ctype, T__INT(-2, char) }, { &schar_ctype, T_SINT(-2, char) }, { &uchar_ctype, T_UINT(-2, char) }, { &short_ctype, T__INT(-1, short) }, { &sshort_ctype, T_SINT(-1, short) }, { &ushort_ctype, T_UINT(-1, short) }, { &int_ctype, T__INT( 0, int) }, { &sint_ctype, T_SINT( 0, int) }, { &uint_ctype, T_UINT( 0, int) }, { &long_ctype, T__INT( 1, long) }, { &slong_ctype, T_SINT( 1, long) }, { &ulong_ctype, T_UINT( 1, long) }, { &llong_ctype, T__INT( 2, longlong) }, { &sllong_ctype, T_SINT( 2, longlong) }, { &ullong_ctype, T_UINT( 2, longlong) }, { &int128_ctype, T__INT( 3, type128) }, { &sint128_ctype, T_SINT( 3, type128) }, { &uint128_ctype, T_UINT( 3, type128) }, { &float_ctype, T_FLOAT(-1, float) }, { &double_ctype, T_FLOAT( 0, double) }, { &ldouble_ctype, T_FLOAT( 1, longdouble) }, { &float32_ctype, T_FLOAT(-1, type32) }, { &float32x_ctype, T_FLOAT(-1, double) }, { &float64_ctype, T_FLOAT( 0, type64) }, { &float64x_ctype, T_FLOAT( 1, longdouble) }, { &float128_ctype, T_FLOAT_(2, type128, &max_alignment) }, { &string_ctype, T_PTR(&char_ctype) }, { &ptr_ctype, T_PTR(&void_ctype) }, { &null_ctype, T_PTR(&void_ctype) }, { &label_ctype, T_PTR(&void_ctype) }, { &lazy_ptr_ctype, T_PTR(&void_ctype) }, { &schar_ptr_ctype, T_PTR(&schar_ctype) }, { &short_ptr_ctype, T_PTR(&short_ctype) }, { &int_ptr_ctype, T_PTR(&int_ctype) }, { &uint_ptr_ctype, T_PTR(&uint_ctype) }, { &long_ptr_ctype, T_PTR(&long_ctype) }, { &ulong_ptr_ctype, T_PTR(&ulong_ctype) }, { &llong_ptr_ctype, T_PTR(&llong_ctype) }, { &ullong_ptr_ctype, T_PTR(&ullong_ctype) }, { &size_t_ptr_ctype, T_PTR(&void_ctype) }, // will be adjusted { &intmax_ptr_ctype, T_PTR(&void_ctype) }, // will be adjusted { &ptrdiff_ptr_ctype, T_PTR(&void_ctype) }, // will be adjusted { &const_ptr_ctype, T_PTR(&const_void_ctype) }, { &const_string_ctype, T_PTR(&const_char_ctype) }, { &const_wstring_ctype,T_PTR(&const_wchar_ctype) }, { &const_void_ctype, T_CONST(&void_ctype, NULL, NULL) }, { &const_char_ctype, T_CONST(&char_ctype, &bits_in_char, &max_int_alignment)}, { &const_wchar_ctype, T_CONST(&int_ctype, NULL, NULL) }, { &volatile_void_ctype,T_NODE(MOD_VOLATILE, &void_ctype, NULL, NULL) }, { &volatile_ptr_ctype, T_PTR(&volatile_void_ctype) }, { &volatile_bool_ctype,T_NODE(MOD_VOLATILE, &bool_ctype, NULL, NULL) }, { &volatile_bool_ptr_ctype, T_PTR(&volatile_bool_ctype) }, { NULL, } }; 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->rank = ctype->rank; sym->bit_size = bit_size; sym->ctype.alignment = alignment; sym->ctype.base_type = ctype->base_type; sym->ctype.modifiers = ctype->modifiers; if (sym->type == SYM_NODE) { struct symbol *base = sym->ctype.base_type; sym->rank = base->rank; if (!ctype->bit_size) sym->bit_size = base->bit_size; if (!ctype->maxalign) sym->ctype.alignment = base->ctype.alignment; } } // and now some adjustments if (funsigned_char) { char_ctype.ctype.modifiers |= MOD_UNSIGNED; char_ctype.ctype.modifiers &= ~MOD_SIGNED; } if (!ptrdiff_ctype) ptrdiff_ctype = ssize_t_ctype; if (!intptr_ctype) intptr_ctype = ssize_t_ctype; if (!uintptr_ctype) uintptr_ctype = size_t_ctype; size_t_ptr_ctype.ctype.base_type = size_t_ctype; intmax_ptr_ctype.ctype.base_type = intmax_ctype; ptrdiff_ptr_ctype.ctype.base_type = ptrdiff_ctype; const_wchar_ctype.ctype.base_type = wchar_ctype; const_wchar_ctype.rank = wchar_ctype->rank; const_wchar_ctype.ctype.alignment = wchar_ctype->ctype.alignment; const_wchar_ctype.bit_size = wchar_ctype->bit_size; } sparse-0.6.4/symbol.h000066400000000000000000000406071411531012200144710ustar00rootroot00000000000000#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_TYPEOF, 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_BUILTIN = 1 << 4, KW_ASM = 1 << 5, KW_MODE = 1 << 6, KW_STATIC = 1 << 7, // KW UNUSED = 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 { struct symbol *base_type; unsigned long modifiers; unsigned long alignment; struct context_list *contexts; struct ident *as; }; struct decl_state { struct ctype ctype; struct ident **ident; struct symbol_op *mode; unsigned long f_modifiers; // function attributes unsigned long storage_class; unsigned char prefer_abstract; unsigned char autotype; unsigned char forced; unsigned char packed; }; struct pseudo; struct entrypoint; struct arg; struct symbol_op { enum keyword type; int (*evaluate)(struct expression *); int (*expand)(struct expression *, int); int (*args)(struct expression *); struct pseudo *(*linearize)(struct entrypoint *, struct expression *); /* keywords */ struct token *(*declarator)(struct token *token, struct symbol *, 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 *); void (*asm_modifier)(struct token *token, unsigned long *mods, unsigned long mod); 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; void (*expand_simple)(struct token *); bool (*expand)(struct token *, struct arg *args); }; struct /* NS_PREPROCESSOR */ { int (*handler)(struct stream *, struct token **, struct token *); int normal; }; struct /* NS_LABEL */ { struct scope *label_scope; struct position label_pos; unsigned long label_modifiers; }; struct /* NS_SYMBOL */ { unsigned long offset; int bit_size; unsigned int bit_offset:8, bogus_linear:1, variadic:1, initialized:1, examined:1, expanding:1, evaluated:1, has_flex_array:1, string:1, designated_init:1, forced_arg:1, accessed:1, builtin:1, torename:1, packed:1, transparent_union:1; int rank:3; // arithmetic's rank 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; struct symbol *definition; }; }; union /* backend */ { struct basic_block *bb_target; /* label */ void *aux; /* Auxiliary info, e.g. backend information */ struct { char kind; /* used by ctags & dissect */ unsigned char visited:1; unsigned char inspected:1; }; }; pseudo_t pseudo; }; /* Modifiers */ #define MOD_AUTO 0x00000001 #define MOD_REGISTER 0x00000002 #define MOD_STATIC 0x00000004 #define MOD_EXTERN 0x00000008 #define MOD_TOPLEVEL 0x00000010 // scoping.. #define MOD_TLS 0x00000020 #define MOD_ASM_GOTO MOD_TLS // never used together #define MOD_INLINE 0x00000040 #define MOD_ASSIGNED 0x00000080 #define MOD_ADDRESSABLE 0x00000100 #define MOD_CONST 0x00000200 #define MOD_VOLATILE 0x00000400 #define MOD_RESTRICT 0x00000800 #define MOD_ATOMIC 0x00001000 #define MOD_SIGNED 0x00002000 #define MOD_UNSIGNED 0x00004000 #define MOD_EXPLICITLY_SIGNED 0x00008000 #define MOD_GNU_INLINE 0x00010000 #define MOD_USERTYPE 0x00020000 // MOD UNUSED 0x00040000 // MOD UNUSED 0x00080000 // MOD UNUSED 0x00100000 // MOD UNUSED 0x00200000 #define MOD_UNUSED 0x00400000 #define MOD_SAFE 0x00800000 // non-null/non-trapping pointer #define MOD_PURE 0x01000000 #define MOD_BITWISE 0x02000000 #define MOD_NOCAST 0x04000000 #define MOD_NODEREF 0x08000000 #define MOD_NORETURN 0x10000000 #define MOD_EXT_VISIBLE 0x20000000 #define MOD_ACCESS (MOD_ASSIGNED | MOD_ADDRESSABLE) #define MOD_NONLOCAL (MOD_EXTERN | MOD_TOPLEVEL) #define MOD_STORAGE (MOD_AUTO | MOD_REGISTER | MOD_STATIC | MOD_EXTERN | MOD_INLINE | MOD_TOPLEVEL) #define MOD_ESIGNED (MOD_SIGNED | MOD_EXPLICITLY_SIGNED) #define MOD_SIGNEDNESS (MOD_SIGNED | MOD_UNSIGNED | MOD_EXPLICITLY_SIGNED) #define MOD_SPECIFIER MOD_SIGNEDNESS #define MOD_IGNORE (MOD_STORAGE | MOD_ACCESS | MOD_USERTYPE | MOD_EXPLICITLY_SIGNED | MOD_EXT_VISIBLE | MOD_UNUSED | MOD_GNU_INLINE) #define MOD_QUALIFIER (MOD_CONST | MOD_VOLATILE | MOD_RESTRICT) #define MOD_PTRINHERIT (MOD_QUALIFIER | MOD_ATOMIC | MOD_NODEREF | MOD_NORETURN | MOD_NOCAST) /* modifiers preserved by typeof() operator */ #define MOD_TYPEOF (MOD_QUALIFIER | MOD_ATOMIC | MOD_NOCAST | MOD_SPECIFIER) /* modifiers for function attributes */ #define MOD_FUN_ATTR (MOD_PURE|MOD_NORETURN) /* like cvr-qualifiers but 'reversed' (OK: source <= target) */ #define MOD_REV_QUAL (MOD_PURE|MOD_NORETURN) /* do not warn when these are duplicated */ #define MOD_DUP_OK (MOD_UNUSED|MOD_GNU_INLINE) /* must be part of the declared symbol, not its type */ #define MOD_DECLARE (MOD_STORAGE|MOD_INLINE|MOD_TLS|MOD_GNU_INLINE|MOD_UNUSED|MOD_PURE|MOD_NORETURN|MOD_EXT_VISIBLE) /* 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, int128_ctype, sint128_ctype, uint128_ctype, float_ctype, double_ctype, ldouble_ctype, string_ctype, ptr_ctype, lazy_ptr_ctype, incomplete_ctype, label_ctype, bad_ctype, null_ctype; extern struct symbol autotype_ctype; extern struct symbol schar_ptr_ctype, short_ptr_ctype; extern struct symbol int_ptr_ctype, uint_ptr_ctype; extern struct symbol long_ptr_ctype, ulong_ptr_ctype; extern struct symbol llong_ptr_ctype, ullong_ptr_ctype; extern struct symbol size_t_ptr_ctype, intmax_ptr_ctype, ptrdiff_ptr_ctype; extern struct symbol float32_ctype, float32x_ctype; extern struct symbol float64_ctype, float64x_ctype; extern struct symbol float128_ctype; extern struct symbol const_void_ctype, const_char_ctype; extern struct symbol const_ptr_ctype, const_string_ctype; extern struct symbol const_wchar_ctype, const_wstring_ctype; extern struct symbol volatile_void_ctype, volatile_ptr_ctype; extern struct symbol volatile_bool_ctype, volatile_bool_ptr_ctype; /* Special internal symbols */ extern struct symbol zero_int; #define __IDENT(n,str,res) \ extern struct ident n #include "ident-list.h" 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_linearized_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_name(unsigned long mod); 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 *); extern void add_symbol(struct symbol_list **, struct symbol *); extern void bind_symbol(struct symbol *, struct ident *, enum namespace); extern void bind_symbol_with_scope(struct symbol *, struct ident *, enum namespace, struct scope *); extern struct symbol *examine_symbol_type(struct symbol *); extern struct symbol *examine_pointer_target(struct symbol *); extern const char *show_as(struct ident *as); extern const char *show_typename(struct symbol *sym); extern const char *builtin_typename(struct symbol *sym); extern const char *builtin_type_suffix(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); extern void check_duplicates(struct symbol *sym); static inline int valid_type(const struct symbol *ctype) { return ctype && ctype != &bad_ctype; } static inline struct symbol *get_base_type(const struct symbol *sym) { return examine_symbol_type(sym->ctype.base_type); } /// // test if type is an integer type // // @return: ``1`` for plain integer type, enums & bitfields // but ``0`` for bitwise types! 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_signed_type(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 inline int is_type_type(struct symbol *type) { return type == &type_ctype; } 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_struct_type(struct symbol *type) { if (type->type == SYM_NODE) type = type->ctype.base_type; return type->type == SYM_STRUCT; } static inline int is_union_type(struct symbol *type) { if (type->type == SYM_NODE) type = type->ctype.base_type; return type->type == SYM_UNION; } 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_wchar_type(struct symbol *type) { if (type->type == SYM_NODE) type = type->ctype.base_type; return type == wchar_ctype; } 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 case SYM_FOULED: // idem return 1; default: break; } if (type->ctype.base_type == &int_type) return 1; if (type->ctype.base_type == &fp_type) return 1; return 0; } /// return true for integer & pointer types static inline bool is_integral_type(struct symbol *type) { if (type->type == SYM_NODE) type = type->ctype.base_type; switch (type->type) { case SYM_ENUM: case SYM_PTR: case SYM_RESTRICT: // OK, always integer types case SYM_FOULED: // idem return 1; default: break; } if (type->ctype.base_type == &int_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 has_flexible_array(struct symbol *type) { if (type->type == SYM_NODE) type = type->ctype.base_type; return type->has_flex_array; } 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 long long extend_value(long long val, struct symbol *ctype) { int is_signed = !(ctype->ctype.modifiers & MOD_UNSIGNED); unsigned size = ctype->bit_size; return bits_extend(val, size, is_signed); } 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) void create_fouled(struct symbol *type); struct symbol *befoul(struct symbol *type); extern struct ident bad_address_space; static inline bool valid_as(struct ident *as) { return as && as != &bad_address_space; } static inline void combine_address_space(struct position pos, struct ident **tas, struct ident *sas) { struct ident *as; if (!sas) return; as = *tas; if (!as) *tas = sas; else if (as != sas) { *tas = &bad_address_space; sparse_error(pos, "multiple address spaces given"); } } #endif /* SYMBOL_H */ sparse-0.6.4/target-alpha.c000066400000000000000000000017321411531012200155240ustar00rootroot00000000000000#include "symbol.h" #include "target.h" #include "machine.h" #include "builtin.h" static void predefine_alpha(const struct target *self) { predefine("__alpha__", 1, "1"); predefine("__alpha", 1, "1"); } static const struct builtin_fn builtins_alpha[] = { { "__builtin_alpha_cmpbge", &long_ctype, 0, { &long_ctype, &long_ctype }}, { "__builtin_alpha_extbl", &long_ctype, 0, { &long_ctype, &long_ctype }}, { "__builtin_alpha_extwl", &long_ctype, 0, { &long_ctype, &long_ctype }}, { "__builtin_alpha_insbl", &long_ctype, 0, { &long_ctype, &long_ctype }}, { "__builtin_alpha_inslh", &long_ctype, 0, { &long_ctype, &long_ctype }}, { "__builtin_alpha_insql", &long_ctype, 0, { &long_ctype, &long_ctype }}, { "__builtin_alpha_inswl", &long_ctype, 0, { &long_ctype, &long_ctype }}, { } }; const struct target target_alpha = { .mach = MACH_ALPHA, .bitness = ARCH_LP64, .has_int128 = 1, .bits_in_longdouble = 64, .predefine = predefine_alpha, .builtins = builtins_alpha, }; sparse-0.6.4/target-arm.c000066400000000000000000000020661411531012200152170ustar00rootroot00000000000000#include "symbol.h" #include "target.h" #include "machine.h" static void init_arm(const struct target *self) { fast16_ctype = &int_ctype; ufast16_ctype = &uint_ctype; fast32_ctype = &int_ctype; ufast32_ctype = &uint_ctype; if (arch_os == OS_NONE) { int32_ctype = &long_ctype; uint32_ctype = &ulong_ctype; fast8_ctype = &int_ctype; ufast8_ctype = &uint_ctype; } } static void predefine_arm(const struct target *self) { predefine("__arm__", 1, "1"); predefine("__VFP_FP__", 1, "1"); switch (arch_fp_abi) { case FP_ABI_HARD: predefine("__ARM_PCS_VFP", 1, "1"); break; case FP_ABI_SOFT: predefine("__SOFTFP__", 1, "1"); /* fall-through */ case FP_ABI_HYBRID: predefine("__ARM_PCS", 1, "1"); break; } if (arch_big_endian) predefine("__ARMEB__", 0, "1"); else predefine("__ARMEL__", 0, "1"); } const struct target target_arm = { .mach = MACH_ARM, .bitness = ARCH_LP32, .big_endian = 0, .unsigned_char = 1, .wchar = &uint_ctype, .bits_in_longdouble = 64, .max_fp_alignment = 8, .init = init_arm, .predefine = predefine_arm, }; sparse-0.6.4/target-arm64.c000066400000000000000000000015171411531012200153710ustar00rootroot00000000000000#include "symbol.h" #include "target.h" #include "machine.h" static void init_arm64(const struct target *self) { if (arch_cmodel == CMODEL_UNKNOWN) arch_cmodel = CMODEL_SMALL; } static void predefine_arm64(const struct target *self) { static const char *cmodels[CMODEL_LAST] = { [CMODEL_LARGE] = "LARGE", [CMODEL_SMALL] = "SMALL", [CMODEL_TINY] = "TINY", }; const char *cmodel = cmodels[arch_cmodel]; predefine("__aarch64__", 1, "1"); if (arch_big_endian) predefine("__AARCH64EB__", 0, "1"); else predefine("__AARCH64EL__", 0, "1"); if (cmodel) predefine_strong("__AARCH64_CMODEL_%s__", cmodel); } const struct target target_arm64 = { .mach = MACH_ARM64, .bitness = ARCH_LP64, .big_endian = 0, .unsigned_char = 1, .has_int128 = 1, .wchar = &uint_ctype, .init = init_arm64, .predefine = predefine_arm64, }; sparse-0.6.4/target-bfin.c000066400000000000000000000010631411531012200153520ustar00rootroot00000000000000#include "symbol.h" #include "target.h" #include "machine.h" #include "builtin.h" static void predefine_bfin(const struct target *self) { predefine("__BFIN__", 1, "1"); predefine("__bfin__", 1, "1"); } static const struct builtin_fn builtins_bfin[] = { { "__builtin_bfin_csync", &void_ctype, 0 }, { "__builtin_bfin_ssync", &void_ctype, 0 }, { "__builtin_bfin_norm_fr1x32", &int_ctype, 0, { &int_ctype }}, { } }; const struct target target_bfin = { .mach = MACH_BFIN, .bitness = ARCH_LP32, .predefine = predefine_bfin, .builtins = builtins_bfin, }; sparse-0.6.4/target-default.c000066400000000000000000000002761411531012200160650ustar00rootroot00000000000000#include "symbol.h" #include "target.h" #include "machine.h" const struct target target_default = { .mach = MACH_UNKNOWN, .bitness = ARCH_LP64, .big_endian = 0, .unsigned_char = 0, }; sparse-0.6.4/target-h8300.c000066400000000000000000000012271411531012200152000ustar00rootroot00000000000000#include "symbol.h" #include "target.h" #include "machine.h" static void init_h8300(const struct target *self) { intptr_ctype = &int_ctype; uintptr_ctype = &uint_ctype; ssize_t_ctype = &long_ctype; size_t_ctype = &ulong_ctype; wchar_ctype = &ushort_ctype; fast16_ctype = &int_ctype; ufast16_ctype = &uint_ctype; fast32_ctype = &int_ctype; ufast32_ctype = &uint_ctype; } static void predefine_h8300(const struct target *self) { predefine("__H8300H__", 1, "1"); } const struct target target_h8300 = { .mach = MACH_H8300, .bitness = ARCH_LP32, .big_endian = true, .bits_in_longdouble = 64, .init = init_h8300, .predefine = predefine_h8300, }; sparse-0.6.4/target-m68k.c000066400000000000000000000010741411531012200152230ustar00rootroot00000000000000#include "symbol.h" #include "target.h" #include "machine.h" static void init_m68k(const struct target *self) { fast16_ctype = &int_ctype; ufast16_ctype = &uint_ctype; fast32_ctype = &int_ctype; ufast32_ctype = &uint_ctype; } static void predefine_m68k(const struct target *self) { predefine("__m68k__", 1, "1"); } const struct target target_m68k = { .mach = MACH_M68K, .bitness = ARCH_LP32, .big_endian = 1, .unsigned_char = 0, .wchar = &long_ctype, .bits_in_longdouble = 96, .max_fp_alignment = 4, .init = init_m68k, .predefine = predefine_m68k, }; sparse-0.6.4/target-microblaze.c000066400000000000000000000012711411531012200165640ustar00rootroot00000000000000#include "symbol.h" #include "target.h" #include "machine.h" static void init_microblaze(const struct target *self) { fast16_ctype = &int_ctype; ufast16_ctype = &uint_ctype; fast32_ctype = &int_ctype; ufast32_ctype = &uint_ctype; } static void predefine_microblaze(const struct target *self) { predefine("__MICROBLAZE__", 1, "1"); predefine("__microblaze__", 1, "1"); if (arch_big_endian) predefine("__MICROBLAZEEB__", 1, "1"); else predefine("__MICROBLAZEEL__", 1, "1"); } const struct target target_microblaze = { .mach = MACH_MICROBLAZE, .bitness = ARCH_LP32, .big_endian = true, .bits_in_longdouble = 64, .init = init_microblaze, .predefine = predefine_microblaze, }; sparse-0.6.4/target-mips.c000066400000000000000000000023701411531012200154060ustar00rootroot00000000000000#include "symbol.h" #include "target.h" #include "machine.h" static void predefine_mips(const struct target *self) { predefine("__mips__", 1, "1"); predefine("__mips", 1, "%d", ptr_ctype.bit_size); predefine("_MIPS_SZINT", 1, "%d", int_ctype.bit_size); predefine("_MIPS_SZLONG", 1, "%d", long_ctype.bit_size); predefine("_MIPS_SZPTR", 1, "%d", ptr_ctype.bit_size); if (arch_big_endian) { predefine("_MIPSEB", 1, "1"); predefine("__MIPSEB", 1, "1"); predefine("__MIPSEB__", 1, "1"); } else { predefine("_MIPSEL", 1, "1"); predefine("__MIPSEL", 1, "1"); predefine("__MIPSEL__", 1, "1"); } } static void predefine_mips32(const struct target *self) { predefine_mips(self); } const struct target target_mips32 = { .mach = MACH_MIPS32, .bitness = ARCH_LP32, .big_endian = 1, .unsigned_char = 0, .bits_in_longdouble = 64, .max_fp_alignment = 8, .target_64bit = &target_mips64, .predefine = predefine_mips32, }; static void predefine_mips64(const struct target *self) { predefine("__mips64", 1, "64"); predefine_mips(self); } const struct target target_mips64 = { .mach = MACH_MIPS64, .bitness = ARCH_LP64, .big_endian = 1, .unsigned_char = 0, .has_int128 = 1, .target_32bit = &target_mips32, .predefine = predefine_mips64, }; sparse-0.6.4/target-nds32.c000066400000000000000000000012001411531012200153560ustar00rootroot00000000000000#include "symbol.h" #include "target.h" #include "machine.h" static void init_nds32(const struct target *self) { fast16_ctype = &int_ctype; ufast16_ctype = &uint_ctype; fast32_ctype = &int_ctype; ufast32_ctype = &uint_ctype; wchar_ctype = &uint_ctype; } static void predefine_nds32(const struct target *self) { predefine("__NDS32__", 1, "1"); predefine("__nds32__", 1, "1"); predefine_weak("__NDS32_E%c__", arch_big_endian ? 'B' : 'L'); } const struct target target_nds32 = { .mach = MACH_NDS32, .bitness = ARCH_LP32, .big_endian = false, .bits_in_longdouble = 64, .init = init_nds32, .predefine = predefine_nds32, }; sparse-0.6.4/target-nios2.c000066400000000000000000000021121411531012200154620ustar00rootroot00000000000000#include "symbol.h" #include "target.h" #include "machine.h" #include "builtin.h" static void init_nios2(const struct target *self) { fast16_ctype = &int_ctype; ufast16_ctype = &uint_ctype; fast32_ctype = &int_ctype; ufast32_ctype = &uint_ctype; } static void predefine_nios2(const struct target *self) { predefine("__NIOS2", 1, "1"); predefine("__NIOS2__", 1, "1"); predefine("__nios2", 1, "1"); predefine("__nios2__", 1, "1"); if (arch_big_endian) { predefine("__nios2_big_endian", 1, "1"); predefine("__nios2_big_endian__", 1, "1"); } else { predefine("__nios2_little_endian", 1, "1"); predefine("__nios2_little_endian__", 1, "1"); } } static const struct builtin_fn builtins_nios2[] = { { "__builtin_rdctl", &int_ctype, 0, { &int_ctype }}, { "__builtin_wrctl", &void_ctype, 0, { &int_ctype, &int_ctype }}, { "__builtin_custom_ini", &int_ctype, 0, { &int_ctype }}, { } }; const struct target target_nios2 = { .mach = MACH_NIOS2, .bitness = ARCH_LP32, .bits_in_longdouble = 64, .init = init_nios2, .predefine = predefine_nios2, .builtins = builtins_nios2, }; sparse-0.6.4/target-openrisc.c000066400000000000000000000011061411531012200162540ustar00rootroot00000000000000#include "symbol.h" #include "target.h" #include "machine.h" static void init_openrisc(const struct target *self) { fast16_ctype = &int_ctype; ufast16_ctype = &uint_ctype; fast32_ctype = &int_ctype; ufast32_ctype = &uint_ctype; wchar_ctype = &uint_ctype; } static void predefine_openrisc(const struct target *self) { predefine_weak("__OR1K__"); predefine_weak("__or1k__"); } const struct target target_openrisc = { .mach = MACH_NDS32, .bitness = ARCH_LP32, .big_endian = true, .bits_in_longdouble = 64, .init = init_openrisc, .predefine = predefine_openrisc, }; sparse-0.6.4/target-ppc.c000066400000000000000000000032171411531012200152210ustar00rootroot00000000000000#include "symbol.h" #include "target.h" #include "machine.h" #include "expression.h" static void predefine_ppc(const struct target *self) { predefine("__powerpc__", 1, "1"); predefine("__powerpc", 1, "1"); predefine("__ppc__", 1, "1"); predefine("__PPC__", 1, "1"); predefine("__PPC", 1, "1"); predefine("_ARCH_PPC", 1, "1"); if (arch_big_endian) predefine("_BIG_ENDIAN", 1, "1"); if (ldouble_ctype.bit_size == 128) { predefine("__LONGDOUBLE128", 1, "1"); predefine("__LONG_DOUBLE_128__", 1, "1"); } } static const char *asm_constraint_ppc(struct asm_operand *op, int c, const char *str) { switch (c) { case 'Z': op->is_memory = true; break; } return str; } static void init_ppc32(const struct target *self) { fast16_ctype = &int_ctype; ufast16_ctype = &uint_ctype; fast32_ctype = &int_ctype; ufast32_ctype = &uint_ctype; } static void predefine_ppc32(const struct target *self) { predefine_ppc(self); } const struct target target_ppc32 = { .mach = MACH_PPC32, .bitness = ARCH_LP32, .big_endian = 1, .unsigned_char = 1, .wchar = &long_ctype, .target_64bit = &target_ppc64, .init = init_ppc32, .predefine = predefine_ppc32, .asm_constraint = asm_constraint_ppc, }; static void predefine_ppc64(const struct target *self) { predefine("__powerpc64__", 1, "1"); predefine("__ppc64__", 1, "1"); predefine("__PPC64__", 1, "1"); predefine("_ARCH_PPC64", 1, "1"); predefine_ppc(self); } const struct target target_ppc64 = { .mach = MACH_PPC64, .bitness = ARCH_LP64, .big_endian = 1, .unsigned_char = 1, .has_int128 = 1, .target_32bit = &target_ppc32, .predefine = predefine_ppc64, .asm_constraint = asm_constraint_ppc, }; sparse-0.6.4/target-riscv.c000066400000000000000000000074641411531012200155750ustar00rootroot00000000000000#include "lib.h" #include "symbol.h" #include "target.h" #include "machine.h" #include #define RISCV_32BIT (1 << 0) #define RISCV_64BIT (1 << 1) #define RISCV_MUL (1 << 2) #define RISCV_DIV (1 << 3) #define RISCV_ATOMIC (1 << 4) #define RISCV_FLOAT (1 << 5) #define RISCV_DOUBLE (1 << 6) #define RISCV_FDIV (1 << 7) #define RISCV_COMP (1 << 8) #define RISCV_EMBD (1 << 9) #define RISCV_FPU (RISCV_FLOAT|RISCV_DOUBLE|RISCV_FDIV) #define RISCV_GENERIC (RISCV_MUL|RISCV_DIV|RISCV_ATOMIC|RISCV_FPU) static unsigned int riscv_flags; static void parse_march_riscv(const char *arg) { static struct { const char *pattern; unsigned int flags; } basic_sets[] = { { "rv32i", RISCV_32BIT }, { "rv32e", RISCV_32BIT|RISCV_EMBD }, { "rv32g", RISCV_32BIT|RISCV_GENERIC }, { "rv64i", RISCV_64BIT }, { "rv64g", RISCV_64BIT|RISCV_GENERIC }, }, extensions[] = { { "m", RISCV_MUL|RISCV_DIV }, { "a", RISCV_ATOMIC }, { "f", RISCV_FLOAT|RISCV_FDIV }, { "d", RISCV_DOUBLE|RISCV_FDIV }, { "g", RISCV_GENERIC }, { "q", 0 }, { "l", 0 }, { "c", RISCV_COMP }, { "b", 0 }, { "j", 0 }, { "t", 0 }, { "p", 0 }, { "v", 0 }, { "n", 0 }, { "h", 0 }, { "s", 0 }, }; int i; for (i = 0; i < ARRAY_SIZE(basic_sets); i++) { const char *pat = basic_sets[i].pattern; size_t len = strlen(pat); if (!strncmp(arg, pat, len)) { riscv_flags |= basic_sets[i].flags; arg += len; goto ext; } } die("invalid argument to '-march': '%s'\n", arg); ext: for (i = 0; i < ARRAY_SIZE(extensions); i++) { const char *pat = extensions[i].pattern; size_t len = strlen(pat); if (!strncmp(arg, pat, len)) { riscv_flags |= extensions[i].flags; arg += len; } } if (arg[0]) die("invalid argument to '-march': '%s'\n", arg); } static void init_riscv(const struct target *self) { if (arch_cmodel == CMODEL_UNKNOWN) arch_cmodel = CMODEL_MEDLOW; if (fpic) arch_cmodel = CMODEL_PIC; if (riscv_flags == 0) riscv_flags = self->flags; } static void init_riscv32(const struct target *self) { fast16_ctype = &int_ctype; ufast16_ctype = &uint_ctype; fast32_ctype = &int_ctype; ufast32_ctype = &uint_ctype; init_riscv(self); } static void predefine_riscv(const struct target *self) { static const char *cmodels[CMODEL_LAST] = { [CMODEL_MEDANY] = "medany", [CMODEL_MEDLOW] = "medlow", [CMODEL_PIC] = "pic", }; const char *cmodel = cmodels[arch_cmodel]; predefine("__riscv", 1, "1"); predefine("__riscv_xlen", 1, "%d", ptr_ctype.bit_size); if (riscv_flags & RISCV_ATOMIC) predefine("__riscv_atomic", 1, "1"); if (riscv_flags & RISCV_COMP) predefine("__riscv_compressed", 1, "1"); if (riscv_flags & RISCV_DIV) predefine("__riscv_div", 1, "1"); if (riscv_flags & RISCV_EMBD) predefine("__riscv_32e", 1, "1"); if (riscv_flags & RISCV_FPU) predefine("__riscv_flen", 1, "%d", (riscv_flags & RISCV_DOUBLE) ? 64 : 32); if (riscv_flags & RISCV_FDIV) predefine("__riscv_fdiv", 1, "1"); if (riscv_flags & RISCV_FDIV) predefine("__riscv_fsqrt", 1, "1"); if (riscv_flags & RISCV_MUL) predefine("__riscv_mul", 1, "1"); if ((riscv_flags & RISCV_MUL) && (riscv_flags & RISCV_DIV)) predefine("__riscv_muldiv", 1, "1"); if (cmodel) predefine_strong("__riscv_cmodel_%s", cmodel); } const struct target target_riscv32 = { .mach = MACH_RISCV32, .bitness = ARCH_LP32, .big_endian = 0, .unsigned_char = 1, .flags = RISCV_32BIT|RISCV_GENERIC|RISCV_COMP, .target_64bit = &target_riscv64, .init = init_riscv32, .predefine = predefine_riscv, .parse_march = parse_march_riscv, }; const struct target target_riscv64 = { .mach = MACH_RISCV64, .bitness = ARCH_LP64, .big_endian = 0, .unsigned_char = 1, .has_int128 = 1, .flags = RISCV_64BIT|RISCV_GENERIC|RISCV_COMP, .target_32bit = &target_riscv32, .init = init_riscv, .predefine = predefine_riscv, .parse_march = parse_march_riscv, }; sparse-0.6.4/target-s390.c000066400000000000000000000025161411531012200151360ustar00rootroot00000000000000#include "symbol.h" #include "target.h" #include "machine.h" #include "expression.h" static void init_s390(const struct target *self) { intptr_ctype = &int_ctype; uintptr_ctype = &uint_ctype; fast16_ctype = &int_ctype; ufast16_ctype = &uint_ctype; fast32_ctype = &int_ctype; ufast32_ctype = &uint_ctype; } static void predefine_s390(const struct target *self) { predefine("__s390__", 1, "1"); } static const char *asm_constraint_s390(struct asm_operand *op, int c, const char *str) { switch (c) { case 'R': case 'S': case 'T': op->is_memory = true; break; } return str; } const struct target target_s390 = { .mach = MACH_S390, .bitness = ARCH_LP32, .big_endian = 1, .unsigned_char = 1, .size_t_long = 1, .bits_in_longdouble = 64, .max_fp_alignment = 8, .target_64bit = &target_s390x, .init = init_s390, .predefine = predefine_s390, .asm_constraint = asm_constraint_s390, }; static void predefine_s390x(const struct target *self) { predefine("__zarch__", 1, "1"); predefine("__s390x__", 1, "1"); predefine_s390(self); } const struct target target_s390x = { .mach = MACH_S390X, .bitness = ARCH_LP64, .big_endian = 1, .unsigned_char = 1, .has_int128 = 1, .bits_in_longdouble = 64, .max_fp_alignment = 8, .target_32bit = &target_s390, .predefine = predefine_s390x, .asm_constraint = asm_constraint_s390, }; sparse-0.6.4/target-sh.c000066400000000000000000000012321411531012200150440ustar00rootroot00000000000000#include "symbol.h" #include "target.h" #include "machine.h" static void init_sh(const struct target *self) { int64_ctype = &llong_ctype; uint64_ctype = &ullong_ctype; intptr_ctype = &int_ctype; uintptr_ctype = &uint_ctype; fast16_ctype = &int_ctype; ufast16_ctype = &uint_ctype; fast32_ctype = &int_ctype; ufast32_ctype = &uint_ctype; wchar_ctype = &long_ctype; } static void predefine_sh(const struct target *self) { predefine_weak("__SH4__"); predefine_weak("__sh__"); } const struct target target_sh = { .mach = MACH_SH, .bitness = ARCH_LP32, .big_endian = false, .bits_in_longdouble = 64, .init = init_sh, .predefine = predefine_sh, }; sparse-0.6.4/target-sparc.c000066400000000000000000000031271411531012200155470ustar00rootroot00000000000000#include "symbol.h" #include "target.h" #include "machine.h" static int sparc_version; static void predefine_sparc(const struct target *self) { predefine("__sparc__", 1, "1"); predefine("__sparc", 1, "1"); predefine_nostd("sparc"); predefine_weak("__sparc_v%d__", sparc_version); predefine_weak("__sparcv%d__", sparc_version); predefine_weak("__sparcv%d", sparc_version); } static void init_sparc32(const struct target *target) { fast16_ctype = &int_ctype; ufast16_ctype = &uint_ctype; fast32_ctype = &int_ctype; ufast32_ctype = &uint_ctype; if (!sparc_version) sparc_version = 8; if (arch_os == OS_SUNOS) { wint_ctype = &long_ctype; wchar_ctype = &long_ctype; bits_in_longdouble = 128; max_fp_alignment = 16; funsigned_char = 0; } } static void predefine_sparc32(const struct target *self) { predefine_sparc(self); } const struct target target_sparc32 = { .mach = MACH_SPARC32, .bitness = ARCH_LP32, .big_endian = 1, .unsigned_char = 0, .bits_in_longdouble = 64, .max_fp_alignment = 8, .init = init_sparc32, .target_64bit = &target_sparc64, .predefine = predefine_sparc32, }; static void init_sparc64(const struct target *target) { if (!sparc_version) sparc_version = 9; } static void predefine_sparc64(const struct target *self) { predefine("__sparc64__", 1, "1"); predefine("__arch64__", 1, "1"); predefine_sparc(self); } const struct target target_sparc64 = { .mach = MACH_SPARC64, .bitness = ARCH_LP64, .big_endian = 1, .unsigned_char = 0, .has_int128 = 1, .target_32bit = &target_sparc32, .init = init_sparc64, .predefine = predefine_sparc64, }; sparse-0.6.4/target-x86.c000066400000000000000000000066371411531012200150750ustar00rootroot00000000000000#include "symbol.h" #include "target.h" #include "machine.h" #include "builtin.h" static void predefine_i386(const struct target *self) { predefine("__i386__", 1, "1"); predefine("__i386", 1, "1"); predefine_nostd("i386"); } static void predefine_x86_64(const struct target *self) { predefine("__x86_64__", 1, "1"); predefine("__x86_64", 1, "1"); predefine("__amd64__", 1, "1"); predefine("__amd64", 1, "1"); } static void init_x86_common(const struct target *target) { switch (arch_os) { case OS_CYGWIN: wchar_ctype = &ushort_ctype; break; case OS_FREEBSD: wint_ctype = &int_ctype; break; case OS_OPENBSD: size_t_ctype = &ulong_ctype; ssize_t_ctype = &long_ctype; wchar_ctype = &int_ctype; wint_ctype = &int_ctype; fast16_ctype = &short_ctype; ufast16_ctype = &ushort_ctype; break; } } static const struct builtin_fn builtins_x86_common[] = { { "__builtin_ia32_pause", &void_ctype, 0, }, { } }; static void init_i386(const struct target *target) { fast16_ctype = &int_ctype; ufast16_ctype = &uint_ctype; fast32_ctype = &int_ctype; ufast32_ctype = &uint_ctype; init_x86_common(target); } const struct target target_i386 = { .mach = MACH_I386, .bitness = ARCH_LP32, .big_endian = 0, .unsigned_char = 0, .wchar = &long_ctype, .bits_in_longdouble = 96, .max_fp_alignment = 4, .target_64bit = &target_x86_64, .init = init_i386, .predefine = predefine_i386, .builtins = builtins_x86_common, }; static void init_x86_x32(const struct target *target) { init_x86_common(target); max_int_alignment = 8; fast16_ctype = &int_ctype; ufast16_ctype = &uint_ctype; fast32_ctype = &int_ctype; ufast32_ctype = &uint_ctype; wchar_ctype = &long_ctype; } static const struct target target_x86_x32 = { .mach = MACH_X86_64, .bitness = ARCH_X32, .big_endian = 0, .unsigned_char = 0, .has_int128 = 1, .bits_in_longdouble = 128, .max_fp_alignment = 16, .target_32bit = &target_i386, .target_64bit = &target_x86_64, .init = init_x86_x32, .predefine = predefine_x86_64, }; static void init_x86_64(const struct target *target) { init_x86_common(target); switch (arch_os) { case OS_CYGWIN: break; case OS_DARWIN: int64_ctype = &llong_ctype; uint64_ctype = &ullong_ctype; wint_ctype = &int_ctype; fast16_ctype = &short_ctype; ufast16_ctype = &ushort_ctype; fast32_ctype = &int_ctype; ufast32_ctype = &uint_ctype; fast64_ctype = &llong_ctype; ufast64_ctype = &ullong_ctype; break; case OS_FREEBSD: fast16_ctype = &short_ctype; ufast16_ctype = &ushort_ctype; fast32_ctype = &int_ctype; ufast32_ctype = &uint_ctype; break; case OS_NETBSD: fast8_ctype = &int_ctype; ufast8_ctype = &uint_ctype; fast16_ctype = &int_ctype; ufast16_ctype = &uint_ctype; fast32_ctype = &int_ctype; ufast32_ctype = &uint_ctype; wint_ctype = &int_ctype; break; case OS_OPENBSD: fast32_ctype = &int_ctype; ufast32_ctype = &uint_ctype; int64_ctype = &llong_ctype; uint64_ctype = &ullong_ctype; intmax_ctype = &llong_ctype; uintmax_ctype = &ullong_ctype; least64_ctype = &long_ctype; uleast64_ctype = &ulong_ctype; break; } } const struct target target_x86_64 = { .mach = MACH_X86_64, .bitness = ARCH_LP64, .big_endian = 0, .unsigned_char = 0, .has_int128 = 1, .bits_in_longdouble = 128, .max_fp_alignment = 16, .target_32bit = &target_i386, .target_x32bit = &target_x86_x32, .init = init_x86_64, .predefine = predefine_x86_64, .builtins = builtins_x86_common, }; sparse-0.6.4/target-xtensa.c000066400000000000000000000011071411531012200157350ustar00rootroot00000000000000#include "symbol.h" #include "target.h" #include "machine.h" static void init_xtensa(const struct target *self) { fast16_ctype = &int_ctype; ufast16_ctype = &uint_ctype; fast32_ctype = &int_ctype; ufast32_ctype = &uint_ctype; wchar_ctype = &long_ctype; } static void predefine_xtensa(const struct target *self) { predefine("__XTENSA__", 1, "1"); predefine("__xtensa__", 1, "1"); } const struct target target_xtensa = { .mach = MACH_XTENSA, .bitness = ARCH_LP32, .big_endian = true, .bits_in_longdouble = 64, .init = init_xtensa, .predefine = predefine_xtensa, }; sparse-0.6.4/target.c000066400000000000000000000147461411531012200144520ustar00rootroot00000000000000#include #include #include "symbol.h" #include "target.h" #include "machine.h" struct symbol *ptrdiff_ctype; struct symbol *intptr_ctype; struct symbol *uintptr_ctype; struct symbol *size_t_ctype = &ulong_ctype; struct symbol *ssize_t_ctype = &long_ctype; struct symbol *intmax_ctype = &long_ctype; struct symbol *uintmax_ctype = &ulong_ctype; struct symbol *int64_ctype = &long_ctype; struct symbol *uint64_ctype = &ulong_ctype; struct symbol *int32_ctype = &int_ctype; struct symbol *uint32_ctype = &uint_ctype; struct symbol *wchar_ctype = &int_ctype; struct symbol *wint_ctype = &uint_ctype; struct symbol *least8_ctype = &schar_ctype; struct symbol *uleast8_ctype = &uchar_ctype; struct symbol *least16_ctype = &short_ctype; struct symbol *uleast16_ctype = &ushort_ctype; struct symbol *least32_ctype = &int_ctype; struct symbol *uleast32_ctype = &uint_ctype; struct symbol *least64_ctype = &llong_ctype; struct symbol *uleast64_ctype = &ullong_ctype; struct symbol *fast8_ctype = &schar_ctype; struct symbol *ufast8_ctype = &uchar_ctype; struct symbol *fast16_ctype = &long_ctype; struct symbol *ufast16_ctype = &ulong_ctype; struct symbol *fast32_ctype = &long_ctype; struct symbol *ufast32_ctype = &ulong_ctype; struct symbol *fast64_ctype = &long_ctype; struct symbol *ufast64_ctype = &ulong_ctype; struct symbol *sig_atomic_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 = 64; int bits_in_longlong = 64; int bits_in_longlonglong = 128; int max_int_alignment = 8; /* * Floating point data types */ int bits_in_float = 32; int bits_in_double = 64; int bits_in_longdouble = 128; int max_fp_alignment = 16; /* * Pointer data type */ int bits_in_pointer = 64; int pointer_alignment = 8; /* * Enum data types */ int bits_in_enum = 32; int enum_alignment = 4; static const struct target *targets[] = { [MACH_ALPHA] = &target_alpha, [MACH_ARM] = &target_arm, [MACH_ARM64] = &target_arm64, [MACH_BFIN] = &target_bfin, [MACH_H8300] = &target_h8300, [MACH_I386] = &target_i386, [MACH_M68K] = &target_m68k, [MACH_MICROBLAZE] = &target_microblaze, [MACH_MIPS32] = &target_mips32, [MACH_MIPS64] = &target_mips64, [MACH_NDS32] = &target_nds32, [MACH_NIOS2] = &target_nios2, [MACH_OPENRISC] = &target_openrisc, [MACH_PPC32] = &target_ppc32, [MACH_PPC64] = &target_ppc64, [MACH_RISCV32] = &target_riscv32, [MACH_RISCV64] = &target_riscv64, [MACH_S390] = &target_s390, [MACH_S390X] = &target_s390x, [MACH_SH] = &target_sh, [MACH_SPARC32] = &target_sparc32, [MACH_SPARC64] = &target_sparc64, [MACH_X86_64] = &target_x86_64, [MACH_XTENSA] = &target_xtensa, [MACH_UNKNOWN] = &target_default, }; const struct target *arch_target = &target_default; enum machine target_parse(const char *name) { static const struct arch { const char *name; enum machine mach; char bits; } archs[] = { { "alpha", MACH_ALPHA, 64, }, { "aarch64", MACH_ARM64, 64, }, { "arm64", MACH_ARM64, 64, }, { "arm", MACH_ARM, 32, }, { "bfin", MACH_BFIN, 32, }, { "h8300", MACH_H8300, 32, }, { "i386", MACH_I386, 32, }, { "m68k", MACH_M68K, 32, }, { "microblaze", MACH_MICROBLAZE,32, }, { "mips", MACH_MIPS32, 0, }, { "nds32", MACH_NDS32, 32, }, { "nios2", MACH_NIOS2, 32, }, { "openrisc", MACH_OPENRISC, 32, }, { "powerpc", MACH_PPC32, 0, }, { "ppc", MACH_PPC32, 0, }, { "riscv", MACH_RISCV32, 0, }, { "s390x", MACH_S390X, 64, }, { "s390", MACH_S390, 32, }, { "sparc", MACH_SPARC32, 0, }, { "x86_64", MACH_X86_64, 64, }, { "x86-64", MACH_X86_64, 64, }, { "sh", MACH_SH, 32, }, { "xtensa", MACH_XTENSA, 32, }, { NULL }, }; const struct arch *p; for (p = &archs[0]; p->name; p++) { size_t len = strlen(p->name); if (strncmp(p->name, name, len) == 0) { enum machine mach = p->mach; const char *suf = name + len; int bits = p->bits; if (bits == 0) { if (!strcmp(suf, "") || !strcmp(suf, "32")) { ; } else if (!strcmp(suf, "64")) { mach += 1; } else { die("invalid architecture: %s", name); } } else { if (strcmp(suf, "")) die("invalid architecture: %s", name); } return mach; } } return MACH_UNKNOWN; } void target_os(const char *name) { static const struct os { const char *name; int os; } oses[] = { { "cygwin", OS_CYGWIN }, { "darwin", OS_DARWIN }, { "freebsd", OS_FREEBSD }, { "linux", OS_LINUX }, { "native", OS_NATIVE, }, { "netbsd", OS_NETBSD }, { "none", OS_NONE }, { "openbsd", OS_OPENBSD }, { "sunos", OS_SUNOS }, { "unix", OS_UNIX }, { NULL }, }, *p; for (p = &oses[0]; p->name; p++) { if (!strcmp(p->name, name)) { arch_os = p->os; return; } } die("invalid os: %s", name); } void target_config(enum machine mach) { const struct target *target = targets[mach]; arch_target = target; arch_m64 = target->bitness; arch_big_endian = target->big_endian; funsigned_char = target->unsigned_char; } void target_init(void) { const struct target *target = arch_target; switch (arch_m64) { case ARCH_X32: if (target->target_x32bit) target = target->target_x32bit; goto case_32bit; case ARCH_LP32: max_int_alignment = 4; if (target->target_32bit) target = target->target_32bit; /* fallthrough */ case_32bit: bits_in_long = 32; bits_in_pointer = 32; pointer_alignment = 4; size_t_ctype = &uint_ctype; ssize_t_ctype = &int_ctype; int64_ctype = &llong_ctype; uint64_ctype = &ullong_ctype; intmax_ctype = &llong_ctype; uintmax_ctype = &ullong_ctype; fast64_ctype = &llong_ctype; ufast64_ctype = &ullong_ctype; break; case ARCH_LLP64: bits_in_long = 32; size_t_ctype = &ullong_ctype; ssize_t_ctype = &llong_ctype; int64_ctype = &llong_ctype; uint64_ctype = &ullong_ctype; intmax_ctype = &llong_ctype; uintmax_ctype = &ullong_ctype; /* fallthrough */ case ARCH_LP64: if (target->target_64bit) target = target->target_64bit; break; } arch_target = target; if (fpie > fpic) fpic = fpie; if (target->wchar) wchar_ctype = target->wchar; if (target->wint) wint_ctype = target->wint; if (target->bits_in_longdouble) bits_in_longdouble = target->bits_in_longdouble; if (target->max_fp_alignment) max_fp_alignment = target->max_fp_alignment; if (target->init) target->init(target); if (arch_msize_long || target->size_t_long) { size_t_ctype = &ulong_ctype; ssize_t_ctype = &long_ctype; } if (fshort_wchar) wchar_ctype = &ushort_ctype; } sparse-0.6.4/target.h000066400000000000000000000101711411531012200144430ustar00rootroot00000000000000#ifndef TARGET_H #define TARGET_H #include "machine.h" extern struct symbol *size_t_ctype; extern struct symbol *ssize_t_ctype; extern struct symbol *ptrdiff_ctype; extern struct symbol *intptr_ctype; extern struct symbol *uintptr_ctype; extern struct symbol *intmax_ctype; extern struct symbol *uintmax_ctype; extern struct symbol *int64_ctype; extern struct symbol *uint64_ctype; extern struct symbol *int32_ctype; extern struct symbol *uint32_ctype; extern struct symbol *wchar_ctype; extern struct symbol *wint_ctype; extern struct symbol *least8_ctype; extern struct symbol *uleast8_ctype; extern struct symbol *least16_ctype; extern struct symbol *uleast16_ctype; extern struct symbol *least32_ctype; extern struct symbol *uleast32_ctype; extern struct symbol *least64_ctype; extern struct symbol *uleast64_ctype; extern struct symbol *fast8_ctype; extern struct symbol *ufast8_ctype; extern struct symbol *fast16_ctype; extern struct symbol *ufast16_ctype; extern struct symbol *fast32_ctype; extern struct symbol *ufast32_ctype; extern struct symbol *fast64_ctype; extern struct symbol *ufast64_ctype; extern struct symbol *sig_atomic_ctype; /* * For "__attribute__((aligned))" */ extern int max_alignment; /* * Integer data types */ extern int bits_in_bool; extern int bits_in_char; extern int bits_in_short; extern int bits_in_int; extern int bits_in_long; extern int bits_in_longlong; extern int bits_in_longlonglong; extern int max_int_alignment; /* * Floating point data types */ extern int bits_in_float; extern int bits_in_double; extern int bits_in_longdouble; extern int max_fp_alignment; /* * Pointer data type */ extern int bits_in_pointer; extern int pointer_alignment; /* * Enum data types */ extern int bits_in_enum; extern int enum_alignment; struct asm_operand; struct builtin_fn; struct target { enum machine mach; enum bitness bitness; unsigned int big_endian:1; unsigned int unsigned_char:1; unsigned int size_t_long:1; unsigned int has_int128:1; unsigned long flags; struct symbol *wchar; struct symbol *wint; unsigned int bits_in_longdouble; unsigned int max_fp_alignment; const struct target *target_32bit; const struct target *target_x32bit; const struct target *target_64bit; const struct builtin_fn *builtins; void (*init)(const struct target *self); void (*parse_march)(const char *arg); void (*predefine)(const struct target *self); const char *(*asm_constraint)(struct asm_operand *op, int c, const char *str); }; extern const struct target target_default; extern const struct target target_alpha; extern const struct target target_arm; extern const struct target target_arm64; extern const struct target target_bfin; extern const struct target target_h8300; extern const struct target target_m68k; extern const struct target target_microblaze; extern const struct target target_mips32; extern const struct target target_mips64; extern const struct target target_nds32; extern const struct target target_nios2; extern const struct target target_openrisc; extern const struct target target_ppc32; extern const struct target target_ppc64; extern const struct target target_riscv32; extern const struct target target_riscv64; extern const struct target target_s390; extern const struct target target_s390x; extern const struct target target_sh; extern const struct target target_sparc32; extern const struct target target_sparc64; extern const struct target target_i386; extern const struct target target_x86_64; extern const struct target target_xtensa; /* target.c */ extern const struct target *arch_target; enum machine target_parse(const char *name); void target_os(const char *name); void target_config(enum machine mach); void target_init(void); /* * 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.6.4/test-dissect.c000066400000000000000000000052531411531012200155700ustar00rootroot00000000000000#include "dissect.h" 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; static struct ident null; struct ident *ctx = &null; if (curr_stream != pos->stream) { curr_stream = pos->stream; printf("\nFILE: %s\n\n", stream_name(curr_stream)); } if (dissect_ctx) ctx = dissect_ctx->ident; printf("%4d:%-3d %-16.*s %s ", pos->line, pos->pos, ctx->len, ctx->name, show_mode(mode)); } static char symscope(struct symbol *sym) { if (sym_is_local(sym)) { if (!dissect_ctx) warning(sym->pos, "no context"); return '.'; } return ' '; } 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("%c %c %-32.*s %s\n", symscope(sym), sym->kind, sym->ident->len, sym->ident->name, show_typename(sym->ctype.base_type)); switch (sym->kind) { case 's': if (sym->type == SYM_STRUCT || sym->type == SYM_UNION) break; goto err; case 'f': if (sym->type != SYM_BAD && sym->ctype.base_type->type != SYM_FN) goto err; case 'v': if (sym->type == SYM_NODE || sym->type == SYM_BAD) break; goto err; default: goto err; } return; err: warning(*pos, "r_symbol bad sym type=%d kind=%d", sym->type, sym->kind); } 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("%c m %.*s.%-*.*s %s\n", symscope(sym), si->len, si->name, 32-1 - si->len, mi->len, mi->name, show_typename(mem ? mem->ctype.base_type : sym)); if (sym->ident && sym->kind != 's') warning(*pos, "r_member bad sym type=%d kind=%d", sym->type, sym->kind); if (mem && mem->kind != 'm') warning(*pos, "r_member bad mem->kind = %d", mem->kind); } static void r_symdef(struct symbol *sym) { r_symbol(-1, &sym->pos, sym); } static void r_memdef(struct symbol *sym, struct symbol *mem) { r_member(-1, &mem->pos, sym, mem); } int main(int argc, char **argv) { static struct reporter reporter = { .r_symdef = r_symdef, .r_memdef = r_memdef, .r_symbol = r_symbol, .r_member = r_member, }; struct string_list *filelist = NULL; sparse_initialize(argc, argv, &filelist); dissect(&reporter, filelist); return 0; } sparse-0.6.4/test-inspect.c000066400000000000000000000016031411531012200155720ustar00rootroot00000000000000 #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 "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); setlocale(LC_ALL, "C"); expand_symbols(sparse_initialize(argc, argv, &filelist)); FOR_EACH_PTR(filelist, file) { struct symbol_list *syms = sparse(file); expand_symbols(syms); concat_symbol_list(syms, &view_syms); } END_FOR_EACH_PTR(file); treeview_main(view_syms); return 0; } sparse-0.6.4/test-lexing.c000066400000000000000000000032641411531012200154200ustar00rootroot00000000000000/* * 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(filelist, file) { sparse(file); } END_FOR_EACH_PTR(file); show_identifier_stats(); return 0; } sparse-0.6.4/test-linearize.c000066400000000000000000000040031411531012200161040ustar00rootroot00000000000000/* * 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 (!(fdump_ir & PASS_FINAL)) continue; 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(filelist, file) { clean_up_symbols(sparse(file)); } END_FOR_EACH_PTR(file); report_stats(); return 0; } sparse-0.6.4/test-parsing.c000066400000000000000000000045661411531012200156030ustar00rootroot00000000000000/* * 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); printf("\n\n"); #endif FOR_EACH_PTR(filelist, file) { list = sparse(file); // Simplification clean_up_symbols(list); #if 1 // Show the end result. show_symbol_list(list); printf("\n\n"); #endif } END_FOR_EACH_PTR(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.6.4/test-show-type.c000066400000000000000000000010001411531012200160530ustar00rootroot00000000000000// SPDX-License-Identifier: MIT #include #include "lib.h" #include "symbol.h" static void show_symbols(struct symbol_list *list) { struct symbol *sym; FOR_EACH_PTR(list, sym) { printf("%s;\n", show_typename(sym)); } END_FOR_EACH_PTR(sym); } int main(int argc, char **argv) { struct string_list *filelist = NULL; char *file; sparse_initialize(argc, argv, &filelist); Wdecl = 0; FOR_EACH_PTR(filelist, file) { show_symbols(sparse(file)); } END_FOR_EACH_PTR(file); return has_error; } sparse-0.6.4/test-sort.c000066400000000000000000000015241411531012200151160ustar00rootroot00000000000000#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.6.4/test-unssa.c000066400000000000000000000032351411531012200152610ustar00rootroot00000000000000#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("%s\n", show_label(bb)); FOR_EACH_PTR(bb->insns, insn) { if (!insn->bb) continue; printf("\t%s\n", show_instruction(insn)); } END_FOR_EACH_PTR(insn); printf("\n"); } static void output_fn(struct entrypoint *ep) { struct basic_block *bb; unsigned long generation = ++bb_generation; struct symbol *sym = ep->name; const char *name = show_ident(sym->ident); if (sym->ctype.modifiers & MOD_STATIC) printf("\n\n%s:\n", name); else printf("\n\n.globl %s\n%s:\n", name, name); unssa(ep); FOR_EACH_PTR(ep->bbs, bb) { if (bb->generation == generation) continue; output_bb(bb, generation); } END_FOR_EACH_PTR(bb); } static int output_data(struct symbol *sym) { printf("symbol %s:\n", show_ident(sym->ident)); printf("\ttype = %d\n", sym->ctype.base_type->type); printf("\tmodif= %lx\n", sym->ctype.modifiers); return 0; } static int compile(struct symbol_list *list) { struct symbol *sym; FOR_EACH_PTR(list, sym) { struct entrypoint *ep; expand_symbol(sym); ep = linearize_symbol(sym); if (!(fdump_ir & PASS_FINAL)) continue; 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(filelist, file) { compile(sparse(file)); } END_FOR_EACH_PTR(file); report_stats(); return 0; } sparse-0.6.4/token.h000066400000000000000000000150061411531012200142770ustar00rootroot00000000000000#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; struct position pos; //position of the #include, if any /* 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 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_BAD, 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 struct position *pos, const char *, int fd, const char **next_path); extern int stream_prev(int stream); 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 struct position *pos, 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; } static inline int match_token_zero(struct token *token) { if (token_type(token) != TOKEN_NUMBER) return false; return token->number[0] == '0' && !token->number[1]; } #endif sparse-0.6.4/tokenize.c000066400000000000000000000563001411531012200150040ustar00rootroot00000000000000/* * 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; } int stream_prev(int stream) { if (stream < 0 || stream > input_stream_nr) return -1; stream = input_streams[stream].pos.stream; if (stream > input_stream_nr) return -1; return stream; } 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 buff[4][256]; static int n; char *buffer; if (!ident) return ""; buffer = buff[3 & ++n]; 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 || !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 struct position *pos, 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; if (pos) current->pos = *pos; else current->pos.stream = -1; 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 & Wnewline_eof) 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); eof_token_entry.pos = end->pos; 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 char cclass[257] = { ['0' + 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 ... '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, ['_' + 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, *buffer_end = buffer + sizeof (buffer); *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; token = stream->token; token_type(token) = TOKEN_NUMBER; token->number = xmemdup(buffer, p - buffer); 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), "missing terminating %c character", delim); /* assume delimiter is 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 && len <= 4) { 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 struct position *pos, 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(pos, 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.6.4/unssa.c000066400000000000000000000070131411531012200143020ustar00rootroot00000000000000/* * 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 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_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.6.4/utils.c000066400000000000000000000016741411531012200143200ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // Copyright (C) 2018 Luc Van Oostenryck #include "utils.h" #include "allocate.h" #include #include #include 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; } void *xmemdup(const void *src, size_t len) { return memcpy(__alloc_bytes(len), src, len); } char *xstrdup(const char *src) { return xmemdup(src, strlen(src) + 1); } char *xvasprintf(const char *fmt, va_list ap) { va_list ap2; char *str; int n; va_copy(ap2, ap); n = vsnprintf(NULL, 0, fmt, ap2) + 1; va_end(ap2); str = __alloc_bytes(n); vsnprintf(str, n, fmt, ap); return str; } char *xasprintf(const char *fmt, ...) { va_list ap; char *str; va_start(ap, fmt); str = xvasprintf(fmt, ap); va_end(ap); return str; } sparse-0.6.4/utils.h000066400000000000000000000024621411531012200143210ustar00rootroot00000000000000#ifndef UTILS_H #define UTILS_H /// // Miscellaneous utilities // ----------------------- #include #include /// // return the value coresponding to an hexadecimal digit unsigned int hexval(unsigned int c); /// // duplicate a memory buffer in a newly allocated buffer. // @src: a pointer to the memory buffer to be duplicated // @len: the size of the memory buffer to be duplicated // @return: a pointer to a copy of @src allocated via // :func:`__alloc_bytes()`. void *xmemdup(const void *src, size_t len); /// // duplicate a null-terminated string in a newly allocated buffer. // @src: a pointer to string to be duplicated // @return: a pointer to a copy of @str allocated via // :func:`__alloc_bytes()`. char *xstrdup(const char *src); /// // printf to allocated string // @fmt: the format followed by its arguments. // @return: the allocated & formatted string. // This function is similar to asprintf() but the resulting string // is allocated with __alloc_bytes(). char *xasprintf(const char *fmt, ...); /// // vprintf to allocated string // @fmt: the format // @ap: the variadic arguments // @return: the allocated & formatted string. // This function is similar to asprintf() but the resulting string // is allocated with __alloc_bytes(). char *xvasprintf(const char *fmt, va_list ap); #endif sparse-0.6.4/validation/000077500000000000000000000000001411531012200151365ustar00rootroot00000000000000sparse-0.6.4/validation/.gitignore000066400000000000000000000000451411531012200171250ustar00rootroot00000000000000# test-suite *.diff *.got *.expected sparse-0.6.4/validation/Waddress-array.c000066400000000000000000000006601411531012200201740ustar00rootroot00000000000000int 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: Waddress-array * check-command: sparse -Wno-decl -Waddress $file * * check-error-start Waddress-array.c:4:13: warning: the address of an array will always evaluate as true Waddress-array.c:12:13: warning: the address of an array will always evaluate as true * check-error-end */ sparse-0.6.4/validation/Waddress-function.c000066400000000000000000000004721411531012200207040ustar00rootroot00000000000000extern void func(void); int global_function(void) { if (func) return 1; return 0; } /* * check-name: Waddress-function * check-command: sparse -Wno-decl -Waddress $file * * check-error-start Waddress-function.c:5:13: warning: the address of a function will always evaluate as true * check-error-end */ sparse-0.6.4/validation/Waddress-space-all-attr.c000066400000000000000000000047601411531012200216740ustar00rootroot00000000000000/* Resembles include/linux/compiler_types.h */ #define __kernel __attribute__((address_space(0))) #define __user __attribute__((address_space(1))) #define __iomem __attribute__((address_space(2))) #define __percpu __attribute__((address_space(3))) #define __rcu __attribute__((address_space(4))) typedef unsigned long ulong; typedef struct s obj_t; static void expl(obj_t __kernel *k, obj_t __iomem *o, obj_t __user *p, obj_t __percpu *pc, obj_t __rcu *r) { (ulong)(k); (__UINTPTR_TYPE__)(k); (void *)(k); (obj_t*)(k); (obj_t __kernel*)(k); (ulong)(o); (__UINTPTR_TYPE__)(o); (void *)(o); (obj_t*)(o); (obj_t __iomem*)(o); (ulong)(p); (__UINTPTR_TYPE__)(p); (void *)(p); (obj_t*)(p); (obj_t __user*)(p); (ulong)(pc); (__UINTPTR_TYPE__)(pc); (void *)(pc); (obj_t*)(pc); (obj_t __percpu*)(pc); (ulong)(r); (__UINTPTR_TYPE__)(r); (void *)(r); (obj_t*)(r); (obj_t __rcu*)(r); } /* * check-name: Waddress-space-all-attr * check-command: sparse -Wcast-from-as -Wcast-to-as $file * * check-error-start Waddress-space-all-attr.c:21:10: warning: cast removes address space '' of expression Waddress-space-all-attr.c:21:22: warning: cast removes address space '' of expression Waddress-space-all-attr.c:22:10: warning: cast removes address space '' of expression Waddress-space-all-attr.c:23:10: warning: cast removes address space '' of expression Waddress-space-all-attr.c:26:10: warning: cast removes address space '' of expression Waddress-space-all-attr.c:26:22: warning: cast removes address space '' of expression Waddress-space-all-attr.c:27:10: warning: cast removes address space '' of expression Waddress-space-all-attr.c:28:10: warning: cast removes address space '' of expression Waddress-space-all-attr.c:31:10: warning: cast removes address space '' of expression Waddress-space-all-attr.c:31:23: warning: cast removes address space '' of expression Waddress-space-all-attr.c:32:10: warning: cast removes address space '' of expression Waddress-space-all-attr.c:33:10: warning: cast removes address space '' of expression Waddress-space-all-attr.c:36:10: warning: cast removes address space '' of expression Waddress-space-all-attr.c:36:22: warning: cast removes address space '' of expression Waddress-space-all-attr.c:37:10: warning: cast removes address space '' of expression Waddress-space-all-attr.c:38:10: warning: cast removes address space '' of expression * check-error-end */ sparse-0.6.4/validation/Waddress-space-from.c000066400000000000000000000037221411531012200211140ustar00rootroot00000000000000 #define __kernel __attribute__((address_space(0))) #define __user __attribute__((address_space(__user))) #define __iomem __attribute__((address_space(__iomem))) #define __percpu __attribute__((address_space(__percpu))) #define __rcu __attribute__((address_space(__rcu))) typedef struct s obj_t; static void expl(obj_t __kernel *k, obj_t __iomem *o, obj_t __user *p, obj_t __percpu *pc, obj_t __rcu *r) { (__UINTPTR_TYPE__)(k); // OK (unsigned long)(k); // OK (void *)(k); // OK (obj_t*)(k); // OK (obj_t __kernel*)(k); // OK (__UINTPTR_TYPE__)(o); // OK (unsigned long)(o); // OK (void *)(o); (obj_t*)(o); (obj_t __iomem*)(o); // OK (__UINTPTR_TYPE__)(p); // OK (unsigned long)(p); // OK (void *)(p); (obj_t*)(p); (obj_t __user*)(p); // OK (__UINTPTR_TYPE__)(pc); // OK (unsigned long)(pc); // OK (void *)(pc); (obj_t*)(pc); (obj_t __percpu*)(pc); // OK (__UINTPTR_TYPE__)(r); // OK (unsigned long)(r); // OK (void *)(r); (obj_t*)(r); (obj_t __rcu*)(r); // OK } /* * check-name: Waddress-space-from * check-command: sparse -Wno-cast-from-as $file * check-description: Test the removal of AS from a pointer but only * in the non-strict variant where casts to ulong (or uintptr_t) * are allowed. * * check-error-start Waddress-space-from.c:23:10: warning: cast removes address space '__iomem' of expression Waddress-space-from.c:24:10: warning: cast removes address space '__iomem' of expression Waddress-space-from.c:29:10: warning: cast removes address space '__user' of expression Waddress-space-from.c:30:10: warning: cast removes address space '__user' of expression Waddress-space-from.c:35:10: warning: cast removes address space '__percpu' of expression Waddress-space-from.c:36:10: warning: cast removes address space '__percpu' of expression Waddress-space-from.c:41:10: warning: cast removes address space '__rcu' of expression Waddress-space-from.c:42:10: warning: cast removes address space '__rcu' of expression * check-error-end */ sparse-0.6.4/validation/Waddress-space-strict.c000066400000000000000000000017341411531012200214620ustar00rootroot00000000000000#define __user __attribute__((address_space(1))) typedef unsigned long ulong; typedef struct s obj_t; static void expl(ulong u, void *v, obj_t *o, obj_t __user *p) { (obj_t*)(u); (obj_t __user*)(u); (obj_t*)(v); (obj_t __user*)(v); (ulong)(o); (void *)(o); (obj_t*)(o); (obj_t __user*)(o); (ulong)(p); // w! (void *)(p); // w (obj_t*)(p); // w (obj_t __user*)(p); // ok } /* * check-name: Waddress-space-strict * check-command: sparse -Wcast-from-as -Wcast-to-as $file * * check-error-start Waddress-space-strict.c:12:10: warning: cast adds address space '' to expression Waddress-space-strict.c:17:10: warning: cast adds address space '' to expression Waddress-space-strict.c:19:10: warning: cast removes address space '' of expression Waddress-space-strict.c:20:10: warning: cast removes address space '' of expression Waddress-space-strict.c:21:10: warning: cast removes address space '' of expression * check-error-end */ sparse-0.6.4/validation/Waddress-weak.c000066400000000000000000000011521411531012200200020ustar00rootroot00000000000000extern int var __attribute__((weak)); extern int arr[] __attribute__((weak)); extern int fun(void) __attribute__((weak)); int test_addr_weak_fun(void) { if ( &var) return 1; if ( arr) return 1; if ( &arr) return 1; if ( fun) return 1; if ( &fun) return 1; if ( *fun) return 1; if (!&var) return 0; if (! arr) return 0; if (!&arr) return 0; if (! fun) return 0; if (!&fun) return 0; if (!*fun) return 0; return -1; } /* * check-name: Waddress-weak * check-note: Undefined weak symbols (can) have a null address. * check-command: sparse -Wno-decl -Waddress $file * check-known-to-fail */ sparse-0.6.4/validation/Waddress.c000066400000000000000000000073701411531012200170650ustar00rootroot00000000000000extern int fun(void); extern int arr[]; extern int var; int test_address(int arg, int ptr[]) { if (fun()) return -1; if (var) return -1; if (arg) return -1; if (ptr) return -1; lab: if (arr) return 1; if (&arr) return 1; if (fun) return 1; if (&fun) return 1; if (*fun) return 1; if (&var) return 1; if (&arg) return 1; if (&&lab) return 1; return -1; } int test_address_not(int arg, int ptr[]) { if (!fun()) return -1; if (!var) return -1; if (!arg) return -1; if (!ptr) return -1; lab: if (!arr) return 0; if (!&arr) return 0; if (!fun) return 0; if (!&fun) return 0; if (!*fun) return 0; if (!&var) return 0; if (!&arg) return 0; if (!&&lab) return 0; return -1; } int test_address_cmp(int arg, int ptr[]) { if (fun() == 0) return -1; if (0 == fun()) return -1; if (var == 0) return -1; if (0 == var) return -1; if (arg == 0) return -1; if (0 == arg) return -1; if (ptr == 0) return -1; if (0 == ptr) return -1; lab: if (arr == 0) return 0; if (0 == arr) return 0; if (&arr == 0) return 0; if (0 == &arr) return 0; if (fun == 0) return 0; if (0 == fun) return 0; if (&fun == 0) return 0; if (0 == &fun) return 0; if (*fun == 0) return 0; if (0 == *fun) return 0; if (&var == 0) return 0; if (0 == &var) return 0; if (&arg == 0) return 0; if (0 == &arg) return 0; if (&&lab == 0) return 0; if (0 == &&lab) return 0; return -1; } /* * check-name: Waddress * check-command: sparse -Wno-decl -Wno-non-pointer-null -Waddress $file * check-known-to-fail * * check-error-start Waddress.c:14:13: warning: the address of an array will always evaluate as true Waddress.c:15:14: warning: the address of an array will always evaluate as true Waddress.c:16:13: warning: the address of a function will always evaluate as true Waddress.c:17:14: warning: the address of a function will always evaluate as true Waddress.c:18:13: warning: the address of a variable will always evaluate as true Waddress.c:19:13: warning: the address of a variable will always evaluate as true Waddress.c:20:13: warning: the address of a label will always evaluate as true Waddress.c:34:13: warning: the address of an array will always evaluate as true Waddress.c:35:13: warning: the address of an array will always evaluate as true Waddress.c:36:13: warning: the address of a function will always evaluate as true Waddress.c:37:13: warning: the address of a function will always evaluate as true Waddress.c:38:13: warning: the address of a variable will always evaluate as true Waddress.c:39:13: warning: the address of a variable will always evaluate as true Waddress.c:40:13: warning: the address of a label will always evaluate as true Waddress.c:57:13: warning: the address of an array will always evaluate as true Waddress.c:58:13: warning: the address of an array will always evaluate as true Waddress.c:59:13: warning: the address of an array will always evaluate as true Waddress.c:60:13: warning: the address of an array will always evaluate as true Waddress.c:61:13: warning: the address of a function will always evaluate as true Waddress.c:62:13: warning: the address of a function will always evaluate as true Waddress.c:63:13: warning: the address of a function will always evaluate as true Waddress.c:64:13: warning: the address of a function will always evaluate as true Waddress.c:65:13: warning: the address of a variable will always evaluate as true Waddress.c:66:13: warning: the address of a variable will always evaluate as true Waddress.c:67:13: warning: the address of a variable will always evaluate as true Waddress.c:68:13: warning: the address of a variable will always evaluate as true Waddress.c:69:13: warning: the address of a label will always evaluate as true Waddress.c:70:13: warning: the address of a label will always evaluate as true * check-error-end */ sparse-0.6.4/validation/Wcast-to-as.c000066400000000000000000000013041411531012200174020ustar00rootroot00000000000000#define __user __attribute__((address_space(1))) typedef __UINTPTR_TYPE__ uintptr_t; typedef unsigned long ulong; typedef struct s obj_t; static void expl(ulong u, uintptr_t uip, void *v, obj_t *o, obj_t __user *p) { (obj_t*)(u); (obj_t __user*)(u); (obj_t*)(uip); (obj_t __user*)(uip); (obj_t*)(v); (obj_t __user*)(v); (ulong)(o); (void *)(o); (obj_t*)(o); (obj_t __user*)(o); (ulong)(p); (obj_t __user*)(p); } /* * check-name: cast-to-as * check-command: sparse -Wcast-to-as $file * * check-error-start Wcast-to-as.c:16:10: warning: cast adds address space '' to expression Wcast-to-as.c:21:10: warning: cast adds address space '' to expression * check-error-end */ sparse-0.6.4/validation/Woverride-init-def.c000066400000000000000000000004561411531012200207520ustar00rootroot00000000000000static 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.6.4/validation/Woverride-init-no.c000066400000000000000000000003151411531012200206220ustar00rootroot00000000000000static 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.6.4/validation/Woverride-init-yes.c000066400000000000000000000004761411531012200210160ustar00rootroot00000000000000static 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.6.4/validation/Wuniv-init-ko.c000066400000000000000000000012151411531012200177610ustar00rootroot00000000000000struct s { void *ptr; }; static struct s s = { 0 }; static int a = { 0 }; static int b = { }; static int c = { 1, 2 }; static struct s *ptr = { 0 }; struct o { struct i { int a; }; }; static struct o o = { 0 }; /* * check-name: univ-init-ko * check-command: sparse -Wuniversal-initializer $file * * check-error-start Wuniv-init-ko.c:6:23: warning: Using plain integer as NULL pointer Wuniv-init-ko.c:8:16: error: invalid initializer Wuniv-init-ko.c:9:16: error: invalid initializer Wuniv-init-ko.c:10:26: warning: Using plain integer as NULL pointer Wuniv-init-ko.c:18:23: warning: missing braces around initializer * check-error-end */ sparse-0.6.4/validation/Wuniv-init-ok.c000066400000000000000000000010131411531012200177550ustar00rootroot00000000000000struct s { void *ptr; }; static struct s s = { 0 }; static int a = { 0 }; static int b = { }; static int c = { 1, 2 }; static struct s *ptr = { 0 }; struct o { struct i { int a; }; }; static struct o o = { 0 }; /* * check-name: univ-init-ok * check-command: sparse -Wno-universal-initializer $file * * check-error-start Wuniv-init-ok.c:8:16: error: invalid initializer Wuniv-init-ok.c:9:16: error: invalid initializer Wuniv-init-ok.c:10:26: warning: Using plain integer as NULL pointer * check-error-end */ sparse-0.6.4/validation/Wunknown-attribute-def.c000066400000000000000000000001471411531012200216670ustar00rootroot00000000000000static int foo(void) __attribute__((unknown_attribute)); /* * check-name: warn-unknown-attribute */ sparse-0.6.4/validation/Wunknown-attribute-no.c000066400000000000000000000003131411531012200215400ustar00rootroot00000000000000static 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.6.4/validation/Wunknown-attribute-yes.c000066400000000000000000000004271411531012200217320ustar00rootroot00000000000000static 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: unknown attribute 'unknown_attribute' * check-error-end */ sparse-0.6.4/validation/__func__-scope.c000066400000000000000000000002131411531012200201340ustar00rootroot00000000000000static void foo(void) { const char *name = ({ __func__; }); } /* * check-name: __func__'s scope * check-command: sparse -Wall $file */ sparse-0.6.4/validation/__func__.c000066400000000000000000000005051411531012200170310ustar00rootroot00000000000000static 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.6.4/validation/abi-integer.c000066400000000000000000000010371411531012200174710ustar00rootroot00000000000000#define TEST(T, S, A) \ _Static_assert(sizeof(T) == S && _Alignof(T) == A, #T) int main(void) { TEST(int, 4, 4); #if defined(__LP64__) TEST(long, 8, 8); TEST(void *, 8, 8); TEST(long long, 8, 8); #elif defined(__LLP64__) TEST(long, 4, 4); TEST(void *, 8, 8); TEST(long long, 8, 8); #elif defined(__x86_64__) TEST(long, 4, 4); TEST(void *, 4, 4); TEST(long long, 8, 8); #else TEST(long, 4, 4); TEST(void *, 4, 4); TEST(long long, 8, 4); #endif return 0; } /* * check-name: abi-integer */ sparse-0.6.4/validation/abstract-array-declarator-quals.c000066400000000000000000000010721411531012200234620ustar00rootroot00000000000000#define N 2 void ok1(int []); void ok2(int [N]); void ok3(int [const volatile restrict]); void ok4(int [const volatile restrict N]); void ok5(int [static N]); void ok6(int [static const volatile restrict N]); void ok7(int [const volatile restrict static N]); void ok1(int a[]); void ok2(int a[N]); void ok3(int a[const volatile restrict]); void ok4(int a[const volatile restrict N]); void ok5(int a[static N]); void ok6(int a[static const volatile restrict N]); void ok7(int a[const volatile restrict static N]); /* * check-name: abstract-array-declarator-quals */ sparse-0.6.4/validation/abstract-array-declarator-star.c000066400000000000000000000002121411531012200233010ustar00rootroot00000000000000void ok8(int [*]); void ok8(int a[*]); void ok9(int a[const volatile restrict *]); /* * check-name: abstract-array-declarator-star */ sparse-0.6.4/validation/abstract-array-declarator-static.c000066400000000000000000000010261411531012200236230ustar00rootroot00000000000000 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.6.4/validation/abstract-array-declarator.c000066400000000000000000000004141411531012200223360ustar00rootroot00000000000000void f77(int a[1, 2]); void c99(int a[(1, 2)]); /* * check-name: abstract-array-declarator * * check-error-start abstract-array-declarator.c:1:17: error: Expected ] in abstract_array_declarator abstract-array-declarator.c:1:17: error: got , * check-error-end */ sparse-0.6.4/validation/address_space.c000066400000000000000000000006751411531012200201120ustar00rootroot00000000000000#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.6.4/validation/alloc-align.c000066400000000000000000000024301411531012200174630ustar00rootroot00000000000000typedef 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.6.4/validation/alternate-keywords.c000066400000000000000000000032171411531012200211310ustar00rootroot00000000000000 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.6.4/validation/anon-union.c000066400000000000000000000002401411531012200173570ustar00rootroot00000000000000struct s { union { int val; }; }; static struct s foo = { .val = 5, }; /* * check-name: test anonymous union initializer */ sparse-0.6.4/validation/arch/000077500000000000000000000000001411531012200160535ustar00rootroot00000000000000sparse-0.6.4/validation/arch/arm-predef-float-abi-hard.c000066400000000000000000000003031411531012200230050ustar00rootroot00000000000000#if !defined(__ARM_PCS_VFP) || defined(__SOFTFP__) || defined(__ARM_PCS) #error #endif /* * check-name: arm-predef-float-abi-hard * check-command: sparse --arch=arm -mfloat-abi=hard $file */ sparse-0.6.4/validation/arch/arm-predef-float-abi-mixed.c000066400000000000000000000003061411531012200232000ustar00rootroot00000000000000#if defined(__ARM_PCS_VFP) || defined(__SOFTFP__) || !defined(__ARM_PCS) #error #endif /* * check-name: arm-predef-float-abi-mixed * check-command: sparse --arch=arm -mfloat-abi=softfp $file */ sparse-0.6.4/validation/arch/arm-predef-float-abi-soft.c000066400000000000000000000003041411531012200230430ustar00rootroot00000000000000#if defined(__ARM_PCS_VFP) || !defined(__SOFTFP__) || !defined(__ARM_PCS) #error #endif /* * check-name: arm-predef-float-abi-soft * check-command: sparse --arch=arm -mfloat-abi=soft $file */ sparse-0.6.4/validation/arch/arm-predef-hard-float.c000066400000000000000000000002731411531012200222620ustar00rootroot00000000000000#if !defined(__ARM_PCS_VFP) || defined(__SOFTFP__) || defined(__ARM_PCS) #error #endif /* * check-name: arm-predef-hard-float * check-command: sparse --arch=arm -mhard-float $file */ sparse-0.6.4/validation/arch/arm-predef-soft-float.c000066400000000000000000000002741411531012200223200ustar00rootroot00000000000000#if defined(__ARM_PCS_VFP) || !defined(__SOFTFP__) || !defined(__ARM_PCS) #error #endif /* * check-name: arm-predef-soft-float * check-command: sparse --arch=arm -msoft-float $file */ sparse-0.6.4/validation/arch/arm.c000066400000000000000000000004511411531012200167760ustar00rootroot00000000000000__arm__ __aarch64__ __i386__ __x86_64__ __LP64__ __BYTE_ORDER__ __SIZEOF_INT__ __SIZEOF_LONG__ __SIZE_TYPE__ /* * check-name: arch/arm * check-command: sparse --arch=arm -E $file * * check-output-start 1 __aarch64__ __i386__ __x86_64__ __LP64__ 1234 4 4 unsigned int * check-output-end */ sparse-0.6.4/validation/arch/arm64.c000066400000000000000000000004011411531012200171430ustar00rootroot00000000000000__aarch64__ __x86_64__ __LP64__ __BYTE_ORDER__ __SIZEOF_INT__ __SIZEOF_LONG__ __SIZE_TYPE__ /* * check-name: arch/arm64 * check-command: sparse --arch=arm64 -E $file * * check-output-start 1 __x86_64__ 1 1234 4 8 unsigned long * check-output-end */ sparse-0.6.4/validation/arch/mips32.c000066400000000000000000000004701411531012200173350ustar00rootroot00000000000000__mips__ __mips __mips64__ __i386__ __x86_64__ __LP64__ __BYTE_ORDER__ __SIZEOF_INT__ __SIZEOF_LONG__ __SIZE_TYPE__ /* * check-name: arch/mips32 * check-command: sparse --arch=mips32 -E $file * * check-output-start 1 32 __mips64__ __i386__ __x86_64__ __LP64__ 4321 4 4 unsigned int * check-output-end */ sparse-0.6.4/validation/arch/riscv64.c000066400000000000000000000004431411531012200175200ustar00rootroot00000000000000__riscv __riscv_xlen __i386__ __x86_64__ __LP64__ __BYTE_ORDER__ __SIZEOF_INT__ __SIZEOF_LONG__ __SIZE_TYPE__ /* * check-name: arch/riscv64 * check-command: sparse --arch=riscv64 -E $file * * check-output-start 1 64 __i386__ __x86_64__ 1 1234 4 8 unsigned long * check-output-end */ sparse-0.6.4/validation/arch/short-wchar.c000066400000000000000000000002251411531012200204570ustar00rootroot00000000000000_Static_assert([__WCHAR_TYPE__] == [unsigned short], "short wchar"); /* * check-name: short-wchar * check-command: sparse -fshort-wchar $file */ sparse-0.6.4/validation/array-implicit-size.c000066400000000000000000000007331411531012200212030ustar00rootroot00000000000000static int array[] = { 0, 1, 2, 3, }; _Static_assert(sizeof(array) == 4 * sizeof(int), "size of array"); typedef int table_t[]; static table_t tbl2 = { 0, 1, }; _Static_assert(sizeof(tbl2) == 2 * sizeof(int), "size of tbl2"); static table_t tbl1 = { 0, }; _Static_assert(sizeof(tbl1) == 1 * sizeof(int), "size of tbl1"); static table_t tbl3 = { 0, 1, 2, }; _Static_assert(sizeof(tbl3) == 3 * sizeof(int), "size of tbl3"); /* * check-name: array-implicit-size */ sparse-0.6.4/validation/as-name.c000066400000000000000000000006001411531012200166170ustar00rootroot00000000000000#define __user __attribute__((address_space(__user))) extern void fun(void *addr); static void foo(void __user *ptr) { return fun(ptr); } /* * check-name: as-name attribute * * check-error-start as-name.c:7:20: warning: incorrect type in argument 1 (different address spaces) as-name.c:7:20: expected void *addr as-name.c:7:20: got void __user *ptr * check-error-end */ sparse-0.6.4/validation/asm-bad0.c000066400000000000000000000015551411531012200166740ustar00rootroot00000000000000extern char string[]; extern int *var; static void templ(void) { asm(string); } static void ocons(void) { asm("template" : [out] string (var) : [in] "r" (0)); } static void icons(void) { asm("template" : [out] "=r" (var): [in] string (0)); } static void oexpr(void) { asm("template" : [out] "=" (var[) : [in] "r" (0)); } static void iexpr(void) { asm("template" : [out] "=r" (var) : [in] "r" (var[)); } /* * check-name: asm-bad0 * * check-error-start asm-bad0.c:6:13: error: string literal expected for inline asm asm-bad0.c:11:32: error: string literal expected for asm constraint asm-bad0.c:16:49: error: string literal expected for asm constraint asm-bad0.c:21:41: error: Expected ] at end of array dereference asm-bad0.c:21:41: error: got ) asm-bad0.c:26:59: error: Expected ] at end of array dereference asm-bad0.c:26:59: error: got ) * check-error-end */ sparse-0.6.4/validation/asm-empty-clobber.c000066400000000000000000000015701411531012200206270ustar00rootroot00000000000000 # 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.6.4/validation/asm-goto-labels.c000066400000000000000000000012251411531012200202700ustar00rootroot00000000000000static 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.6.4/validation/asm-inline.c000066400000000000000000000027641411531012200173470ustar00rootroot00000000000000static void foo(void) { asm(""); asm volatile ("v"); asm inline ("i"); asm volatile inline ("vi"); asm inline volatile ("iv"); asm goto ("g" :::: label); asm volatile goto ("vg" :::: label); asm inline goto ("ig" :::: label); asm volatile inline goto ("vig" :::: label); asm inline volatile goto ("ivg" :::: label); asm goto volatile ("gv" :::: label); asm goto inline ("gi" :::: label); asm goto volatile inline ("gvi" :::: label); asm goto inline volatile ("giv" :::: label); asm volatile goto inline ("vgi" :::: label); asm inline goto volatile ("giv" :::: label); // warn on duplicates asm volatile volatile ("vv"); asm inline inline ("ii"); asm goto goto ("gg" :::: label); asm inline volatile inline ("ivi"); asm inline goto inline ("igi" :::: label); asm goto inline goto ("gig" :::: label); asm goto volatile goto ("gvg" :::: label); asm volatile inline volatile ("viv"); asm volatile goto volatile ("vgv" :::: label); label: ; } /* * check-name: asm-inline * * check-error-start asm-inline.c:23:22: warning: duplicated asm modifier asm-inline.c:24:20: warning: duplicated asm modifier asm-inline.c:25:18: warning: duplicated asm modifier asm-inline.c:27:29: warning: duplicated asm modifier asm-inline.c:28:25: warning: duplicated asm modifier asm-inline.c:29:25: warning: duplicated asm modifier asm-inline.c:30:27: warning: duplicated asm modifier asm-inline.c:31:29: warning: duplicated asm modifier asm-inline.c:32:27: warning: duplicated asm modifier * check-error-end */ sparse-0.6.4/validation/attr-context.c000066400000000000000000000032121411531012200177340ustar00rootroot00000000000000static void a(void) __attribute__((context)); // KO static void b(void) __attribute__((context())); // KO static void c(void) __attribute__((context 1)); // KO static void d(void) __attribute__((context 1,2)); // KO static void e(void) __attribute__((context (1))); // !!!! static void f(void) __attribute__((context(0))); // !!!! static void g(void) __attribute__((context(0,1,2,3))); // KO static void h(void) __attribute__((context (1,2))); // OK static void i(void) __attribute__((context(0,1))); // OK static void j(void) __attribute__((context(0,1,2))); // OK extern int u, v; static void x(void) __attribute__((context(0,1,v))); static void y(void) __attribute__((context(0,u,1))); static void z(void) __attribute__((context(0,u))); /* * check-name: attr-context * * check-error-start attr-context.c:1:43: error: Expected ( after context attribute attr-context.c:1:43: error: got ) attr-context.c:2:44: error: Expected , after context 1st argument attr-context.c:2:44: error: got ) attr-context.c:3:44: error: Expected ( after context attribute attr-context.c:3:44: error: got 1 attr-context.c:4:44: error: Expected ( after context attribute attr-context.c:4:44: error: got 1 attr-context.c:5:46: error: Expected , after context 1st argument attr-context.c:5:46: error: got ) attr-context.c:6:45: error: Expected , after context 1st argument attr-context.c:6:45: error: got ) attr-context.c:7:49: error: Expected ) after context 3rd argument attr-context.c:7:49: error: got , attr-context.c:14:48: error: bad constant expression attr-context.c:15:46: error: bad constant expression attr-context.c:16:46: error: bad constant expression * check-error-end */ sparse-0.6.4/validation/attr-inline.c000066400000000000000000000005741411531012200175360ustar00rootroot00000000000000 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.6.4/validation/attr-no_sanitize_address.c000066400000000000000000000002511411531012200222770ustar00rootroot00000000000000#define __no_sanitize_address __attribute__((no_sanitize_address)) static void __no_sanitize_address bar(void) { } /* * check-name: attribute no_sanitize_address */ sparse-0.6.4/validation/attr-noclone.c000066400000000000000000000001721411531012200177070ustar00rootroot00000000000000#define noclone __attribute__((__noclone__)) static void noclone bar(void) { } /* * check-name: attribute noclone */ sparse-0.6.4/validation/attr-optimize.c000066400000000000000000000004021411531012200201060ustar00rootroot00000000000000 #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.6.4/validation/attr-visible.c000066400000000000000000000002631411531012200177100ustar00rootroot00000000000000#define __visible __attribute__((externally_visible)) __visible void foo(void) { } int flag __visible; /* * check-name: attr-visible * check-command: sparse -Wdecl $file */ sparse-0.6.4/validation/attr-visible2.c000066400000000000000000000002611411531012200177700ustar00rootroot00000000000000#define __visible __attribute__((externally_visible)) int flag __visible; int arr[2] __visible; /* * check-name: attr-visible-after * check-command: sparse -Wdecl $file */ sparse-0.6.4/validation/attr-warning.c000066400000000000000000000002621411531012200177170ustar00rootroot00000000000000# define __warndecl(name, msg) \ extern void name (void) __attribute__((__warning__ (msg))) __warndecl (__warn_func, "warn message"); /* * check-name: attribute warning */ sparse-0.6.4/validation/attr_aligned.c000066400000000000000000000002421411531012200177350ustar00rootroot00000000000000void *foo(void) __attribute__((__assume_aligned__(4096))); void *foo(void) __attribute__((assume_aligned(4096))); /* * check-name: attribute assume_aligned */ sparse-0.6.4/validation/attr_in_parameter.c000066400000000000000000000003551411531012200210050ustar00rootroot00000000000000#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.6.4/validation/attr_vector_size.c000066400000000000000000000001771411531012200206750ustar00rootroot00000000000000typedef unsigned int u32; typedef u32 __attribute__((vector_size(16))) sse128_t; /* * check-name: attribute vector_size */ sparse-0.6.4/validation/autotype-ko.c000066400000000000000000000032311411531012200175620ustar00rootroot00000000000000__auto_type u; // KO: no initializer __auto_type r[2] = { 0, 1 }; // KO: not a plain identifier __auto_type foo(void) { } // KO: not a plain identifier __auto_type v = 0, w = 1; // KO: in list struct { __auto_type x; } s; // KO: not valid for struct/union __auto_type self = self; // KO: self-declared __auto_type undc = this; // KO: undeclared int i = 1; double f = 1.0; __auto_type i = 2; // KO: redecl, same type __auto_type f = 2.0f; // KO: redecl, diff type static int foo(int a, const int *ptr) { __auto_type i = a; __auto_type c = *ptr; c += 1; return i; } /* * check-name: autotype-ko * check-command: sparse -Wno-decl $file * * check-error-start autotype-ko.c:1:13: error: __auto_type without initializer autotype-ko.c:2:13: error: __auto_type on non-identifier autotype-ko.c:3:13: error: 'foo()' has __auto_type return type autotype-ko.c:4:13: error: __auto_type on declaration list autotype-ko.c:6:13: error: __auto_type on self-init var autotype-ko.c:2:20: error: invalid initializer autotype-ko.c:5:22: error: member 'x' has __auto_type autotype-ko.c:7:20: error: undefined identifier 'this' autotype-ko.c:11:13: error: symbol 'i' has multiple initializers (originally initialized at autotype-ko.c:9) autotype-ko.c:12:13: error: symbol 'f' has multiple initializers (originally initialized at autotype-ko.c:10) autotype-ko.c:12:13: error: symbol 'f' redeclared with different type (different type sizes): autotype-ko.c:12:13: float [addressable] [toplevel] f autotype-ko.c:10:8: note: previously declared as: autotype-ko.c:10:8: double [addressable] [toplevel] f autotype-ko.c:20:9: error: assignment to const expression * check-error-end */ sparse-0.6.4/validation/autotype.c000066400000000000000000000024431411531012200171570ustar00rootroot00000000000000#ifdef __CHECKER__ #define is_type(X, T) _Static_assert([typeof(X)] == [T], "") #else #define is_type(X, T) _Static_assert(1, "") #endif struct s { int x; int bf:3; }; extern char ch; extern const int ci; __auto_type i = 0; is_type(i, int); __auto_type m = 1UL; is_type(m, unsigned long); __auto_type l = (int)0L; is_type(l, int); __auto_type c = (char)'\n'; is_type(c, char); __auto_type p = &i; is_type(p, int *); __auto_type f = 0.0; is_type(f, double); __auto_type s = (struct s){0}; is_type(s, struct s); __auto_type pci = &ci; is_type(pci, const int *); // ~~: not valid for bitfield __auto_type b = (struct s){0}.bf; is_type(b, int); static __auto_type si = 0; is_type(si, int); const __auto_type ci = 0; is_type(ci, const int); __auto_type ch = (char) '\n'; is_type(ch, char); static int foo(int a) { __auto_type i = a; is_type(i, int); __auto_type c = ch; is_type(c, char); __auto_type ct = ci; is_type(&ct, const int *); return ct += i + c; } #define __as __attribute__((address_space(42))) extern int __as aa; __auto_type pa = &aa; is_type(pa, int __as *); /* * check-name: autotype * check-command: sparse -Wno-decl $file * * check-error-start autotype.c:25:13: warning: __auto_type on bitfield autotype.c:37:16: error: assignment to const expression * check-error-end */ sparse-0.6.4/validation/backend/000077500000000000000000000000001411531012200165255ustar00rootroot00000000000000sparse-0.6.4/validation/backend/arithmetic-ops.c000066400000000000000000000025431411531012200216250ustar00rootroot00000000000000static 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; } static int neg(int x) { return -x; } static unsigned int uneg(unsigned int x) { return -x; } static float fneg(float x) { return -x; } static double dneg(double x) { return -x; } /* * check-name: Arithmetic operator code generation * check-command: sparsec -c $file -o tmp.o */ sparse-0.6.4/validation/backend/array.c000066400000000000000000000001611411531012200200050ustar00rootroot00000000000000static char array[128]; /* * check-name: Array code generation * check-command: sparsec -c $file -o tmp.o */ sparse-0.6.4/validation/backend/bitwise-ops.c000066400000000000000000000014741411531012200211440ustar00rootroot00000000000000static 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.6.4/validation/backend/bool-test.c000066400000000000000000000002171411531012200206010ustar00rootroot00000000000000static _Bool return_false(void) { return 0; } /* * check-name: Boolean type code generation * check-command: sparsec -c $file -o tmp.o */ sparse-0.6.4/validation/backend/call-variadic.c000066400000000000000000000011731411531012200213660ustar00rootroot00000000000000#define NULL ((void*)0) extern int print(const char *msg, ...); int foo(const char *fmt, int a, long l, int *p); int foo(const char *fmt, int a, long l, int *p) { return print(fmt, 'x', a, __LINE__, l, 0L, p, NULL); } /* * check-name: call-variadic * check-command: sparse-llvm-dis -m64 $file * * check-output-start ; ModuleID = '' source_filename = "sparse" define i32 @foo(i8* %ARG1., i32 %ARG2., i64 %ARG3., i32* %ARG4.) { L0: %R5. = call i32 (i8*, ...) @print(i8* %ARG1., i32 120, i32 %ARG2., i32 8, i64 %ARG3., i64 0, i32* %ARG4., i8* null) ret i32 %R5. } declare i32 @print(i8*, ...) * check-output-end */ sparse-0.6.4/validation/backend/cast.c000066400000000000000000000021601411531012200176220ustar00rootroot00000000000000typedef _Bool bool; typedef signed char schar; 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, schar) \ 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(schar) 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.6.4/validation/backend/cmp-ops.c000066400000000000000000000017761411531012200202620ustar00rootroot00000000000000static 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.6.4/validation/backend/compare-with-null.c000066400000000000000000000005101411531012200222340ustar00rootroot00000000000000int tstv(void *p) { return !p; } int cmpv(void *p) { return p == ((void*)0); } int tsti(int *p) { return !p; } int cmpi(int *p) { return p == ((int *)0); } int cmpx(int *p) { return p == ((void*)0); } /* * check-name: compare-with-null * check-command: sparsec -Wno-decl -c $file -o tmp.o * check-output-ignore */ sparse-0.6.4/validation/backend/constant-pointer.c000066400000000000000000000006561411531012200222070ustar00rootroot00000000000000extern int *ip[]; void foo(void); void foo(void) { ip[0] = (void *)0L; ip[1] = (int *)0L; ip[2] = (void *)0; ip[3] = (int *)0; ip[4] = (void *)(long)0; ip[5] = (int *)(long)0; ip[6] = (void *)123; ip[7] = (int *)123; ip[8] = (void *)123L; ip[9] = (int *)123L; ip[10] = (void *)(long)123; ip[11] = (int *)(long)123; } /* * check-name: constant pointers * check-command: sparse-llvm $file * check-output-ignore */ sparse-0.6.4/validation/backend/degenerate-ptr.c000066400000000000000000000015421411531012200216010ustar00rootroot00000000000000extern int array[3]; extern int matrix[3][3]; extern int fun(int); extern int fia(int []); extern int fip(int *); extern int fim(int (*)[3]); extern int fvp(void *); extern int ffp(int (*)(int)); void call(void); void call(void) { fia(array); fip(array); fim(matrix); fvp(array); fvp(matrix); fvp(fun); fvp(&fun); ffp(fun); ffp(&fun); } void local(void); void local(void) { int *ip; int (*im)[3]; void *vp; int (*fp)(int); ip = array; im = matrix; vp = array; vp = matrix; vp = fun; vp = &fun; fp = fun; fp = &fun; } extern int *ip; extern int (*im)[3]; extern void *vp; extern int (*fp)(int); void global(void); void global(void) { ip = array; im = matrix; vp = array; vp = matrix; vp = fun; vp = &fun; fp = fun; fp = &fun; } /* * check-name: degenerated pointer handling * check-command: sparsec -c $file -o tmp.o */ sparse-0.6.4/validation/backend/extern.c000066400000000000000000000002541411531012200201770ustar00rootroot00000000000000extern 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.6.4/validation/backend/fn-ref.c000066400000000000000000000006021411531012200200440ustar00rootroot00000000000000extern int fun0(int a); extern int fun1(int a); int foo(int a); int foo(int a) { int v = fun0(a); return v; } void *bar(int a) { return fun1; } int fun0(int a) { return a + 0; } int fun1(int a) { return a + 1; } /* * check-name: llvm function reference * check-command: sparse-llvm-dis -Wno-decl $file * * check-output-ignore * check-output-excludes: fun[0-9]\.[1-9] */ sparse-0.6.4/validation/backend/function-ptr-xtype.c000066400000000000000000000014631411531012200224740ustar00rootroot00000000000000typedef int (*binop_t)(int, int); typedef int (*unop_t)(int); typedef int (*idef_t)(void); typedef long (*ldef_t)(void); typedef void (*use_t)(int); // We want to 'fn' have several different types. // The goal is for the ->priv member to be used // with a type different from what it was first stored. int foo(void *fn, int arg1, int arg2); int foo(void *fn, int arg1, int arg2) { int res = 0; res += ((binop_t)fn)(arg1, arg2); res += ((unop_t)fn)(arg1); res += ((ldef_t)fn)(); res += ((idef_t)fn)(); ((use_t)fn)(res); return res; } int bar(int (*fn)(int), int arg1, int arg2); int bar(int (*fn)(int), int arg1, int arg2) { int res = 0; res += ((binop_t)fn)(arg1, arg2); res += fn(arg1); return res; } /* * check-name: mutate function pointer's type * check-command: sparsec -c $file -o tmp.o */ sparse-0.6.4/validation/backend/function-ptr.c000066400000000000000000000046631411531012200213320ustar00rootroot00000000000000extern int ival; extern int *ipval; extern int array[3]; extern int matrix[3][3]; extern int fun(int); // via an argument void arg(int a, int *p, int (*fb)(unsigned char), int (*fi)(int), int (*fl)(long), int (*fv)(void), int (*fip)(int *), int (*fim)(int (*)[3]), int (*fvp)(void *), int (*ffp)(int (*)(int))); void arg(int a, int *p, int (*fb)(unsigned char), int (*fi)(int), int (*fl)(long), int (*fv)(void), int (*fip)(int *), int (*fim)(int (*)[3]), int (*fvp)(void *), int (*ffp)(int (*)(int))) { fv(); fb(a); fi(a); fl(a); fb(123); fi(123); fl(123); fb(123L); fi(123L); fl(123L); fb(ival); fi(ival); fl(ival); fip(p); fip((void*)0); fip(ipval); fip(&ival); fip(array); fim(matrix); fvp(p); fvp((void*)0); fvp(ipval); fvp(&ival); fvp(array); fvp(matrix); fvp(fun); fvp(&fun); ffp(fun); ffp(&fun); } // a global extern int (*fb)(unsigned char); extern int (*fi)(int); extern int (*fl)(long); extern int (*fv)(void); extern int (*fip)(int *); extern int (*fim)(int (*)[3]); extern int (*fvp)(void *); extern int (*ffp)(int (*)(int)); void glb(int a, int *p); void glb(int a, int *p) { fv(); fb(a); fi(a); fl(a); fb(123); fi(123); fl(123); fb(123L); fi(123L); fl(123L); fb(ival); fi(ival); fl(ival); fip(p); fip((void*)0); fip(ipval); fip(&ival); fip(array); fim(matrix); fvp(p); fvp((void*)0); fvp(ipval); fvp(&ival); fvp(array); fvp(matrix); fvp(fun); fvp(&fun); ffp(fun); ffp(&fun); } // via a struct member: // -> force to create a register containing the function pointer struct ops { int (*fb)(unsigned char); int (*fi)(int); int (*fl)(long); int (*fv)(void); int (*fip)(int *); int (*fim)(int (*)[3]); int (*fvp)(void *); int (*ffp)(int (*)(int)); int (*const cfi)(int); // for the fun of it }; void ops(int a, int *p, struct ops *ops); void ops(int a, int *p, struct ops *ops) { ops->fv(); ops->fb(a); ops->fi(a); ops->fl(a); ops->fb(123); ops->fi(123); ops->fl(123); ops->fb(123L); ops->fi(123L); ops->fl(123L); ops->fb(ival); ops->fi(ival); ops->fl(ival); ops->fip(p); ops->fip((void*)0); ops->fip(ipval); ops->fip(&ival); ops->fip(array); ops->fim(matrix); ops->fvp(p); ops->fvp((void*)0); ops->fvp(ipval); ops->fvp(&ival); ops->fvp(array); ops->fvp(matrix); ops->fvp(fun); ops->fvp(&fun); ops->ffp(fun); ops->ffp(&fun); ops->fvp(fi); ops->cfi(42); } /* * check-name: Function pointer code generation * check-command: sparsec -c $file -o tmp.o */ sparse-0.6.4/validation/backend/hello.c000066400000000000000000000003041411531012200177710ustar00rootroot00000000000000int puts(const char *s); 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.6.4/validation/backend/int-cond.c000066400000000000000000000006411411531012200204050ustar00rootroot00000000000000static 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.6.4/validation/backend/label-as-value.c000066400000000000000000000002561411531012200214660ustar00rootroot00000000000000void *foo(void *def); void *foo(void *def) { if (!def) yes: return &&yes; return def; } /* * check-name: label-as-value * check-command: sparsec -c $file -o tmp.o */ sparse-0.6.4/validation/backend/load-global.c000066400000000000000000000006231411531012200210470ustar00rootroot00000000000000const char *s = "abc"; int x = 4; int y; int *p = &x; int *q; int loadn(void) { return y; } int loadi(void) { return x; } const char *loads(void) { return s; } int *retpn(void) { return q; } int loadpn(void) { return *q; } int *retpi(void) { return p; } int loadpi(void) { return *p; } /* * check-name: use simple value from global vars * check-command: sparsec -Wno-decl -c $file -o tmp.o */ sparse-0.6.4/validation/backend/load-type.c000066400000000000000000000003221411531012200205640ustar00rootroot00000000000000extern 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.6.4/validation/backend/logical-ops.c000066400000000000000000000005651411531012200211100ustar00rootroot00000000000000static 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.6.4/validation/backend/loop.c000066400000000000000000000003321411531012200176400ustar00rootroot00000000000000 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.6.4/validation/backend/loop2.c000066400000000000000000000002671411531012200177310ustar00rootroot00000000000000extern 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.6.4/validation/backend/pointer-add.c000066400000000000000000000042521411531012200211020ustar00rootroot00000000000000char *caddv(char *p, int o) { char *r = p; r = r + o; return r; } void *vaddv(void *p, int o) { void *r = p; r = r + o; return r; } int *iaddv(int *p, int o) { int *r = p; r = r + o; return r; } char *caddc(char *p, int o) { char *r = p; r = r + 3; return r; } void *vaddc(void *p, int o) { void *r = p; r = r + 3; return r; } int *iaddc(int *p, int o) { int *r = p; r = r + 3; return r; } char *cincv(char *p, int o) { char *r = p; r += o; return r; } void *vincv(void *p, int o) { void *r = p; r += o; return r; } int *iincv(int *p, int o) { int *r = p; r += o; return r; } char *cincc(char *p, int o) { char *r = p; r += 3; return r; } void *vincc(void *p, int o) { void *r = p; r += 3; return r; } int *iincc(int *p, int o) { int *r = p; r += 3; return r; } char *ciniaddv(char *p, int o) { char *r = p + o; return r; } void *viniaddv(void *p, int o) { void *r = p + o; return r; } int *iiniaddv(int *p, int o) { int *r = p + o; return r; } char *ciniaddc(char *p, int o) { char *r = p + 3; return r; } void *viniaddc(void *p, int o) { void *r = p + 3; return r; } int *iiniaddc(int *p, int o) { int *r = p + 3; return r; } char *ciniincv(char *p, int o) { char *r = p += o; return r; } void *viniincv(void *p, int o) { void *r = p += o; return r; } int *iiniincv(int *p, int o) { int *r = p += o; return r; } char *ciniincc(char *p, int o) { char *r = p += 3; return r; } void *viniincc(void *p, int o) { void *r = p += 3; return r; } int *iiniincc(int *p, int o) { int *r = p += 3; return r; } char *cretaddv(char *p, int o) { return p + o; } void *vretaddv(void *p, int o) { return p + o; } int *iretaddv(int *p, int o) { return p + o; } char *cretaddc(char *p, int o) { return p + 3; } void *vretaddc(void *p, int o) { return p + 3; } int *iretaddc(int *p, int o) { return p + 3; } char *cretincv(char *p, int o) { return p += o; } void *vretincv(void *p, int o) { return p += o; } int *iretincv(int *p, int o) { return p += o; } char *cretincc(char *p, int o) { return p += 3; } void *vretincc(void *p, int o) { return p += 3; } int *iretincc(int *p, int o) { return p += 3; } /* * check-name: pointer-add * check-command: sparsec -Wno-decl -c $file -o r.o */ sparse-0.6.4/validation/backend/pointer-cmp.c000066400000000000000000000006231411531012200211270ustar00rootroot00000000000000int cmpint( int x, int y) { return x == y; } int cmpflt( float x, float y) { return x == y; } int cmpvptr(void *x, void *y) { return x == y; } int cmpiptr(int *x, int *y) { return x == y; } int cmpmptr(long x, int *y) { return (int*)x == y; } int cmpnptr(int *x, long y) { return x == (int*)y; } /* * check-name: pointer comparison * check-command: sparsec -Wno-decl -c $file -o tmp.o */ sparse-0.6.4/validation/backend/pointer-param.c000066400000000000000000000013001411531012200214410ustar00rootroot00000000000000extern int gfun(int); static int sfun(int a) { return a; } void usei(int *); void usef(int (*)(int)); void usev(void *); void foo(int *p, int a[5], int (*pfun)(int)); void foo(int *p, int a[5], int (*pfun)(int)) { extern int valg[5], valh[5], vali[5]; static int vals[5], valt[5], valr[5]; int vala[5], valb[5], valc[5]; usei(p); usei(valg); usei(&valh[0]); usei(&vali[1]); usei(vals); usei(&valt[0]); usei(&valr[1]); usei(vala); usei(&valb[0]); usei(&valc[1]); usef(pfun); usef(gfun); usef(&gfun); usef(sfun); usef(&sfun); usev(pfun); usev(gfun); usev(&gfun); usev(sfun); usev(&sfun); } /* * check-name: pointer-param * check-command: sparsec -c $file -o tmp.o */ sparse-0.6.4/validation/backend/pointer-sub.c000066400000000000000000000012401411531012200211350ustar00rootroot00000000000000long subv0(void *p, int a) { return p - ((void*)0); } long subvc(void *p, int a) { return p - ((void*)8); } long subva(void *p, int a) { return p - ((void*)a); } long subvq(void *p, void *q) { return p - q; } long subi0(int *p, int a) { return p - ((int *)0); } long subic(int *p, int a) { return p - ((int *)8); } long subia(int *p, int a) { return p - ((int *)a); } long subiq(int *p, int *q) { return p - q; } long subvm3(void *p, int a) { return (p - ((void*)0)) * 3; } long subvx3(void *p, int a) { return (p - ((void*)0)) ^ 3; } /* * check-name: pointer-sub * check-command: sparsec -Wno-int-to-pointer-cast -Wno-decl -c $file -o tmp.o */ sparse-0.6.4/validation/backend/ptrcast.c000066400000000000000000000002501411531012200203460ustar00rootroot00000000000000static 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.6.4/validation/backend/setval.c000066400000000000000000000002531411531012200201670ustar00rootroot00000000000000double setfval64(void) { return 1.23; } float setfval32(void) { return 1.23F; } /* * check-name: setval-float * check-command: sparsec -Wno-decl -c $file -o tmp.o */ sparse-0.6.4/validation/backend/shift-special.c000066400000000000000000000003061411531012200214230ustar00rootroot00000000000000long shift(long a, short b); long shift(long a, short b) { long r1 = a << b; long r2 = b << a; return r1 + r2; } /* * check-name: shift-special * check-command: sparsec -c $file -o tmp.o */ sparse-0.6.4/validation/backend/store-type.c000066400000000000000000000002621411531012200210040ustar00rootroot00000000000000struct 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.6.4/validation/backend/store-x2.c000066400000000000000000000005251411531012200203560ustar00rootroot00000000000000void foo(int *p, int a, int b); void foo(int *p, int a, int b) { int c = a + b; p[0] = c; p[1] = c; } /* * check-name: store-x2 * check-command: sparsec -c $file -o tmp.o * check-description: Verify in output_op_store() that * the first store doesn't mess anymore with the * 'target' and thus making the second store unusable. */ sparse-0.6.4/validation/backend/string-value.c000066400000000000000000000003631411531012200213130ustar00rootroot00000000000000extern void use(const char *); const char *ret(void) { return "abc"; } const char *add(void) { return "def" + 1; } void call(void) { use("ijk"); } /* * check-name: string-value * check-command: sparsec -Wno-decl -c $file -o tmp.o */ sparse-0.6.4/validation/backend/struct-access.c000066400000000000000000000005361411531012200214600ustar00rootroot00000000000000struct 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.6.4/validation/backend/struct.c000066400000000000000000000006001411531012200202110ustar00rootroot00000000000000struct 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.6.4/validation/backend/sum.c000066400000000000000000000005651411531012200175030ustar00rootroot00000000000000int printf(const char * fmt, ...); 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 --no-jit $file * * check-output-start 15 5050 * check-output-end */ sparse-0.6.4/validation/backend/switch.c000066400000000000000000000075531411531012200202040ustar00rootroot00000000000000int def(void); int r0(void); int r1(void); int r2(void); int r3(void); int r4(void); int r5(void); int r6(void); int r7(void); int r8(void); int r9(void); int small(int a) { switch (a) { case 0: return r0(); case 1: return r1(); case 2: return r2(); } return def(); } int densefull(int a) { switch (a) { case 0: return r0(); case 1: return r1(); case 2: return r2(); case 3: return r3(); case 4: return r4(); case 5: return r5(); case 6: return r6(); case 7: return r7(); case 8: return r8(); case 9: return r9(); } return def(); } int densepart(int a) { switch (a) { case 0: return r0(); case 1: return r1(); case 2: return r2(); case 3: return r3(); case 4: return r4(); case 6: return r6(); case 7: return r7(); case 8: return r8(); case 9: return r9(); } return def(); } int dense_dense_20(int a) { switch (a) { case 0: return r0(); case 1: return r1(); case 2: return r2(); case 3: return r3(); case 4: return r4(); case 5: return r5(); case 6: return r6(); case 7: return r7(); case 8: return r8(); case 9: return r9(); case 20: return r0(); case 21: return r1(); case 22: return r2(); case 23: return r3(); case 24: return r4(); case 25: return r5(); case 26: return r6(); case 27: return r7(); case 28: return r8(); case 29: return r9(); } return def(); } int dense_dense_100(int a) { switch (a) { case 0: return r0(); case 1: return r1(); case 2: return r2(); case 3: return r3(); case 4: return r4(); case 5: return r5(); case 6: return r6(); case 7: return r7(); case 8: return r8(); case 9: return r9(); case 100: return r0(); case 101: return r1(); case 102: return r2(); case 103: return r3(); case 104: return r4(); case 105: return r5(); case 106: return r6(); case 107: return r7(); case 108: return r8(); case 109: return r9(); } return def(); } int dense_dense_1000(int a) { switch (a) { case 0: return r0(); case 1: return r1(); case 2: return r2(); case 3: return r3(); case 4: return r4(); case 5: return r5(); case 6: return r6(); case 7: return r7(); case 8: return r8(); case 9: return r9(); case 1000: return r0(); case 1001: return r1(); case 1002: return r2(); case 1003: return r3(); case 1004: return r4(); case 1005: return r5(); case 1006: return r6(); case 1007: return r7(); case 1008: return r8(); case 1009: return r9(); } return def(); } int sparse(int a) { switch (a) { case 0: return r0(); case 3: return r1(); case 12: return r2(); case 31: return r3(); case 54: return r4(); case 75: return r5(); case 96: return r6(); case 107: return r7(); case 189: return r8(); case 999: return r9(); } return def(); } int range_simple(int a) { switch (a) { case 1 ... 9: return r0(); } return def(); } int range_complex(int a) { switch (a) { case -1: return r0(); case 1 ... 9: return r0(); case 10 ... 19: return r1(); case 200 ... 202: return r2(); case 300 ... 303: return r3(); } return def(); } void switch_call(int a) { int r; switch (a) { case 0: r0(); break; case 1: r1(); break; case 2: r2(); break; case 3: r3(); break; case 4: r4(); break; case 5: r5(); break; case 6: r6(); break; case 7: r7(); break; case 8: r8(); break; case 9: r9(); break; } } int switch_retcall(int a) { int r = 0; switch (a) { case 0: r = r0(); break; case 1: r = r1(); break; case 2: r = r2(); break; case 3: r = r3(); break; case 4: r = r4(); break; case 5: r = r5(); break; case 6: r = r6(); break; case 7: r = r7(); break; case 8: r = r8(); break; case 9: r = r9(); break; } return r; } int switch_cmov(int a) { int r; switch (a) { case 0: r = 3; break; case 1: r = 1; break; case 2: r = 7; break; case 3: r = 2; break; case 4: r = 9; break; case 6: r = 5; break; case 7: r = 8; break; case 8: r = 6; break; case 9: r = 4; break; } return r; } /* * check-name: llvm-switch * check-command: sparsec -Wno-decl -c $file -o tmp.o */ sparse-0.6.4/validation/backend/symaddr.c000066400000000000000000000024771411531012200203460ustar00rootroot00000000000000extern void useip(int *); extern void useia(int (*)[3]); extern void usevp(void *); static int sfun(void) { return 0; } static int spun(void) { return 0; } void lfoo(int *p, int a) { int larra[3], larrb[3], larrc[3], larrd[3], larre[3], larrf[3]; useip(p); useip(larra); useip(larrb + 1); useip(larrc + a); useip(&larrd[1]); useip(&larre[a]); useia(&larrf); } static int sarra[3], sarrb[3], sarrc[3], sarrd[3], sarre[3], sarrf[3]; static int s, sfun(void), spun(void); void sfoo(int *p, int a) { useip(p); useip(&s); useip(sarra); useip(sarrb + 1); useip(sarrc + a); useip(&sarrd[1]); useip(&sarre[a]); useia(&sarrf); usevp(sfun); usevp(&spun); } extern int xarra[3], xarrb[3], xarrc[3], xarrd[3], xarre[3], xarrf[3]; extern int x, xfun(void), xpun(void); void xfoo(int *p, int a) { useip(p); useip(&x); useip(xarra); useip(xarrb + 1); useip(xarrc + a); useip(&xarrd[1]); useip(&xarre[a]); useia(&xarrf); usevp(xfun); usevp(&xpun); } int garra[3], garrb[3], garrc[3], garrd[3], garre[3], garrf[3]; int g, gfun(void), gpun(void); void gfoo(int *p, int a) { useip(p); useip(&g); useip(garra); useip(garrb + 1); useip(garrc + a); useip(&garrd[1]); useip(&garre[a]); useia(&garrf); usevp(gfun); usevp(&gpun); } /* * check-name: symbol address * check-command: sparsec -Wno-decl -c $file -o tmp.o */ sparse-0.6.4/validation/backend/type-constant.c000066400000000000000000000012471411531012200215050ustar00rootroot00000000000000char creti(void) { return 3; } int ireti(void) { return 3; } long lreti(void) { return 3; } char cinii(void) { char r = 3; return r; } int iinii(void) { int r = 3; return r; } long linii(void) { long r = 3; return r; } void *vretn(void) { return (void*)0; } char *cretn(void) { return (void*)0; } int *iretn(void) { return (void*)0; } long *lretn(void) { return (void*)0; } void *vinin(void) { void *r = (void*)0; return r; } char *cinin(void) { char *r = (void*)0; return r; } int *iinin(void) { int *r = (void*)0; return r; } long *linin(void) { long *r = (void*)0; return r; } /* * check-name: type-constant * check-command: sparsec -Wno-decl -c $file -o r.o */ sparse-0.6.4/validation/backend/union.c000066400000000000000000000002671411531012200200260ustar00rootroot00000000000000union 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.6.4/validation/backend/void-return-type.c000066400000000000000000000002501411531012200221230ustar00rootroot00000000000000static 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.6.4/validation/bad-array-designated-initializer.c000066400000000000000000000005571411531012200236010ustar00rootroot00000000000000static 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.6.4/validation/bad-assignment.c000066400000000000000000000004351411531012200202000ustar00rootroot00000000000000static int foo(int a) { a |=\1; return a; } /* * check-name: bad assignment * * check-error-start bad-assignment.c:3:11: error: expression expected before '\' bad-assignment.c:3:13: error: Expected ; at end of statement bad-assignment.c:3:13: error: got \ * check-error-end */ sparse-0.6.4/validation/bad-cast.c000066400000000000000000000004261411531012200167620ustar00rootroot00000000000000struct 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.6.4/validation/bad-check-access0.c000066400000000000000000000007371411531012200204310ustar00rootroot00000000000000#define SIZE 2 static int buf[SIZE]; static inline int swt(int i) { switch (i) { case 0 ... (SIZE-1): return buf[i]; default: return 0; } } static int switch_ok(void) { return swt(1); } static int switch_ko(void) { return swt(2); } static inline int cbr(int i, int p) { if (p) return buf[i]; else return 0; } static int branch_ok(int x) { return cbr(1, x != x); } static int branch_ko(int x) { return cbr(2, x != x); } /* * check-name: bad-check-access0 */ sparse-0.6.4/validation/bad-return-type.c000066400000000000000000000004621411531012200203260ustar00rootroot00000000000000void foo(int a) { return a; } int bar(void) { return; } /* * check-name: bad return type * check-command: sparse -Wno-decl $file * * check-error-start bad-return-type.c:3:16: error: return expression in void function bad-return-type.c:8:9: error: return with no return value * check-error-end */ sparse-0.6.4/validation/bad-ternary-cond.c000066400000000000000000000004371411531012200204370ustar00rootroot00000000000000static 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.6.4/validation/bad-type-twice0.c000066400000000000000000000003031411531012200201740ustar00rootroot00000000000000static int foo(a) { return a ? : 1; } /* * check-name: bad-type-twice0 * * check-error-start bad-type-twice0.c:1:16: error: missing type declaration for parameter 'a' * check-error-end */ sparse-0.6.4/validation/bad-type-twice1.c000066400000000000000000000005161411531012200202030ustar00rootroot00000000000000static unsigned long foo(unsigned long val, void *ref) { if (val >= ref) val = 0; return val; } /* * check-name: bad-type-twice1 * * check-error-start bad-type-twice1.c:3:17: error: incompatible types for operation (>=): bad-type-twice1.c:3:17: unsigned long val bad-type-twice1.c:3:17: void *ref * check-error-end */ sparse-0.6.4/validation/bad-type-twice2.c000066400000000000000000000006621411531012200202060ustar00rootroot00000000000000extern type_t fun(int); int foo(int x, int y) { return ((int)fun(y)) + x; } /* * check-name: bad-type-twice2 * * check-error-start bad-type-twice2.c:1:8: warning: 'type_t' has implicit type bad-type-twice2.c:1:15: error: Expected ; at end of declaration bad-type-twice2.c:1:15: error: got fun bad-type-twice2.c:5:22: error: undefined identifier 'fun' bad-type-twice2.c:5:18: error: cast from unknown type * check-error-end */ sparse-0.6.4/validation/bad-typeof.c000066400000000000000000000003251411531012200173340ustar00rootroot00000000000000static 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.6.4/validation/badtype1.c000066400000000000000000000002751411531012200170170ustar00rootroot00000000000000static 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.6.4/validation/badtype2.c000066400000000000000000000013231411531012200170130ustar00rootroot00000000000000//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:6:11: error: missing type declaration for parameter 'p' 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.6.4/validation/badtype3.c000066400000000000000000000016031411531012200170150ustar00rootroot00000000000000int 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:5:10: error: missing type declaration for parameter 'cur' 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.6.4/validation/badtype4.c000066400000000000000000000004051411531012200170150ustar00rootroot00000000000000void 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.6.4/validation/badtype5.c000066400000000000000000000007371411531012200170260ustar00rootroot00000000000000#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.6.4/validation/binary-constant.c000066400000000000000000000001041411531012200204100ustar00rootroot00000000000000extern int x; int x = 0b11; /* * check-name: binary constant */ sparse-0.6.4/validation/bitfield-bool-layout.c000066400000000000000000000010541411531012200213300ustar00rootroot00000000000000struct bfb { _Bool a:1; _Bool f:1; _Bool z:1; }; struct bfb foo(struct bfb s) { return s; } /* * check-name: bitfield-bool-layout * check-description: given that bool is here 1-bit wide * each field here above completely 'fill' a bool. * Thus 3 bools need to be allocated, but since the * alignment is 1-byte the result has a size of 3 * bytes, 24 bits thus instead of 8. * check-command: test-linearize -Wno-decl $file * * check-known-to-fail * check-output-ignore * check-output-excludes: ret\\.24 * check-output-contains: ret\\.8 */ sparse-0.6.4/validation/bitfield-kr.c000066400000000000000000000003311411531012200174730ustar00rootroot00000000000000static int foo(b) int b: 4; { return 0; } /* * check-name: bitfield in K&R * * check-known-to-fail * check-error-start bitfield-kr.c:2:9: error: bitfield in K&R declaration of 'foo' * check-error-end */ sparse-0.6.4/validation/bitfield-sizes.c000066400000000000000000000013041411531012200202150ustar00rootroot00000000000000struct a { int a:31; int b:32; long c:63; long d:64; int x:33; // KO long y:65; // KO }; static struct a a; struct b { int m1:-1; // KO int x1:2147483648; // KO int :0; int a0:0; // KO }; static struct b b; /* * check-name: bitfield-sizes * check-command: sparse -m64 $file * * check-error-start bitfield-sizes.c:12:18: error: bitfield 'm1' has invalid width (-1) bitfield-sizes.c:13:26: error: bitfield 'x1' has invalid width (2147483648) bitfield-sizes.c:15:17: error: bitfield 'a0' has invalid width (0) bitfield-sizes.c:6:15: error: bitfield 'x' is wider (33) than its type (int) bitfield-sizes.c:7:15: error: bitfield 'y' is wider (65) than its type (long) * check-error-end */ sparse-0.6.4/validation/bitfields.c000066400000000000000000000004641411531012200172530ustar00rootroot00000000000000/* * 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.6.4/validation/bitwise-cast-ptr.c000066400000000000000000000016201411531012200205020ustar00rootroot00000000000000#define __bitwise __attribute__((bitwise)) #define __force __attribute__((force)) typedef unsigned int u32; typedef unsigned int __bitwise __be32; static __be32* tobi(u32 *x) { return x; // should warn, implicit cast } static __be32* tobe(u32 *x) { return (__be32 *) x; // should warn, explicit cast } static __be32* tobf(u32 *x) { return (__force __be32 *) x; // should not warn, forced cast return (__be32 __force *) x; // should not warn, forced cast } /* * check-name: cast of bitwise pointers * check-command: sparse -Wbitwise -Wbitwise-pointer $file * * check-error-start bitwise-cast-ptr.c:9:16: warning: incorrect type in return expression (different base types) bitwise-cast-ptr.c:9:16: expected restricted __be32 [usertype] * bitwise-cast-ptr.c:9:16: got unsigned int [usertype] *x bitwise-cast-ptr.c:14:17: warning: cast to restricted __be32 [usertype] * * check-error-end */ sparse-0.6.4/validation/bitwise-cast.c000066400000000000000000000021051411531012200176760ustar00rootroot00000000000000typedef 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; } /* Explicit case of nonzero forced, legal */ static __be32 quuy(void) { return (__attribute__((force)) __be32) 1730; } /* * 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.6.4/validation/bitwise-function-pointer.c000066400000000000000000000007351411531012200222560ustar00rootroot00000000000000#define __bitwise __attribute__((bitwise)) typedef unsigned int __bitwise t; unsigned int fun(void); static t (*ptr)(void) = fun; /* * check-name: bitwise-function-pointer * * check-error-start bitwise-function-pointer.c:7:25: warning: incorrect type in initializer (different base types) bitwise-function-pointer.c:7:25: expected restricted t ( *static [toplevel] ptr )( ... ) bitwise-function-pointer.c:7:25: got unsigned int ( * )( ... ) * check-error-end */ sparse-0.6.4/validation/bool-array.c000066400000000000000000000014771411531012200173620ustar00rootroot00000000000000static _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.6.4/validation/bool-cast-bad.c000066400000000000000000000011671411531012200177160ustar00rootroot00000000000000typedef 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.6.4/validation/bool-cast-restricted.c000066400000000000000000000032001411531012200213260ustar00rootroot00000000000000typedef 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.6.4/validation/bool-float.c000066400000000000000000000002721411531012200173410ustar00rootroot00000000000000int ftst(double a) { return !a; } /* * check-name: not-operator on float * check-command: test-linearize -Wno-decl $file * check-output-ignore * * check-output-excludes: \\$0 */ sparse-0.6.4/validation/bswap-constant-folding.c000066400000000000000000000011171411531012200216650ustar00rootroot00000000000000typedef 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.6.4/validation/bug-bad-type.c000066400000000000000000000004641411531012200175660ustar00rootroot00000000000000struct s { int i; }; long a; void foo(void) { (struct s) { .i = (foo - a), }; } /* * check-name: bug-bad-type * * check-error-start bug-bad-type.c:5:6: warning: symbol 'a' was not declared. Should it be static? bug-bad-type.c:8:32: error: arithmetics on pointers to functions * check-error-end */ sparse-0.6.4/validation/bug-crash16.c000066400000000000000000000001601411531012200173210ustar00rootroot00000000000000static void foo(void) { int b[] = { 8 }; int c; for (;;) b[c] = b[0]; } /* * check-name: bug-crash16 */ sparse-0.6.4/validation/bug-rshift-ub.c000066400000000000000000000006071411531012200177630ustar00rootroot00000000000000enum a { A = ~0ULL, }; static enum a a = A; /* * check-name: bug-rshift-ub * check-description: * This test trigger(ed) a bug on x86 caused by a * full width shift (which is UB), expecting to get * 0 but giving the unshifted value and as result * the type is invalid: * warning: incorrect type in initializer (invalid types) * expected bad type enum a static [toplevel] a */ sparse-0.6.4/validation/bug_inline_switch.c000066400000000000000000000005301411531012200207740ustar00rootroot00000000000000 #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.6.4/validation/builtin-args-checking.c000066400000000000000000000034151411531012200214560ustar00rootroot00000000000000static 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.6.4/validation/builtin-arith.c000066400000000000000000000044611411531012200200620ustar00rootroot00000000000000 void test(void (*fun)(void)); void test(void (*fun)(void)) { typedef typeof(__builtin_trap) t; // OK void (*f)(void); int i; f = __builtin_trap; f = &__builtin_trap; f = *__builtin_trap; // OK for GCC f = __builtin_trap + 0; f = __builtin_trap + 1; f = __builtin_trap - 1; // (void) __builtin_trap; f = (void*) __builtin_trap; f = (unsigned long) __builtin_trap; i = !__builtin_trap; i = (__builtin_trap > fun); i = (__builtin_trap == fun); i = (fun < __builtin_trap); i = (fun == __builtin_trap); __builtin_trap - fun; fun - __builtin_trap; } /* * check-name: builtin arithmetic * check-command: sparse -Wno-decl $file * * check-error-start builtin-arith.c:10:14: error: taking the address of built-in function '__builtin_trap' builtin-arith.c:11:13: error: taking the address of built-in function '__builtin_trap' builtin-arith.c:12:14: error: taking the address of built-in function '__builtin_trap' builtin-arith.c:13:14: error: taking the address of built-in function '__builtin_trap' builtin-arith.c:13:29: error: arithmetics on pointers to functions builtin-arith.c:14:14: error: taking the address of built-in function '__builtin_trap' builtin-arith.c:14:29: error: arithmetics on pointers to functions builtin-arith.c:15:14: error: taking the address of built-in function '__builtin_trap' builtin-arith.c:15:29: error: arithmetics on pointers to functions builtin-arith.c:18:21: error: taking the address of built-in function '__builtin_trap' builtin-arith.c:19:29: error: taking the address of built-in function '__builtin_trap' builtin-arith.c:21:14: error: taking the address of built-in function '__builtin_trap' builtin-arith.c:22:14: error: taking the address of built-in function '__builtin_trap' builtin-arith.c:23:14: error: taking the address of built-in function '__builtin_trap' builtin-arith.c:24:21: error: taking the address of built-in function '__builtin_trap' builtin-arith.c:25:21: error: taking the address of built-in function '__builtin_trap' builtin-arith.c:27:9: error: taking the address of built-in function '__builtin_trap' builtin-arith.c:27:24: error: subtraction of functions? Share your drugs builtin-arith.c:28:15: error: taking the address of built-in function '__builtin_trap' builtin-arith.c:28:13: error: subtraction of functions? Share your drugs * check-error-end */ sparse-0.6.4/validation/builtin-atomic-clear.c000066400000000000000000000005651411531012200213140ustar00rootroot00000000000000void foo(void *ptr, _Bool *bptr, volatile void *vptr, volatile _Bool *vbptr, int mo) { __atomic_clear(ptr, mo); __atomic_clear(bptr, mo); __atomic_clear(vptr, mo); __atomic_clear(vbptr, mo); } /* * check-name: builtin-atomic-clear * * check-error-start builtin-atomic-clear.c:1:6: warning: symbol 'foo' was not declared. Should it be static? * check-error-end */ sparse-0.6.4/validation/builtin-bswap-constant.c000066400000000000000000000013351411531012200217130ustar00rootroot00000000000000unsigned 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.6.4/validation/builtin-bswap-variable.c000066400000000000000000000014161411531012200216470ustar00rootroot00000000000000typedef 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:trunc.32 .* (64) %arg1 * check-output-contains:call.32 .* __builtin_bswap32 * check-output-contains:zext.64 .* (32) %arg1 * check-output-contains:call.64 .* __builtin_bswap64 */ sparse-0.6.4/validation/builtin-fp-unop.c000066400000000000000000000054361411531012200203420ustar00rootroot00000000000000static void test_not_enough_args(void) { __builtin_isfinite(); __builtin_isinf(); __builtin_isinf_sign(); __builtin_isnan(); __builtin_isnormal(); __builtin_signbit(); } static void test_too_many_args(double v) { __builtin_isfinite(v, v); __builtin_isinf(v, v); __builtin_isinf_sign(v, v); __builtin_isnan(v, v); __builtin_isnormal(v, v); __builtin_signbit(v, v); } static void test_non_float(int v) { __builtin_isfinite(v); __builtin_isinf(v); __builtin_isinf_sign(v); __builtin_isnan(v); __builtin_isnormal(v); __builtin_signbit(v); } static void test_float(float v) { __builtin_isfinite(v); __builtin_isinf(v); __builtin_isinf_sign(v); __builtin_isnan(v); __builtin_isnormal(v); __builtin_signbit(v); } static void test_double(double v) { __builtin_isfinite(v); __builtin_isinf(v); __builtin_isinf_sign(v); __builtin_isnan(v); __builtin_isnormal(v); __builtin_signbit(v); } static void test_ldouble(long double v) { __builtin_isfinite(v); __builtin_isinf(v); __builtin_isinf_sign(v); __builtin_isnan(v); __builtin_isnormal(v); __builtin_signbit(v); } static void test_constant(void) { __builtin_isfinite(0.0); __builtin_isinf(0.0); __builtin_isinf_sign(0.0); __builtin_isnan(0.0); __builtin_isnormal(0.0); __builtin_signbit(0.0); } /* * check-name: builtin float-point unop * check-command: sparse -Wno-decl $file * * check-error-start builtin-fp-unop.c:3:27: error: not enough arguments for __builtin_isfinite builtin-fp-unop.c:4:24: error: not enough arguments for __builtin_isinf builtin-fp-unop.c:5:29: error: not enough arguments for __builtin_isinf_sign builtin-fp-unop.c:6:24: error: not enough arguments for __builtin_isnan builtin-fp-unop.c:7:27: error: not enough arguments for __builtin_isnormal builtin-fp-unop.c:8:26: error: not enough arguments for __builtin_signbit builtin-fp-unop.c:13:27: error: too many arguments for __builtin_isfinite builtin-fp-unop.c:14:24: error: too many arguments for __builtin_isinf builtin-fp-unop.c:15:29: error: too many arguments for __builtin_isinf_sign builtin-fp-unop.c:16:24: error: too many arguments for __builtin_isnan builtin-fp-unop.c:17:27: error: too many arguments for __builtin_isnormal builtin-fp-unop.c:18:26: error: too many arguments for __builtin_signbit builtin-fp-unop.c:23:27: error: non-floating-point argument in call to __builtin_isfinite() builtin-fp-unop.c:24:24: error: non-floating-point argument in call to __builtin_isinf() builtin-fp-unop.c:25:29: error: non-floating-point argument in call to __builtin_isinf_sign() builtin-fp-unop.c:26:24: error: non-floating-point argument in call to __builtin_isnan() builtin-fp-unop.c:27:27: error: non-floating-point argument in call to __builtin_isnormal() builtin-fp-unop.c:28:26: error: non-floating-point argument in call to __builtin_signbit() * check-error-end */ sparse-0.6.4/validation/builtin-objsize-dyn.c000066400000000000000000000006351411531012200212070ustar00rootroot00000000000000void *alloc(unsigned long)__attribute__((alloc_size(1))); _Bool sta(void) { void *ptr = alloc(4); return __builtin_object_size(ptr, 0) == 4; } _Bool dyn(unsigned long n) { void *ptr = alloc(n); return __builtin_object_size(ptr, 0) == n; } /* * check-name: builtin-objsize-dyn * check-command: test-linearize -Wno-decl $file * check-known-to-fail * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/builtin-objsize0.c000066400000000000000000000010551411531012200204740ustar00rootroot00000000000000#define bos(O, T) __builtin_object_size(O, T) struct s { char arr[8]; __INT32_TYPE__ i; __INT32_TYPE__ padding; }; static struct s s; static char *p = &s.arr[1]; static int *q = &s.i; int obj_int0(void) { return bos(&s.i, 0) == 8; } int obj_arr0(void) { return bos(&s.arr[1], 0) == 15; } int ptr_int(struct s *p) { return bos(&p->i, 0) == -1; } int ptr_arr(struct s *p) { return bos(&p->arr[1], 0) == -1; } /* * check-name: builtin-objsize0 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/builtin-objsize1.c000066400000000000000000000006301411531012200204730ustar00rootroot00000000000000#define bos(O, T) __builtin_object_size(O, T) struct s { char arr[8]; __INT32_TYPE__ i; __INT32_TYPE__ padding; }; static struct s s; int obj_int1(void) { return bos(&s.i, 1) == 4; } int obj_arr1(void) { return bos(&s.arr[1], 1) == 7; } /* * check-name: builtin-objsize1 * check-command: test-linearize -Wno-decl $file * check-known-to-fail * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/builtin-overflow.c000066400000000000000000000266111411531012200206170ustar00rootroot00000000000000enum e { OK, KO = -1 }; typedef _Bool bool; static int test(int i, long l, long long ll, enum e e, bool b, void *p) { int rc = 0; // should be OK rc += __builtin_add_overflow(i, i, &i); rc += __builtin_add_overflow(l, i, &i); rc += __builtin_add_overflow(i, l, &i); rc += __builtin_add_overflow(i, i, &l); rc += __builtin_add_overflow(ll, i, &i); rc += __builtin_add_overflow(i, ll, &i); rc += __builtin_add_overflow(i, i, &ll); rc += __builtin_add_overflow_p(i, i, i); rc += __builtin_add_overflow_p(l, i, i); rc += __builtin_add_overflow_p(i, l, i); rc += __builtin_add_overflow_p(i, i, l); rc += __builtin_add_overflow_p(ll, i, i); rc += __builtin_add_overflow_p(i, ll, i); rc += __builtin_add_overflow_p(i, i, ll); rc += __builtin_sub_overflow(i, i, &i); rc += __builtin_sub_overflow(l, i, &i); rc += __builtin_sub_overflow(i, l, &i); rc += __builtin_sub_overflow(i, i, &l); rc += __builtin_sub_overflow(ll, i, &i); rc += __builtin_sub_overflow(i, ll, &i); rc += __builtin_sub_overflow(i, i, &ll); rc += __builtin_sub_overflow_p(i, i, i); rc += __builtin_sub_overflow_p(l, i, i); rc += __builtin_sub_overflow_p(i, l, i); rc += __builtin_sub_overflow_p(i, i, l); rc += __builtin_sub_overflow_p(ll, i, i); rc += __builtin_sub_overflow_p(i, ll, i); rc += __builtin_sub_overflow_p(i, i, ll); rc += __builtin_mul_overflow(i, i, &i); rc += __builtin_mul_overflow(l, i, &i); rc += __builtin_mul_overflow(i, l, &i); rc += __builtin_mul_overflow(i, i, &l); rc += __builtin_mul_overflow(ll, i, &i); rc += __builtin_mul_overflow(i, ll, &i); rc += __builtin_mul_overflow(i, i, &ll); rc += __builtin_mul_overflow_p(i, i, i); rc += __builtin_mul_overflow_p(l, i, i); rc += __builtin_mul_overflow_p(i, l, i); rc += __builtin_mul_overflow_p(i, i, l); rc += __builtin_mul_overflow_p(ll, i, i); rc += __builtin_mul_overflow_p(i, ll, i); rc += __builtin_mul_overflow_p(i, i, ll); // should be KO rc += __builtin_add_overflow(); rc += __builtin_add_overflow(i); rc += __builtin_add_overflow(i, i); rc += __builtin_add_overflow(i, i, &i, i); rc += __builtin_add_overflow(e, i, &i); rc += __builtin_add_overflow(i, e, &i); rc += __builtin_add_overflow(i, i, &e); rc += __builtin_add_overflow(b, i, &i); rc += __builtin_add_overflow(i, b, &i); rc += __builtin_add_overflow(i, i, &b); rc += __builtin_add_overflow(i, i, p); rc += __builtin_add_overflow_p(); rc += __builtin_add_overflow_p(i); rc += __builtin_add_overflow_p(i, i); rc += __builtin_add_overflow_p(i, i, i, i); rc += __builtin_add_overflow_p(e, i, i); rc += __builtin_add_overflow_p(i, e, i); rc += __builtin_add_overflow_p(i, i, e); rc += __builtin_add_overflow_p(b, i, i); rc += __builtin_add_overflow_p(i, b, i); rc += __builtin_add_overflow_p(i, i, b); rc += __builtin_add_overflow_p(i, i, p); rc += __builtin_sub_overflow(); rc += __builtin_sub_overflow(i); rc += __builtin_sub_overflow(i, i); rc += __builtin_sub_overflow(i, i, &i, i); rc += __builtin_sub_overflow(e, i, &i); rc += __builtin_sub_overflow(i, e, &i); rc += __builtin_sub_overflow(i, i, &e); rc += __builtin_sub_overflow(b, i, &i); rc += __builtin_sub_overflow(i, b, &i); rc += __builtin_sub_overflow(i, i, &b); rc += __builtin_sub_overflow(i, i, p); rc += __builtin_sub_overflow_p(); rc += __builtin_sub_overflow_p(i); rc += __builtin_sub_overflow_p(i, i); rc += __builtin_sub_overflow_p(i, i, i, i); rc += __builtin_sub_overflow_p(e, i, i); rc += __builtin_sub_overflow_p(i, e, i); rc += __builtin_sub_overflow_p(i, i, e); rc += __builtin_sub_overflow_p(b, i, i); rc += __builtin_sub_overflow_p(i, b, i); rc += __builtin_sub_overflow_p(i, i, b); rc += __builtin_sub_overflow_p(i, i, p); rc += __builtin_mul_overflow(); rc += __builtin_mul_overflow(i); rc += __builtin_mul_overflow(i, i); rc += __builtin_mul_overflow(i, i, &i, i); rc += __builtin_mul_overflow(e, i, &i); rc += __builtin_mul_overflow(i, e, &i); rc += __builtin_mul_overflow(i, i, &e); rc += __builtin_mul_overflow(b, i, &i); rc += __builtin_mul_overflow(i, b, &i); rc += __builtin_mul_overflow(i, i, &b); rc += __builtin_mul_overflow(i, i, p); rc += __builtin_mul_overflow_p(); rc += __builtin_mul_overflow_p(i); rc += __builtin_mul_overflow_p(i, i); rc += __builtin_mul_overflow_p(i, i, i, i); rc += __builtin_mul_overflow_p(e, i, i); rc += __builtin_mul_overflow_p(i, e, i); rc += __builtin_mul_overflow_p(i, i, e); rc += __builtin_mul_overflow_p(b, i, i); rc += __builtin_mul_overflow_p(i, b, i); rc += __builtin_mul_overflow_p(i, i, b); rc += __builtin_mul_overflow_p(i, i, p); return rc; } /* * check-name: builtin-overflow * * check-error-start builtin-overflow.c:58:37: error: not enough arguments for __builtin_add_overflow builtin-overflow.c:59:37: error: not enough arguments for __builtin_add_overflow builtin-overflow.c:60:37: error: not enough arguments for __builtin_add_overflow builtin-overflow.c:61:37: error: too many arguments for __builtin_add_overflow builtin-overflow.c:62:38: error: invalid type for argument 1: builtin-overflow.c:62:38: int enum e e builtin-overflow.c:63:41: error: invalid type for argument 2: builtin-overflow.c:63:41: int enum e e builtin-overflow.c:64:45: error: invalid type for argument 3: builtin-overflow.c:64:45: int enum e * builtin-overflow.c:65:38: error: invalid type for argument 1: builtin-overflow.c:65:38: bool [usertype] b builtin-overflow.c:66:41: error: invalid type for argument 2: builtin-overflow.c:66:41: bool [usertype] b builtin-overflow.c:67:45: error: invalid type for argument 3: builtin-overflow.c:67:45: bool * builtin-overflow.c:68:44: error: invalid type for argument 3: builtin-overflow.c:68:44: void *p builtin-overflow.c:70:39: error: not enough arguments for __builtin_add_overflow_p builtin-overflow.c:71:39: error: not enough arguments for __builtin_add_overflow_p builtin-overflow.c:72:39: error: not enough arguments for __builtin_add_overflow_p builtin-overflow.c:73:39: error: too many arguments for __builtin_add_overflow_p builtin-overflow.c:74:40: error: invalid type for argument 1: builtin-overflow.c:74:40: int enum e [addressable] e builtin-overflow.c:75:43: error: invalid type for argument 2: builtin-overflow.c:75:43: int enum e [addressable] e builtin-overflow.c:76:46: error: invalid type for argument 3: builtin-overflow.c:76:46: int enum e [addressable] e builtin-overflow.c:77:40: error: invalid type for argument 1: builtin-overflow.c:77:40: bool [addressable] [usertype] b builtin-overflow.c:78:43: error: invalid type for argument 2: builtin-overflow.c:78:43: bool [addressable] [usertype] b builtin-overflow.c:79:46: error: invalid type for argument 3: builtin-overflow.c:79:46: bool [addressable] [usertype] b builtin-overflow.c:80:46: error: invalid type for argument 3: builtin-overflow.c:80:46: void *p builtin-overflow.c:82:37: error: not enough arguments for __builtin_sub_overflow builtin-overflow.c:83:37: error: not enough arguments for __builtin_sub_overflow builtin-overflow.c:84:37: error: not enough arguments for __builtin_sub_overflow builtin-overflow.c:85:37: error: too many arguments for __builtin_sub_overflow builtin-overflow.c:86:38: error: invalid type for argument 1: builtin-overflow.c:86:38: int enum e [addressable] e builtin-overflow.c:87:41: error: invalid type for argument 2: builtin-overflow.c:87:41: int enum e [addressable] e builtin-overflow.c:88:45: error: invalid type for argument 3: builtin-overflow.c:88:45: int enum e * builtin-overflow.c:89:38: error: invalid type for argument 1: builtin-overflow.c:89:38: bool [addressable] [usertype] b builtin-overflow.c:90:41: error: invalid type for argument 2: builtin-overflow.c:90:41: bool [addressable] [usertype] b builtin-overflow.c:91:45: error: invalid type for argument 3: builtin-overflow.c:91:45: bool * builtin-overflow.c:92:44: error: invalid type for argument 3: builtin-overflow.c:92:44: void *p builtin-overflow.c:94:39: error: not enough arguments for __builtin_sub_overflow_p builtin-overflow.c:95:39: error: not enough arguments for __builtin_sub_overflow_p builtin-overflow.c:96:39: error: not enough arguments for __builtin_sub_overflow_p builtin-overflow.c:97:39: error: too many arguments for __builtin_sub_overflow_p builtin-overflow.c:98:40: error: invalid type for argument 1: builtin-overflow.c:98:40: int enum e [addressable] e builtin-overflow.c:99:43: error: invalid type for argument 2: builtin-overflow.c:99:43: int enum e [addressable] e builtin-overflow.c:100:46: error: invalid type for argument 3: builtin-overflow.c:100:46: int enum e [addressable] e builtin-overflow.c:101:40: error: invalid type for argument 1: builtin-overflow.c:101:40: bool [addressable] [usertype] b builtin-overflow.c:102:43: error: invalid type for argument 2: builtin-overflow.c:102:43: bool [addressable] [usertype] b builtin-overflow.c:103:46: error: invalid type for argument 3: builtin-overflow.c:103:46: bool [addressable] [usertype] b builtin-overflow.c:104:46: error: invalid type for argument 3: builtin-overflow.c:104:46: void *p builtin-overflow.c:106:37: error: not enough arguments for __builtin_mul_overflow builtin-overflow.c:107:37: error: not enough arguments for __builtin_mul_overflow builtin-overflow.c:108:37: error: not enough arguments for __builtin_mul_overflow builtin-overflow.c:109:37: error: too many arguments for __builtin_mul_overflow builtin-overflow.c:110:38: error: invalid type for argument 1: builtin-overflow.c:110:38: int enum e [addressable] e builtin-overflow.c:111:41: error: invalid type for argument 2: builtin-overflow.c:111:41: int enum e [addressable] e builtin-overflow.c:112:45: error: invalid type for argument 3: builtin-overflow.c:112:45: int enum e * builtin-overflow.c:113:38: error: invalid type for argument 1: builtin-overflow.c:113:38: bool [addressable] [usertype] b builtin-overflow.c:114:41: error: invalid type for argument 2: builtin-overflow.c:114:41: bool [addressable] [usertype] b builtin-overflow.c:115:45: error: invalid type for argument 3: builtin-overflow.c:115:45: bool * builtin-overflow.c:116:44: error: invalid type for argument 3: builtin-overflow.c:116:44: void *p builtin-overflow.c:118:39: error: not enough arguments for __builtin_mul_overflow_p builtin-overflow.c:119:39: error: not enough arguments for __builtin_mul_overflow_p builtin-overflow.c:120:39: error: not enough arguments for __builtin_mul_overflow_p builtin-overflow.c:121:39: error: too many arguments for __builtin_mul_overflow_p builtin-overflow.c:122:40: error: invalid type for argument 1: builtin-overflow.c:122:40: int enum e [addressable] e builtin-overflow.c:123:43: error: invalid type for argument 2: builtin-overflow.c:123:43: int enum e [addressable] e builtin-overflow.c:124:46: error: invalid type for argument 3: builtin-overflow.c:124:46: int enum e [addressable] e builtin-overflow.c:125:40: error: invalid type for argument 1: builtin-overflow.c:125:40: bool [addressable] [usertype] b builtin-overflow.c:126:43: error: invalid type for argument 2: builtin-overflow.c:126:43: bool [addressable] [usertype] b builtin-overflow.c:127:46: error: invalid type for argument 3: builtin-overflow.c:127:46: bool [addressable] [usertype] b builtin-overflow.c:128:46: error: invalid type for argument 3: builtin-overflow.c:128:46: void *p * check-error-end */ sparse-0.6.4/validation/builtin-prototype.c000066400000000000000000000004661411531012200210210ustar00rootroot00000000000000void memcpy(void *dst, const void *src, unsigned int size); void memcpy(void *dst, const void *src, unsigned int size) { __builtin_memcpy(dst, src, size); } unsigned int strlen(const char *src); unsigned int strlen(const char *src) { return __builtin_strlen(src); } /* * check-name: builtin-prototype */ sparse-0.6.4/validation/builtin-sync-cas.c000066400000000000000000000010431411531012200204640ustar00rootroot00000000000000static int *foo(int *ptr) { __sync_val_compare_and_swap(ptr, 123, 0L); return __sync_val_compare_and_swap(&ptr, ptr, ptr); } static long bar(long *ptr) { return __sync_val_compare_and_swap(ptr, ptr, 1); } static _Bool boz(_Bool *ptr) { return __sync_bool_compare_and_swap(ptr, 0, ptr); } /* * check-name: builtin-sync-cas * * check-error-start builtin-sync-cas.c:9:49: warning: incorrect type in argument 2 (different base types) builtin-sync-cas.c:9:49: expected long builtin-sync-cas.c:9:49: got long *ptr * check-error-end */ sparse-0.6.4/validation/builtin-sync-fetch.c000066400000000000000000000007041411531012200210120ustar00rootroot00000000000000static int ok_int(int *ptr, int val) { return __sync_add_and_fetch(ptr, val); } static long* ok_ptr(long **ptr, long *val) { return __sync_add_and_fetch(ptr, val); } static void chk_ret_ok(long *ptr, long val) { _Static_assert([typeof(__sync_add_and_fetch(ptr, val))] == [long], ""); } static int chk_val(int *ptr, long val) { // OK: val is converted to an int return __sync_add_and_fetch(ptr, val); } /* * check-name: builtin-sync-fetch */ sparse-0.6.4/validation/builtin_atomic.c000066400000000000000000000013431411531012200203050ustar00rootroot00000000000000static 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.6.4/validation/builtin_bswap.c000066400000000000000000000003411411531012200201420ustar00rootroot00000000000000static 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.6.4/validation/builtin_inf.c000066400000000000000000000010271411531012200176040ustar00rootroot00000000000000static 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(""); static int inf = __builtin_isinf_sign(0.0); static int fin = __builtin_isfinite(0.0); static int nan = __builtin_isnan(0.0); /* * check-name: __builtin INFINITY / nan() */ sparse-0.6.4/validation/builtin_safe1.c000066400000000000000000000020271411531012200200300ustar00rootroot00000000000000#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.6.4/validation/builtin_unreachable.c000066400000000000000000000003511411531012200213000ustar00rootroot00000000000000/* 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.6.4/validation/builtin_va_arg_pack.c000066400000000000000000000005301411531012200212630ustar00rootroot00000000000000extern 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.6.4/validation/c11-alignas.c000066400000000000000000000017261411531012200173100ustar00rootroot00000000000000static _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.6.4/validation/c11-alignof.c000066400000000000000000000003101411531012200172750ustar00rootroot00000000000000static 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.6.4/validation/c11-atomic.c000066400000000000000000000100531411531012200171370ustar00rootroot00000000000000void f00(int _Atomic dst); void f01(int _Atomic *dst); void f02(int _Atomic *dst); void f03(int _Atomic *dst); int _Atomic qo; int uo; void f00(int dst) { } /* check-should-fail */ void f01(typeof(&qo) dst) { } /* check-should-pass */ void f02(int *dst) { } /* check-should-fail */ void f03(typeof(&uo) dst) { } /* check-should-fail */ void foo(void) { qo = uo; /* check-should-pass */ uo = qo; /* check-should-pass */ } void ref(void) { const int qo; int uo; const int *pqo; int *puo; pqo = &qo; /* check-should-pass */ pqo = &uo; /* check-should-pass */ pqo = puo; /* check-should-pass */ puo = &uo; /* check-should-pass */ puo = &qo; /* check-should-fail */ puo = pqo; /* check-should-fail */ } void bar(void) { int _Atomic *pqo; int *puo; pqo = &qo; /* check-should-pass */ pqo = &uo; /* check-should-fail */ pqo = puo; /* check-should-fail */ puo = &uo; /* check-should-pass */ puo = &qo; /* check-should-fail */ puo = pqo; /* check-should-fail */ } void baz(void) { typeof(&qo) pqo; typeof(&uo) puo; pqo = &qo; /* check-should-pass */ pqo = &uo; /* check-should-fail*/ pqo = puo; /* check-should-fail */ puo = &uo; /* check-should-pass */ puo = &qo; /* check-should-fail */ puo = pqo; /* check-should-fail */ } /* * check-name: C11 _Atomic type qualifier * check-command: sparse -Wno-decl $file * * check-error-start c11-atomic.c:9:6: error: symbol 'f00' redeclared with different type (incompatible argument 1 (different modifiers)): c11-atomic.c:9:6: void extern [addressable] [toplevel] f00( ... ) c11-atomic.c:1:6: note: previously declared as: c11-atomic.c:1:6: void extern [addressable] [toplevel] f00( ... ) c11-atomic.c:11:6: error: symbol 'f02' redeclared with different type (incompatible argument 1 (different modifiers)): c11-atomic.c:11:6: void extern [addressable] [toplevel] f02( ... ) c11-atomic.c:3:6: note: previously declared as: c11-atomic.c:3:6: void extern [addressable] [toplevel] f02( ... ) c11-atomic.c:12:6: error: symbol 'f03' redeclared with different type (incompatible argument 1 (different modifiers)): c11-atomic.c:12:6: void extern [addressable] [toplevel] f03( ... ) c11-atomic.c:4:6: note: previously declared as: c11-atomic.c:4:6: void extern [addressable] [toplevel] f03( ... ) c11-atomic.c:33:13: warning: incorrect type in assignment (different modifiers) c11-atomic.c:33:13: expected int *[assigned] puo c11-atomic.c:33:13: got int const * c11-atomic.c:34:13: warning: incorrect type in assignment (different modifiers) c11-atomic.c:34:13: expected int *[assigned] puo c11-atomic.c:34:13: got int const *[assigned] pqo c11-atomic.c:43:13: warning: incorrect type in assignment (different modifiers) c11-atomic.c:43:13: expected int [atomic] *[assigned] pqo c11-atomic.c:43:13: got int * c11-atomic.c:44:13: warning: incorrect type in assignment (different modifiers) c11-atomic.c:44:13: expected int [atomic] *[assigned] pqo c11-atomic.c:44:13: got int *puo c11-atomic.c:48:13: warning: incorrect type in assignment (different modifiers) c11-atomic.c:48:13: expected int *[assigned] puo c11-atomic.c:48:13: got int [atomic] * c11-atomic.c:49:13: warning: incorrect type in assignment (different modifiers) c11-atomic.c:49:13: expected int *[assigned] puo c11-atomic.c:49:13: got int [atomic] *[assigned] pqo c11-atomic.c:58:13: warning: incorrect type in assignment (different modifiers) c11-atomic.c:58:13: expected int [atomic] *[assigned] pqo c11-atomic.c:58:13: got int * c11-atomic.c:59:13: warning: incorrect type in assignment (different modifiers) c11-atomic.c:59:13: expected int [atomic] *[assigned] pqo c11-atomic.c:59:13: got int *puo c11-atomic.c:63:13: warning: incorrect type in assignment (different modifiers) c11-atomic.c:63:13: expected int *[assigned] puo c11-atomic.c:63:13: got int [atomic] * c11-atomic.c:64:13: warning: incorrect type in assignment (different modifiers) c11-atomic.c:64:13: expected int *[assigned] puo c11-atomic.c:64:13: got int [atomic] *[assigned] pqo * check-error-end */ sparse-0.6.4/validation/c11-noreturn.c000066400000000000000000000003051411531012200175360ustar00rootroot00000000000000static _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.6.4/validation/c11-stdc-version.c000066400000000000000000000002321411531012200203010ustar00rootroot00000000000000__STDC_VERSION__ /* * check-name: c11-stdc-version * check-command: sparse -E -std=c11 $file * * check-output-start 201112L * check-output-end */ sparse-0.6.4/validation/c11-thread-local.c000066400000000000000000000002621411531012200202230ustar00rootroot00000000000000static _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.6.4/validation/c99-for-loop-decl.c000066400000000000000000000017321411531012200203510ustar00rootroot00000000000000static 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.6.4/validation/c99-for-loop.c000066400000000000000000000005071411531012200174430ustar00rootroot00000000000000int 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.6.4/validation/call-inlined.c000066400000000000000000000016741411531012200176450ustar00rootroot00000000000000static const char messg[] = "def"; static inline int add(int a, int b) { return a + b; } int foo(int a, int b) { return add(a + b, 1); } void bar(int a, int b) { add(a + b, 1); } static inline const char *lstrip(const char *str) { return str + 1; } const char *bas(void) { return lstrip("abc"); } const char *qus(void) { return lstrip(messg); } /* * check-name: call-inlined * check-command: test-linearize -Wno-decl -m64 $file * check-assert: sizeof(void*) == 8 * * check-output-start foo: .L0: add.32 %r3 <- %arg1, %arg2 add.32 %r5 <- %r3, $1 # call %r6 <- add, %r3, $1 ret.32 %r5 bar: .L3: # call %r13 <- add, %r10, $1 ret bas: .L6: add.64 %r16 <- "abc", $1 # call %r17 <- lstrip, %r14 ret.64 %r16 qus: .L9: add.64 %r21 <- messg, $1 # call %r22 <- lstrip, %r19 ret.64 %r21 * check-output-end */ sparse-0.6.4/validation/call-variadic.c000066400000000000000000000007311411531012200177760ustar00rootroot00000000000000#define NULL ((void*)0) extern int print(const char *msg, ...); int foo(const char *fmt, int a, long l, int *p) { return print("msg %c: %d %d/%ld %ld/%p %p\n", 'x', a, __LINE__, l, 0L, p, NULL); } /* * check-name: call-variadic * check-command: test-linearize -Wno-decl $file * * check-output-start foo: .L0: call.32 %r5 <- print, "msg %c: %d %d/%ld %ld/%p %p\n", $120, %arg2, $7, %arg3, $0, %arg4, $0 ret.32 %r5 * check-output-end */ sparse-0.6.4/validation/calling-convention-attributes.c000066400000000000000000000014211411531012200232550ustar00rootroot00000000000000extern 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.6.4/validation/cast-bad-00.c000066400000000000000000000014341411531012200171770ustar00rootroot00000000000000typedef unsigned short u16; typedef unsigned int u32; union u { u32 a; u16 b; }; struct s { u32 a; u16 b; }; void bar(u16, u32); void union_to_int(u16 val); void struct_to_int(u16 val); void union_to_int(u16 val) { union u u; u.b = val; bar(u.b, u); } void struct_to_int(u16 val) { struct s s; s.b = val; bar(s.b, s); } /* * check-name: cast-bad 00 * * check-error-start cast-bad-00.c:25:18: warning: incorrect type in argument 2 (different base types) cast-bad-00.c:25:18: expected unsigned int [usertype] cast-bad-00.c:25:18: got union u [assigned] u cast-bad-00.c:33:18: warning: incorrect type in argument 2 (different base types) cast-bad-00.c:33:18: expected unsigned int [usertype] cast-bad-00.c:33:18: got struct s [assigned] s * check-error-end */ sparse-0.6.4/validation/cast-bad-01.c000066400000000000000000000003311411531012200171730ustar00rootroot00000000000000extern unsigned long l; int foo(void) { return (int) (typeof(fundecl(0))) l; } /* * check-name: cast-bad 01 * * check-error-start cast-bad-01.c:4:30: error: undefined identifier 'fundecl' * check-error-end */ sparse-0.6.4/validation/cast-kinds-check.c000066400000000000000000000014621411531012200204200ustar00rootroot00000000000000#include "optim/cast-kinds.c" /* * check-name: cast-kinds check * check-command: sparse -m64 -v -Wno-pointer-to-int-cast $file * check-assert: sizeof(long) == 8 * * check-error-start cast-kinds-check.c: note: in included file: optim/cast-kinds.c:5:45: warning: cast drops bits optim/cast-kinds.c:6:47: warning: cast drops bits optim/cast-kinds.c:7:46: warning: cast drops bits optim/cast-kinds.c:8:45: warning: cast drops bits optim/cast-kinds.c:12:48: warning: cast drops bits optim/cast-kinds.c:13:50: warning: cast drops bits optim/cast-kinds.c:14:49: warning: cast drops bits optim/cast-kinds.c:15:48: warning: cast drops bits optim/cast-kinds.c:37:48: warning: non size-preserving integer to pointer cast optim/cast-kinds.c:38:50: warning: non size-preserving integer to pointer cast * check-error-end */ sparse-0.6.4/validation/cast-weirds.c000066400000000000000000000010731411531012200175300ustar00rootroot00000000000000typedef unsigned int uint; typedef unsigned long ulong; static int * int_2_iptr(int a) { return (int *)a; } static int * uint_2_iptr(uint a) { return (int *)a; } static void * int_2_vptr(int a) { return (void *)a; } static void * uint_2_vptr(uint a) { return (void *)a; } /* * check-name: cast-weirds * check-command: sparse -m64 $file * check-assert: sizeof(void *) == 8 * * check-error-start cast-weirds.c:4:48: warning: non size-preserving integer to pointer cast cast-weirds.c:5:50: warning: non size-preserving integer to pointer cast * check-error-end */ sparse-0.6.4/validation/char-signed-native.c000066400000000000000000000002601411531012200207500ustar00rootroot00000000000000void foo(void) { _Static_assert((char) -1 == -1, "plain char is not signed"); } /* * check-name: char-signed-native * check-command: sparse --arch=i386 -Wno-decl $file */ sparse-0.6.4/validation/char-signed.c000066400000000000000000000002541411531012200174670ustar00rootroot00000000000000void foo(void) { _Static_assert((char) -1 == -1, "plain char is not signed"); } /* * check-name: fsigned-char * check-command: sparse -fsigned-char -Wno-decl $file */ sparse-0.6.4/validation/char-unsigned-native.c000066400000000000000000000003441411531012200213160ustar00rootroot00000000000000#define MASK ((1 << __CHAR_BIT__) - 1) void foo(void) { _Static_assert((char) -1 == (-1 & MASK), "plain char is not unsigned"); } /* * check-name: char-unsigned-native * check-command: sparse --arch=arm -Wno-decl $file */ sparse-0.6.4/validation/char-unsigned.c000066400000000000000000000003431411531012200200310ustar00rootroot00000000000000#define MASK ((1 << __CHAR_BIT__) - 1) void foo(void) { _Static_assert((char) -1 == (-1 & MASK), "plain char is not unsigned"); } /* * check-name: funsigned-char * check-command: sparse -funsigned-char -Wno-decl $file */ sparse-0.6.4/validation/check_access-multi.c000066400000000000000000000003511411531012200210270ustar00rootroot00000000000000extern int *a; extern int b[1]; static void foo(void) { *a = b[1]; } /* * check-name: check_access-multi * * check-error-start check_access-multi.c:6:15: warning: invalid access past the end of 'b' (4 4) * check-error-end */ sparse-0.6.4/validation/check_access-store.c000066400000000000000000000005421411531012200210330ustar00rootroot00000000000000extern int a[1]; static int r(void) { return a[1]; } static void w(void) { a[1] = 2; } /* * check-name: check_access-store * check-known-to-fail * * check-error-start check_access-store.c:5:17: warning: invalid access past the end of 'a' (4 4) check_access-store.c:10:17: warning: invalid access past the end of 'a' (4 4) * check-error-end */ sparse-0.6.4/validation/check_byte_count-ice.c000066400000000000000000000012151411531012200213470ustar00rootroot00000000000000extern 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: missing terminating ' character 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 } check_byte_count-ice.c:20:0: error: Expected } at end of function check_byte_count-ice.c:20: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.6.4/validation/choose_expr.c000066400000000000000000000012361411531012200176220ustar00rootroot00000000000000static 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 [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 [toplevel] y choose_expr.c:2:41: got char * choose_expr.c:4:17: warning: division by zero * check-error-end */ sparse-0.6.4/validation/comma.c000066400000000000000000000004151411531012200163760ustar00rootroot00000000000000static 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.6.4/validation/compare-null-to-int.c000066400000000000000000000005331411531012200211110ustar00rootroot00000000000000static 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: void * compare-null-to-int.c:1:44: int * check-error-end */ sparse-0.6.4/validation/compound-assign-type.c000066400000000000000000000006031411531012200213660ustar00rootroot00000000000000static unsigned int foo(unsigned int x, long a) { x /= a; return x; } /* * check-name: compound-assign-type * check-command: test-linearize -m64 $file * check-assert: sizeof(long) == 8 * * check-output-ignore * * check-output-excludes: divu\\.32 * check-output-contains: divs\\.64 * check-output-contains: zext.64 .* (32) %arg1 * check-output-contains: trunc.32 .* (64) */ sparse-0.6.4/validation/compound-sizes.c000066400000000000000000000034451411531012200202670ustar00rootroot00000000000000// This tests sparse "-vcompound" output. #define NULL ((void*)0) typedef unsigned int uint32_t; typedef unsigned long long uint64_t; // Do not list functions. static int do_nothing(void) {} // no: static inline int zero(void) { return 0 / 1; } // no: struct inventory { unsigned char description[64]; unsigned char department[64]; uint32_t dept_number; uint32_t item_cost; uint64_t stock_number; uint32_t tally[12]; // per month }; // no static struct inventory *get_inv(uint64_t stocknum) { return NULL; } // no union un { struct inventory inv; unsigned char bytes[0]; }; // yes static union un un; // yes static struct inventory inven[100]; // no typedef struct inventory inventory_t; // no static struct inventory *invptr; // yes static inventory_t invent[10]; // no static float floater; static double double_float; // yes static float floats[42]; static double doubles[84]; // no int main(void) { // no, these are not global. struct inventory inv[10]; inventory_t invt[10]; // what about statics? static struct inventory invtop; static inventory_t inv_top; static uint64_t stocknums[100]; invptr = get_inv(42000); return 0; } /* * check-name: compound-sizes * check-command: sparse -vcompound $file * check-assert: _Alignof(long long) == 8 * * check-error-start compound-sizes.c:39:17: union un static [toplevel] un: compound size 192, alignment 8 compound-sizes.c:42:25: struct inventory static [toplevel] inven[100]: compound size 19200, alignment 8 compound-sizes.c:51:33: struct inventory static [toplevel] [usertype] invent[10]: compound size 1920, alignment 8 compound-sizes.c:58:25: float static [toplevel] floats[42]: compound size 168, alignment 4 compound-sizes.c:59:25: double static [toplevel] doubles[84]: compound size 672, alignment 8 * check-error-end */ sparse-0.6.4/validation/cond-address.c000066400000000000000000000005021411531012200176450ustar00rootroot00000000000000extern 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-output-excludes: VOID */ sparse-0.6.4/validation/cond-err-expand.c000066400000000000000000000012331411531012200202670ustar00rootroot00000000000000static 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:8:11: int cond-err-expand.c:8:11: void cond-err-expand.c:13:11: error: incompatible types in conditional expression (different base types): cond-err-expand.c:13:11: void cond-err-expand.c:13:11: int * check-error-end * * check-output-ignore * check-output-excludes: call.* __builtin_constant_p */ sparse-0.6.4/validation/cond_expr.c000066400000000000000000000007341411531012200172670ustar00rootroot00000000000000/* * 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 type for operation (~): cond_expr.c:10:16: double * check-error-end */ sparse-0.6.4/validation/cond_expr2.c000066400000000000000000000015451411531012200173520ustar00rootroot00000000000000extern 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] [assigned] [toplevel] r cond_expr2.c:8:11: got int const volatile * * check-error-end */ sparse-0.6.4/validation/cond_expr3.c000066400000000000000000000011561411531012200173510ustar00rootroot00000000000000static 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.6.4/validation/conditional-type.c000066400000000000000000000036301411531012200205660ustar00rootroot00000000000000extern 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: non-scalar type in conditional: conditional-type.c:18:18: void conditional-type.c:19:13: error: non-scalar type in conditional: conditional-type.c:19:13: struct state s conditional-type.c:24:18: error: non-scalar type in conditional: conditional-type.c:24:18: void conditional-type.c:29:21: error: non-scalar type in conditional: conditional-type.c:29:21: void conditional-type.c:30:16: error: non-scalar type in conditional: conditional-type.c:30:16: struct state s conditional-type.c:34:21: error: non-scalar type in conditional: conditional-type.c:34:21: void conditional-type.c:36:20: error: non-scalar type in conditional: conditional-type.c:36:20: void conditional-type.c:40:21: error: non-scalar type in conditional: conditional-type.c:40:21: void * check-error-end */ sparse-0.6.4/validation/constant-suffix-32.c000066400000000000000000000005761411531012200206670ustar00rootroot00000000000000#define BIGU 0xfffff00000000000U #define BIGULL 0xfffff00000000000ULL static unsigned long long a = BIGU; static unsigned long long b = BIGULL; /* * check-name: constant-suffix * check-command: sparse -m32 -Wconstant-suffix $file * * check-error-start constant-suffix-32.c:4:31: warning: constant 0xfffff00000000000U is so big it is unsigned long long * check-error-end */ sparse-0.6.4/validation/constant-suffix-64.c000066400000000000000000000006171411531012200206700ustar00rootroot00000000000000#define BIGU 0xfffff00000000000U #define BIGUL 0xfffff00000000000UL static unsigned long a = BIGU; static unsigned long b = BIGUL; /* * check-name: constant-suffix * check-command: sparse -m64 -Wconstant-suffix $file * check-assert: sizeof(long) == 8 * * check-error-start constant-suffix-64.c:4:26: warning: constant 0xfffff00000000000U is so big it is unsigned long * check-error-end */ sparse-0.6.4/validation/constexpr-addr-of-static-member.c000066400000000000000000000007211411531012200233730ustar00rootroot00000000000000struct A { int a; int b[2]; }; struct B { int c; struct A d; }; static struct B a= {1, {1, {1, 1}}}; static int *b = &a.d.a; // OK static int *c = &(&a.d)->a; // OK static int *d = a.d.b; // OK static int *e = (&a.d)->b; // OK static int *f = &a.d.b[1]; // OK static int *g = &(&a.d)->b[1]; // OK /* * check-name: constexpr static object's member address * check-command: sparse -Wconstexpr-not-const $file * * check-error-start * check-error-end */ sparse-0.6.4/validation/constexpr-addr-of-static.c000066400000000000000000000015171411531012200221320ustar00rootroot00000000000000static int a = 1; static int b[2] = {1, 1}; static void c(void) {} static int *d = &a; // OK static int *e = d; // KO static int *f = b; // OK static void (*g)(void) = c; // OK static void (*h)(void) = &c; // OK static int *i = &*&a; // OK static int *j = &*b; // OK static int *k = &*d; // KO static void l(void) { int a = 1; static int *b = &a; // KO } static void m(void) { static int a = 1; static int *b = &a; // OK } /* * check-name: constexpr static object address * check-command: sparse -Wconstexpr-not-const $file * * check-error-start constexpr-addr-of-static.c:6:17: warning: non-constant initializer for static object constexpr-addr-of-static.c:14:19: warning: non-constant initializer for static object constexpr-addr-of-static.c:19:26: warning: non-constant initializer for static object * check-error-end */ sparse-0.6.4/validation/constexpr-binop.c000066400000000000000000000023461411531012200204410ustar00rootroot00000000000000static int a[] = { [0 + 0] = 0, // OK [0 + 0.] = 0, // KO [(void*)0 + 0] = 0, // KO [0 + __builtin_choose_expr(0, 0, 0)] = 0, // OK [0 + __builtin_choose_expr(0, 0., 0)] = 0, // OK [0 + __builtin_choose_expr(0, 0, 0.)] = 0, // KO [0 < 0] = 0, // OK [0 < 0.] = 0, // KO [0 < __builtin_choose_expr(0, 0, 0)] = 0, // OK [0 < __builtin_choose_expr(0, 0., 0)] = 0, // OK [0 < __builtin_choose_expr(0, 0, 0.)] = 0, // KO [0 && 0] = 0, // OK [0 && 0.] = 0, // KO [0 && __builtin_choose_expr(0, 0, 0)] = 0, // OK [0 && __builtin_choose_expr(0, 0., 0)] = 0, // OK [0 && __builtin_choose_expr(0, 0, 0.)] = 0, // KO [0 + __builtin_types_compatible_p(int, float)] = 0, // OK }; /* * check-name: constexprness in binops and alike * * check-error-start constexpr-binop.c:3:12: error: bad constant expression constexpr-binop.c:4:19: error: bad integer constant expression constexpr-binop.c:7:12: error: bad constant expression constexpr-binop.c:9:12: error: bad integer constant expression constexpr-binop.c:12:12: error: bad integer constant expression constexpr-binop.c:14:12: error: bad integer constant expression constexpr-binop.c:17:12: error: bad integer constant expression * check-error-end */ sparse-0.6.4/validation/constexpr-cast.c000066400000000000000000000012451411531012200202610ustar00rootroot00000000000000static int a[] = { [(int)0] = 0, // OK [(int)(int)0] = 0, // OK [(int)0.] = 0, // OK [(int)(int)0.] = 0, // OK [(int)__builtin_choose_expr(0, 0, 0)] = 0, // OK [(int)__builtin_choose_expr(0, 0, 0.)] = 0, // OK [(int)(float)0] = 0, // KO [(int)(float)0.] = 0, // KO [(int)(void*)0] = 0, // KO [(int)(void*)0.] = 0, // KO }; /* * check-name: constexprness in casts * * check-error-start constexpr-cast.c:9:11: error: bad integer constant expression constexpr-cast.c:10:11: error: bad integer constant expression constexpr-cast.c:12:11: error: bad integer constant expression constexpr-cast.c:13:11: error: bad integer constant expression * check-error-end */ sparse-0.6.4/validation/constexpr-compound-literal.c000066400000000000000000000010131411531012200225760ustar00rootroot00000000000000static int *a = &(int){ 1 }; // OK static int *b = &(int){ *a }; // KO static void foo(void) { int *b = &(int){ 1 }; // OK int *c = &(int){ *a }; // OK static int *d = &(int){ 1 }; // KO } /* * check-name: constexpr compound literal address * check-command: sparse -Wconstexpr-not-const $file * * check-error-start constexpr-compound-literal.c:2:25: warning: non-constant initializer for static object constexpr-compound-literal.c:8:27: warning: non-constant initializer for static object * check-error-end */ sparse-0.6.4/validation/constexpr-conditional.c000066400000000000000000000024611411531012200216330ustar00rootroot00000000000000static int a[] = { [0 ? : 0] = 0, // OK [1 ? : 0] = 0, // OK [0 ? 0 : 0] = 0, // OK [1 ? 0 : 0] = 0, // OK [0 ? 0 : __builtin_choose_expr(0, 0, 0)] = 0, // OK [1 ? __builtin_choose_expr(0, 0, 0) : 0] = 0, // OK [0 ? __builtin_choose_expr(0, 0, 0) : 0] = 0, // OK [1 ? 1 : __builtin_choose_expr(0, 0, 0)] = 0, // OK [__builtin_choose_expr(0, 0, 0) ? : 0] = 0, // OK [__builtin_choose_expr(0, 0, 1) ? : 0] = 0, // OK [0. ? : 0] = 0, // KO [0 ? 0. : 0] = 0, // KO [1 ? : 0.] = 0, // KO [__builtin_choose_expr(0, 0., 0) ? : 0] = 0, // OK [__builtin_choose_expr(0, 0, 0.) ? : 0] = 0, // KO [0 ? __builtin_choose_expr(0, 0., 0) : 0] = 0, // OK [0 ? __builtin_choose_expr(0, 0, 0.) : 0] = 0, // KO [1 ? 0 : __builtin_choose_expr(0, 0., 0)] = 0, // OK [1 ? 0 : __builtin_choose_expr(0, 0, 0.)] = 0, // KO }; /* * check-name: constexprness in conditionals * * check-error-start constexpr-conditional.c:12:13: error: bad constant expression constexpr-conditional.c:13:19: error: bad constant expression constexpr-conditional.c:14:12: error: bad constant expression constexpr-conditional.c:16:42: error: bad constant expression constexpr-conditional.c:18:48: error: bad constant expression constexpr-conditional.c:20:14: error: bad constant expression * check-error-end */ sparse-0.6.4/validation/constexpr-constcond.c000066400000000000000000000002151411531012200213150ustar00rootroot00000000000000extern int var; static int a[] = { [0 ? var : 1] = 0, [1 ? 2 : var] = 0, }; /* * check-name: constexprness in constant conditionals */ sparse-0.6.4/validation/constexpr-init.c000066400000000000000000000041261411531012200202730ustar00rootroot00000000000000static int a = 1; // OK static int b[2] = {1, 1}; // OK static void c(void) {} struct A { int a; int b[2]; }; struct B { int c; struct A d; }; static struct B d= {1, {1, {1, 1}}}; // OK static struct B e= {a, {1, {1, 1}}}; // KO static struct B f= {1, {a, {1, 1}}}; // KO static struct B g= {1, {1, {a, 1}}}; // KO static struct B h= {1, {1, {1, a}}}; // KO static struct B i= {.c = 1, .d = {.a = 1, .b = {1, 1}}}; // OK static struct B j= {.c = a, .d = {.a = 1, .b = {1, 1}}}; // KO static struct B k= {.c = 1, .d = {.a = a, .b = {1, 1}}}; // KO static struct B l= {.c = 1, .d = {.a = 1, .b = {a, 1}}}; // KO static struct B m= {.c = 1, .d = {.a = 1, .b = {1, a}}}; // KO static int n[] = {a, 1}; // KO static int o[] = {1, a}; // KO static int p[] = {[0] = a, [1] = 1}; // KO static int q[] = {[0] = 1, [1] = a}; // KO static void r(void) { int a = 0; int b = a; // OK } static void s(void) { int a = 1; static int b = a; // KO } /* * check-name: constexprness static storage object initializer * check-command: sparse -Wconstexpr-not-const $file * * check-error-start constexpr-init.c:16:21: warning: non-constant initializer for static object constexpr-init.c:17:25: warning: non-constant initializer for static object constexpr-init.c:18:29: warning: non-constant initializer for static object constexpr-init.c:19:32: warning: non-constant initializer for static object constexpr-init.c:21:26: warning: non-constant initializer for static object constexpr-init.c:22:40: warning: non-constant initializer for static object constexpr-init.c:23:49: warning: non-constant initializer for static object constexpr-init.c:24:52: warning: non-constant initializer for static object constexpr-init.c:26:19: warning: non-constant initializer for static object constexpr-init.c:27:22: warning: non-constant initializer for static object constexpr-init.c:28:25: warning: non-constant initializer for static object constexpr-init.c:29:34: warning: non-constant initializer for static object constexpr-init.c:38:24: warning: non-constant initializer for static object * check-error-end */ sparse-0.6.4/validation/constexpr-labelref.c000066400000000000000000000003241411531012200211000ustar00rootroot00000000000000static void a(void) { label1: ; static void *b = &&label1; } /* * check-name: constexprness label reference * check-command: sparse -Wconstexpr-not-const $file * * check-error-start * check-error-end */ sparse-0.6.4/validation/constexpr-offsetof.c000066400000000000000000000006521411531012200211430ustar00rootroot00000000000000struct A { int a[1]; int b; }; extern int c; static int o[] = { [__builtin_offsetof(struct A, b)] = 0, // OK [__builtin_offsetof(struct A, a[0])] = 0, // OK [__builtin_offsetof(struct A, a[0*0])] = 0, // OK [__builtin_offsetof(struct A, a[c])] = 0 // KO }; /* * check-name: constexprness __builtin_offsetof() * * check-error-start constexpr-offsetof.c:12:39: error: bad constant expression * check-error-end */ sparse-0.6.4/validation/constexpr-pointer-arith.c000066400000000000000000000016561411531012200221220ustar00rootroot00000000000000static int a = 1; static int b[2] = {1, 1}; static int *c = &b[1]; // OK static int *d = (int*)0 + 1; // OK static int *e = &b[1] + 1; // OK static int *f = b + 1; // OK static int *g = d + 1; // KO static int *h = &a + 1; // OK static int *i = &b[1] + 1; // OK static int *j = b + 1; // OK static int *k = d + 1; // KO static int *l = &*&b[1]; // OK static int *m = &*(&a + 1); // OK static int *n = &*(&b[1] + 1); // OK static int *o = &*(b + 1); // OK static int *p = &*(d + 1); // KO /* * check-name: consrexprness pointer arithmetic * check-command: sparse -Wconstexpr-not-const $file * * check-error-start constexpr-pointer-arith.c:8:19: warning: non-constant initializer for static object constexpr-pointer-arith.c:12:19: warning: non-constant initializer for static object constexpr-pointer-arith.c:17:22: warning: non-constant initializer for static object * check-error-end */ sparse-0.6.4/validation/constexpr-pointer-cast.c000066400000000000000000000005161411531012200217370ustar00rootroot00000000000000static int *a = (int*)0; // OK static int b = 0; static int *c = (int*)b; // KO /* * check-name: constexprness integer literal cast to pointer type * check-command: sparse -Wconstexpr-not-const $file * * check-error-start constexpr-pointer-cast.c:3:18: warning: non-constant initializer for static object * check-error-end */ sparse-0.6.4/validation/constexpr-preop.c000066400000000000000000000020641411531012200204540ustar00rootroot00000000000000static int a[] = { [+0] = 0, // OK [+__builtin_choose_expr(0, 0, 0)] = 0, // OK [+0.] = 0, // KO [+__builtin_choose_expr(0, 0, 0.)] = 0, // KO [-0] = 0, // OK [-__builtin_choose_expr(0, 0, 0)] = 0, // OK [-0.] = 0, // KO [-__builtin_choose_expr(0, 0, 0.)] = 0, // KO [~0] = 0, // OK [~__builtin_choose_expr(0, 0, 0)] = 0, // OK [!0] = 0, // OK [!__builtin_choose_expr(0, 0, 0)] = 0, // OK [!0.] = 0, // KO [!__builtin_choose_expr(0, 0, 0.)] = 0, // KO }; /* * check-name: constexprness in preops * * check-error-start constexpr-preop.c:4:5: error: bad constant expression constexpr-preop.c:5:33: error: bad constant expression constexpr-preop.c:8:4: error: bad constant expression constexpr-preop.c:9:4: error: bad constant expression constexpr-preop.c:14:4: error: bad integer constant expression constexpr-preop.c:15:4: error: bad integer constant expression constexpr-preop.c:10:4: error: index out of bounds in initializer constexpr-preop.c:11:4: error: index out of bounds in initializer * check-error-end */ sparse-0.6.4/validation/constexpr-pure-builtin.c000066400000000000000000000007331411531012200217470ustar00rootroot00000000000000// requires constant integer expressions static int bar[] = { [__builtin_bswap16(0x1234)] = 0, // OK [__builtin_bswap32(0x1234)] = 0, // OK [__builtin_bswap64(0x1234)] = 0, // OK }; // requires constant integers static int foo(unsigned long long a) { switch (a) { case __builtin_bswap16(1 << 8): case __builtin_bswap32(2L << 24): case __builtin_bswap64(3LL << 56): return 0; default: return 1; } } /* * check-name: constness of pure/const builtins */ sparse-0.6.4/validation/constexpr-shift.c000066400000000000000000000004031411531012200204370ustar00rootroot00000000000000#define __is_constexpr(x) \ (sizeof(int) == sizeof(*(8 ? ((void *)((long)(x) * 0l)) : (int *)8))) static void test(int x) { static int b[] = { [__builtin_choose_expr(__is_constexpr(1 << 1), 1, x)] = 0, }; } /* * check-name: constexpr-shift */ sparse-0.6.4/validation/constexpr-string.c000066400000000000000000000002641411531012200206350ustar00rootroot00000000000000static char *a = "foobar"; // OK /* * check-name: constness of string literal * check-command: sparse -Wconstexpr-not-const $file * * check-error-start * check-error-end */ sparse-0.6.4/validation/constexpr-types-compatible-p.c000066400000000000000000000002571411531012200230470ustar00rootroot00000000000000static int a[] = {[__builtin_types_compatible_p(int, int)] = 0}; /* * check-name: constness of __builtin_types_compatible_p() * * check-error-start * check-error-end */ sparse-0.6.4/validation/context-stmt.c000066400000000000000000000044301411531012200177540ustar00rootroot00000000000000static void foo(int x) { __context__(0); // OK __context__(x, 0); // OK __context__ (x, 1); // OK __context__(x); // KO: no const expr __context__(1,x); // KO: no const expr __context__; // KO: no expression at all __context__(; // KO: no expression at all __context__ 0; // KO: need parens __context__ x, 0; // KO: need parens __context__(x, 0; // KO: unmatched parens __context__ x, 0); // KO: unmatched parens __context__(0; // KO: unmatched parens __context__ 0); // KO: unmatched parens __context__(); // KO: no expression at all __context__(,0); // KO: no expression at all __context__(x,); // KO: no expression at all __context__(,); // KO: no expression at all } /* * check-name: context-stmt * check-command: sparse -Wno-context $file * * check-error-start context-stmt.c:10:20: error: Expected ( after __context__ statement context-stmt.c:10:20: error: got ; context-stmt.c:11:21: error: expression expected after '(' context-stmt.c:11:21: error: got ; context-stmt.c:11:21: error: Expected ) at end of __context__ statement context-stmt.c:11:21: error: got ; context-stmt.c:13:21: error: Expected ( after __context__ statement context-stmt.c:13:21: error: got 0 context-stmt.c:14:21: error: Expected ( after __context__ statement context-stmt.c:14:21: error: got x context-stmt.c:15:25: error: Expected ) at end of __context__ statement context-stmt.c:15:25: error: got ; context-stmt.c:16:21: error: Expected ( after __context__ statement context-stmt.c:16:21: error: got x context-stmt.c:17:22: error: Expected ) at end of __context__ statement context-stmt.c:17:22: error: got ; context-stmt.c:18:21: error: Expected ( after __context__ statement context-stmt.c:18:21: error: got 0 context-stmt.c:20:21: error: expression expected after '(' context-stmt.c:20:21: error: got ) context-stmt.c:21:21: error: expression expected after '(' context-stmt.c:21:21: error: got , context-stmt.c:22:23: error: expression expected after ',' context-stmt.c:22:23: error: got ) context-stmt.c:23:21: error: expression expected after '(' context-stmt.c:23:21: error: got , context-stmt.c:23:22: error: expression expected after ',' context-stmt.c:23:22: error: got ) context-stmt.c:7:21: error: bad constant expression context-stmt.c:8:23: error: bad constant expression * check-error-end */ sparse-0.6.4/validation/context-unreachable.c000066400000000000000000000002571411531012200212410ustar00rootroot00000000000000int fun(void); static void foo(void) { __context__(1); if (!fun()) { __builtin_unreachable(); return; } __context__(-1); } /* * check-name: context-unreachable */ sparse-0.6.4/validation/context.c000066400000000000000000000106501411531012200167700ustar00rootroot00000000000000#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.6.4/validation/crash-add-doms.c000066400000000000000000000003671411531012200200760ustar00rootroot00000000000000char 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.6.4/validation/crash-bb_target.c000066400000000000000000000002121411531012200203240ustar00rootroot00000000000000a() { &&b /* * check-name: crash bb_target * check-command: test-linearize $file * * check-error-ignore * check-output-ignore */ sparse-0.6.4/validation/crash-ep-active.c000066400000000000000000000002501411531012200202520ustar00rootroot00000000000000int a(int b) { return 0( && b; } /* * check-name: crash ep->active * check-command: test-linearize $file * * check-error-ignore * check-output-ignore */ sparse-0.6.4/validation/crash-ptrlist.c000066400000000000000000000006411411531012200201020ustar00rootroot00000000000000a; 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.6.4/validation/crash-rewrite-branch.c000066400000000000000000000004421411531012200213140ustar00rootroot00000000000000void 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.6.4/validation/crash-select.c000066400000000000000000000002471411531012200176620ustar00rootroot00000000000000struct s { void *b; long c; }; long d(void); static long f(void) { struct s s; s.c = d(); if (s.c) s.c = 2; return s.c; } /* * check-name: crash-select */ sparse-0.6.4/validation/crash-undef-in-parens.c000066400000000000000000000003351411531012200213740ustar00rootroot00000000000000void foo(void) { return (UNDEF_STUFF_IN_PARENS); } /* * check-name: crash-undef-in-parens * * check-error-start crash-undef-in-parens.c:1:26: error: undefined identifier 'UNDEF_STUFF_IN_PARENS' * check-error-end */ sparse-0.6.4/validation/crazy02-not-so.c000066400000000000000000000011261411531012200200110ustar00rootroot00000000000000int 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.6.4/validation/crazy03.c000066400000000000000000000005451411531012200166010ustar00rootroot00000000000000extern 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.6.4/validation/declaration-after-statement-ansi.c000066400000000000000000000004011411531012200236130ustar00rootroot00000000000000static 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.6.4/validation/declaration-after-statement-c89.c000066400000000000000000000004021411531012200232650ustar00rootroot00000000000000static 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.6.4/validation/declaration-after-statement-c99.c000066400000000000000000000002151411531012200232700ustar00rootroot00000000000000static void func (int i) { i; int j = i; } /* * check-name: declaration after statement (C99) * check-command: sparse -std=c99 $file */ sparse-0.6.4/validation/declaration-after-statement-default.c000066400000000000000000000002101411531012200243030ustar00rootroot00000000000000static void func (int i) { i; int j = i; } /* * check-name: declaration after statement (default) * check-command: sparse $file */ sparse-0.6.4/validation/definitions.c000066400000000000000000000002311411531012200176110ustar00rootroot00000000000000static inline int f(void); static int g(void) { return f(); } static inline int f(void) { return 0; } /* * check-name: finding definitions */ sparse-0.6.4/validation/designated-init.c000066400000000000000000000161261411531012200203600ustar00rootroot00000000000000struct 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.6.4/validation/div.c000066400000000000000000000016541411531012200160720ustar00rootroot00000000000000#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.6.4/validation/doc/000077500000000000000000000000001411531012200157035ustar00rootroot00000000000000sparse-0.6.4/validation/doc/cdoc.cdoc000066400000000000000000000061431411531012200174510ustar00rootroot00000000000000/// // Title // ----- /// // short description int a(int param, int arg); /// // short description // longer description int b(int param, int arg); /// // short description // // longer description with empty line int c(int param, int arg); /// // short description // longer description // which needs two lines int d(int param, int arg); /// // short description // // longer description with empty line // which needs two lines int e(int param, int arg); /// // condensed format // @param: desc param // @arg: desc arg // @return: desc return // longer description int f(int param, int arg); /// // more airy format // // @param: desc param // @arg: desc arg // @return: desc return // // longer description int g(int param, int arg); /// // short description // @return: ``1`` if @param is zero, // ``0`` otherwise. int h(int param, int arg); /// // short description // @return: // * ``1`` if @param is zero, // * ``0`` otherwise. int i(int param, int arg); /// // short description int m(int param, int arg) { return 0; } /// // short description int n(int param, int arg) { return 0; } /// // short description int o(int param, int arg); /// // short description int p(int param, int arg); /* * check-name: cdoc * check-command: Documentation/sphinx/cdoc.py < $file * * check-output-start 2: Title 3: ----- 4: 4: 5: 7: .. c:function:: int a(int param, int arg) 8: 6: Short description. 7: 12: .. c:function:: int b(int param, int arg) 13: 10: Short description. 11: 11: longer description 12: 18: .. c:function:: int c(int param, int arg) 19: 15: Short description. 16: 17: longer description with empty line 18: 24: .. c:function:: int d(int param, int arg) 25: 21: Short description. 22: 22: longer description 23: which needs two lines 24: 31: .. c:function:: int e(int param, int arg) 32: 27: Short description. 28: 29: longer description with empty line 30: which needs two lines 31: 39: .. c:function:: int f(int param, int arg) 40: 34: Condensed format. 35: 35: :param param: desc param 36: :param arg: desc arg 37: :return: desc return 38: 38: longer description 39: 49: .. c:function:: int g(int param, int arg) 50: 42: More airy format. 43: 44: :param param: desc param 45: :param arg: desc arg 46: :return: desc return 47: 48: longer description 49: 55: .. c:function:: int h(int param, int arg) 56: 52: Short description. 53: 53: :return: ``1`` if **param** is zero, 54: ``0`` otherwise. 54: 62: .. c:function:: int i(int param, int arg) 63: 58: Short description. 59: 59: :return: 60: * ``1`` if **param** is zero, 61: * ``0`` otherwise. 60: 66: .. c:function:: int m(int param, int arg) 67: 65: Short description. 66: 71: .. c:function:: int n(int param, int arg) 72: 70: Short description. 71: 77: .. c:function:: int o(int param, int arg) 78: 76: Short description. 77: 81: .. c:function:: int p(int param, int arg) 82: 80: Short description. 81: * check-output-end */ sparse-0.6.4/validation/double-semicolon.c000066400000000000000000000002631411531012200205430ustar00rootroot00000000000000extern 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.6.4/validation/dubious-bitwise-with-not.c000066400000000000000000000015751411531012200221770ustar00rootroot00000000000000static 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.6.4/validation/dup-defs-local.c000066400000000000000000000013311411531012200200770ustar00rootroot00000000000000extern int a, *ptr; int a = 0; int a = 1; int *ptr = &a; int *ptr = &a; static void foo(void) { int a = 0; int a = 1; int *ptr = &a; int *ptr = &a; } /* * check-name: duplicated-defs * check-known-to-fail * * check-error-start dup-defs-local.c:4:5: error: symbol 'a' has multiple initializers (originally initialized at duplicated-defs.c:3) dup-defs-local.c:7:5: error: symbol 'ptr' has multiple initializers (originally initialized at duplicated-defs.c:6) dup-defs-local.c:12:13: error: symbol 'a' has multiple initializers (originally initialized at duplicated-defs.c:11) dup-defs-local.c:15:13: error: symbol 'ptr' has multiple initializers (originally initialized at duplicated-defs.c:14) * check-error-end */ sparse-0.6.4/validation/empty-assign.c000066400000000000000000000002761411531012200177270ustar00rootroot00000000000000static int foo(int a) { a = ; // KO return a; } /* * check-name: empty-assign * * check-error-start empty-assign.c:3:11: error: expression expected before ';' * check-error-end */ sparse-0.6.4/validation/empty-char-constant.c000066400000000000000000000002451411531012200212030ustar00rootroot00000000000000static int a = ''; /* * check-name: empty-char-constant * * check-error-start empty-char-constant.c:1:16: error: empty character constant * check-error-end */ sparse-0.6.4/validation/empty-expr.c000066400000000000000000000005451411531012200174200ustar00rootroot00000000000000static int foo(void) { switch () { case 0: return 0; default: return 1; } } static int bar(void) { if () return 0; else return 1; } /* * check-name: empty expression * * check-error-start empty-expr.c:3:17: error: an expression is expected before ')' empty-expr.c:13:13: error: an expression is expected before ')' * check-error-end */ sparse-0.6.4/validation/empty-file000066400000000000000000000000001411531012200171220ustar00rootroot00000000000000sparse-0.6.4/validation/empty-initializer.c000066400000000000000000000002541411531012200207620ustar00rootroot00000000000000static int i = ; // KO /* * check-name: empty-initializer * * check-error-start empty-initializer.c:1:16: error: expression expected before ';' * check-error-end */ sparse-0.6.4/validation/endian-big.c000066400000000000000000000005151411531012200173000ustar00rootroot00000000000000#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.6.4/validation/endian-little.c000066400000000000000000000005261411531012200200360ustar00rootroot00000000000000#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.6.4/validation/enum+mode.c000066400000000000000000000006061411531012200171700ustar00rootroot00000000000000enum e { ZERO, ONE, TWO }; struct s { enum e __attribute__ ((mode(__byte__))) b; enum e __attribute__ ((mode(__word__))) w; enum e __attribute__ ((mode(__TI__))) t; }; static struct s s; _Static_assert(sizeof(s.b) == 1, ""); _Static_assert(sizeof(s.w) == sizeof(long), ""); _Static_assert(sizeof(s.t) == sizeof(long long), ""); /* * check-name: enum+mode * check-known-to-fail */ sparse-0.6.4/validation/enum-base-type.c000066400000000000000000000006311411531012200201350ustar00rootroot00000000000000enum n { NA, NB = 1L, NC = 1UL, ND = 1LL, NE = 1ULL, NF = -1, NG = -1L, NH = -1LL, }; _Static_assert(sizeof(enum n) == sizeof(int), "+-1"); enum m { MA = 0L, MB = 1L, MG = -1L, }; _Static_assert(sizeof(enum m) == sizeof(int), "+-1L"); enum p { PA = 0UL, PB = 1UL, }; _Static_assert(sizeof(enum p) == sizeof(int), "UL"); /* * check-name: enum-base-type * check-command: sparse -m64 $file */ sparse-0.6.4/validation/enum-bitwise-bad.c000066400000000000000000000007361411531012200204440ustar00rootroot00000000000000#define __bitwise __attribute__((bitwise)) #define __force __attribute__((force)) typedef int __bitwise apple_t; typedef int __bitwise orange_t; enum fruit { A = (__force apple_t) 0, B = (__force orange_t) 1, }; /* * check-name: enum-bitwise-bad * * check-error-start enum-bitwise-bad.c:9:14: error: incompatible restricted type enum-bitwise-bad.c:9:14: expected: restricted apple_t enum-bitwise-bad.c:9:14: got: restricted orange_t * check-error-end */ sparse-0.6.4/validation/enum-bitwise-mixed.c000066400000000000000000000011031411531012200210110ustar00rootroot00000000000000#define __bitwise __attribute__((bitwise)) #define __force __attribute__((force)) typedef long long __bitwise bits; enum a { AR = (__force bits) 0, AP = 0, AS = (__force bits) 1, AQ = 1, }; _Static_assert(sizeof(AP) == sizeof(int), "is bad?"); enum b { BP = 0, BR = (__force bits) 0, BQ = 1, BS = (__force bits) 1, }; _Static_assert(sizeof(BP) == sizeof(int), "is bad?"); /* * check-name: enum-bitwise-mixed * * check-error-start enum-bitwise-mixed.c:8:14: warning: mixed bitwiseness enum-bitwise-mixed.c:16:15: warning: mixed bitwiseness * check-error-end */ sparse-0.6.4/validation/enum-bitwise.c000066400000000000000000000006641411531012200177200ustar00rootroot00000000000000#define __bitwise __attribute__((bitwise)) #define __force __attribute__((force)) typedef long long __bitwise bits; enum r { RZ = (__force bits) 0, RO = (__force bits) 1, RM = (__force bits) -1, }; _Static_assert([typeof(RZ)] == [bits], "RZ"); _Static_assert([typeof(RO)] == [bits], "RO"); _Static_assert([typeof(RM)] == [bits], "RM"); _Static_assert(sizeof(enum r) == sizeof(bits), "bits"); /* * check-name: enum-bitwise */ sparse-0.6.4/validation/enum-bounds.c000066400000000000000000000007661411531012200175470ustar00rootroot00000000000000enum bound_int_max { IMAX = __INT_MAX__, }; _Static_assert([typeof(IMAX)] == [int], ""); enum bound_int_maxp1 { IMP1 = __INT_MAX__ + 1L, }; _Static_assert([typeof(IMP1)] == [unsigned int], ""); enum bound_int_maxm1 { IMM1 = -__INT_MAX__ - 1L, }; _Static_assert([typeof(IMM1)] == [int], ""); enum bound_int_maxm2 { IMM2 = -__INT_MAX__ - 2L, }; _Static_assert([typeof(IMM2)] == [long], ""); /* * check-name: enum-bounds * check-command: sparse -m64 $file * check-assert: sizeof(long) == 8 */ sparse-0.6.4/validation/enum-init-constness.c000066400000000000000000000001411411531012200212200ustar00rootroot00000000000000extern int invalid; enum e { E = 1 ? 1 : invalid }; /* * check-name: enum-init-constness */ sparse-0.6.4/validation/enum-invalid.c000066400000000000000000000003251411531012200176720ustar00rootroot00000000000000enum e { }; enum f { F = 0.1 }; /* * check-name: enum-invalid * * check-error-start enum-invalid.c:1:10: error: empty enum definition enum-invalid.c:2:14: error: bad constant expression * check-error-end */ sparse-0.6.4/validation/enum-min-size.c000066400000000000000000000013251411531012200200000ustar00rootroot00000000000000enum i { I = 1 }; _Static_assert(sizeof(enum i) == sizeof(int), "int"); enum u { U = 1U }; _Static_assert(sizeof(enum u) == sizeof(int), "uint"); enum l { L = 1L }; _Static_assert(sizeof(enum l) == sizeof(int), "long"); enum m { M = 1UL }; _Static_assert(sizeof(enum m) == sizeof(int), "ulong"); enum n { N = 1LL }; _Static_assert(sizeof(enum n) == sizeof(int), "llong"); enum o { O = 1ULL }; _Static_assert(sizeof(enum o) == sizeof(int), "ullong"); enum mi { MI = -1 }; _Static_assert(sizeof(enum i) == sizeof(int), "int"); enum ml { ML = -1L }; _Static_assert(sizeof(enum l) == sizeof(int), "long"); enum mn { MN = -1LL }; _Static_assert(sizeof(enum n) == sizeof(int), "llong"); /* * check-name: enum-min-size */ sparse-0.6.4/validation/enum-mismatch.c000066400000000000000000000005561411531012200200570ustar00rootroot00000000000000enum 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: unsigned int enum ea enum-mismatch.c:7:16: unsigned int enum eb * check-error-end */ sparse-0.6.4/validation/enum-same-type.c000066400000000000000000000004471411531012200201550ustar00rootroot00000000000000enum num { NEG = -1, NIL = 0, ONE = 1U, DUO = 2LL, }; _Static_assert([typeof(NIL)] == [typeof(NEG)], "enum same type"); _Static_assert([typeof(ONE)] == [typeof(NEG)], "enum same type"); _Static_assert([typeof(DUO)] == [typeof(NEG)], "enum same type"); /* * check-name: enum-same-type */ sparse-0.6.4/validation/enum-sign-extend.c000066400000000000000000000003241411531012200204700ustar00rootroot00000000000000enum num { a = 0x80000000, b = -1, }; _Static_assert([typeof(b)] == [long], "type"); _Static_assert(b == -1L, "value"); /* * check-name: enum-sign-extend * check-command: sparse -m64 $file */ sparse-0.6.4/validation/enum-sign-gcc.c000066400000000000000000000036761411531012200177520ustar00rootroot00000000000000// For enum's underlying/compatible type: // std C: unspecified // GCC: 'unsigned int' if no negative values, // otherwise 'int' (see GCC manul 4.9). // But also accept ulong, long // For the type of the enumerators: // std C: 'int' // GCC: 'int' if the value fit in a 'int' // otherwise same as the enum underlying type? // // The following tests match GCC's choices #define is_unsigned(X) ((typeof(X))-1 > 0) enum u { U = 1U, // fit in 'int' // no negatives }; _Static_assert(sizeof(enum u) == sizeof(int), "size"); _Static_assert(is_unsigned(enum u), "enum u"); _Static_assert(is_unsigned(U) == 0, "value U"); // fail enum v { V = __INT_MAX__ + 1U, // doesn't fit in 'int' // no negatives }; _Static_assert(sizeof(enum v) == sizeof(int), "size"); _Static_assert(is_unsigned(enum v), "enum v"); _Static_assert(is_unsigned(V) == 1, "value V"); enum w { W = __LONG_MAX__ + 1UL, // doesn't fit in 'long' }; _Static_assert(sizeof(enum w) == sizeof(long), "size"); _Static_assert(is_unsigned(enum w), "enum w"); _Static_assert(is_unsigned(W) == 1, "value W"); enum x { A = 1, // fit in 'int' B = 0x100000000UL, // doesn't fit in int }; _Static_assert(sizeof(enum x) == sizeof(long), "size"); _Static_assert(is_unsigned(enum x), "enum x"); _Static_assert(sizeof(A) == sizeof(int), "size A"); // fail _Static_assert(is_unsigned(A) == 0, "enum A"); // fail _Static_assert(sizeof(B) == sizeof(long), "size B"); _Static_assert(is_unsigned(B) == 1, "enum B"); enum y { C = 1, // fit in 'int' D = 0x100000000L, // doesn't fit in int }; _Static_assert(sizeof(enum y) == sizeof(long), "size"); _Static_assert(is_unsigned(enum y), "enum y"); _Static_assert(sizeof(C) == sizeof(int), "size C"); // fail _Static_assert(is_unsigned(C) == 0, "enum C"); // fail _Static_assert(sizeof(D) == sizeof(long), "size D"); _Static_assert(is_unsigned(D) == 1, "enum D"); /* * check-name: enum-sign-gcc * check-command: sparse -m64 $file * check-assert: sizeof(long) == 8 */ sparse-0.6.4/validation/enum-type-dubious.c000066400000000000000000000011041411531012200206710ustar00rootroot00000000000000enum foobar { FOO = (void*)0, BAR = (void*)1, BAZ = (int*)0, QUX = (int*)123, }; /* * check-name: enum-type-dubious * check-known-to-fail * * check-error-start validation/enum-type-dubious.c:2:8: error: enumerator value for 'FOO' is not an integer constant validation/enum-type-dubious.c:3:8: error: enumerator value for 'BAR' is not an integer constant validation/enum-type-dubious.c:4:8: error: enumerator value for 'BAZ' is not an integer constant validation/enum-type-dubious.c:5:8: error: enumerator value for 'QUX' is not an integer constant * check-error-end */ sparse-0.6.4/validation/enum-type-exotic.c000066400000000000000000000011261411531012200205160ustar00rootroot00000000000000enum foobar { C = (unsigned char)0, L = 1L, }; unsigned int foo(void); unsigned int foo(void) { #ifdef __CHECKER__ _Static_assert([typeof(C)] == [enum foobar], "enum type"); _Static_assert([typeof(C)] != [unsigned char], "char type"); #endif typeof(C) v = ~0; return v; } /* * check-name: enum-type-exotic * check-description: * GCC type's for C is 'int' or maybe 'unsigned int' * but certainly not 'unsigned char' like here. * check-command: test-linearize -Wno-decl $file * check-known-to-fail * * check-output-ignore * check-output-excludes: ret\\.32 *\\$255 */ sparse-0.6.4/validation/enum-typecheck.c000066400000000000000000000017201411531012200202230ustar00rootroot00000000000000enum good { G, }; enum bad { B, }; enum good g; enum good compat_int(void) { return 1; } void parg(enum good); void parg(enum bad); void farg(enum good a); void farg(enum bad a) { } enum good pret(void); enum bad pret(void); enum good fret(void); enum bad fret(void) { return 0; } enum good *ptr; enum bad *ptr; enum good *gptr = &g; enum bad *bptr = &g; /* * check-name: enum-typecheck * check-command: sparse -Wno-decl $file * check-known-to-fail * * check-error-start enum-typecheck.c:8:6: error: symbol 'parg' redeclared with different type enum-typecheck.c:11:6: error: symbol 'farg' redeclared with different type enum-typecheck.c:14:11: error: symbol 'pret' redeclared with different type enum-typecheck.c:17:11: error: symbol 'fret' redeclared with different type enum-typecheck.c:21:12: error: symbol 'ptr' redeclared with different type enum-typecheck.c:24:20: warning: incorrect type in initializer (different type sizes) * check-error-end */ sparse-0.6.4/validation/enum_scope.c000066400000000000000000000002301411531012200174320ustar00rootroot00000000000000enum {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.6.4/validation/error-at-eof.c000066400000000000000000000002771411531012200176120ustar00rootroot00000000000000/* * check-name: error-at-eof * * check-error-start error-at-eof.c:11:0: error: Expected ; at end of declaration error-at-eof.c:11:0: error: got end-of-input * check-error-end */ int a sparse-0.6.4/validation/escapes.c000066400000000000000000000023531411531012200167300ustar00rootroot00000000000000static 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.6.4/validation/eval-bad-assign1.c000066400000000000000000000005201411531012200203150ustar00rootroot00000000000000static void kos(int *r, int a) { r = ({ __builtin_types_compatible_p(int, int); }); } /* * check-name: eval-bad-assign1 * * check-error-start eval-bad-assign1.c:3:11: warning: incorrect type in assignment (different base types) eval-bad-assign1.c:3:11: expected int *r eval-bad-assign1.c:3:11: got int * check-error-end */ sparse-0.6.4/validation/eval-bad-assign2.c000066400000000000000000000005401411531012200203200ustar00rootroot00000000000000struct s { char c[1]; }; struct s fun(void); static void foo(void) { char c[1]; c = fun().c; } /* * check-name: eval-bad-assign2 * * check-error-start eval-bad-assign2.c:11:11: warning: incorrect type in assignment (invalid types) eval-bad-assign2.c:11:11: expected char c[1] eval-bad-assign2.c:11:11: got char * * check-error-end */ sparse-0.6.4/validation/eval-typeof-vla.c000066400000000000000000000005061411531012200203160ustar00rootroot00000000000000extern int a[1]; static int foo(int n) { int i = 0; int (*p)[1] = (typeof(++i, (int (*)[n])a)) &a; (void) p; return i; } /* * check-name: eval-typeof-vla * check-command: test-linearize -Wno-vla $file * check-known-to-fail * * check-output-start foo: .L0: ret.32 $1 * check-output-end */ sparse-0.6.4/validation/eval/000077500000000000000000000000001411531012200160655ustar00rootroot00000000000000sparse-0.6.4/validation/eval/addressable-complex.c000066400000000000000000000005071411531012200221510ustar00rootroot00000000000000extern void def(void *); struct s1 { int a; }; int use1(void) { struct s1 s = { 3 }; def(&s.a); return s.a; } /* * check-name: eval/addressable-complex * check-command: test-linearize -Wno-decl -fdump-ir $file * * check-output-ignore * check-output-contains: load\\. * check-output-excludes: return\\..*\\$3 */ sparse-0.6.4/validation/eval/addressable-degen.c000066400000000000000000000004251411531012200215630ustar00rootroot00000000000000extern void def(void *, unsigned int); static int bar(void) { int x[2] = { 1, 2 }; def(x, sizeof(x)); return x[1]; } /* * check-name: eval/addressable-degen * check-command: test-linearize -fdump-ir $file * * check-output-ignore * check-output-contains: load\\. */ sparse-0.6.4/validation/eval/array-quals-node.c000066400000000000000000000007321411531012200214170ustar00rootroot00000000000000struct s { int a; int b[3]; int c[2][3]; }; struct c { const struct s s; }; extern struct c v; void f(void) { v.s.a = 0; *v.s.b = 0; **v.s.c = 0; } /* * check-name: array-quals-node * check-known-to-fail * * check-error-start eval/array-quals-node.c:15:14: error: assignment to const expression eval/array-quals-node.c:16:14: error: assignment to const expression eval/array-quals-node.c:17:14: error: assignment to const expression * check-error-end */ sparse-0.6.4/validation/eval/array-quals0.c000066400000000000000000000001261411531012200205510ustar00rootroot00000000000000static int a[2][3]; static const int (*p)[3] = a; /* * check-name: array-quals0 */ sparse-0.6.4/validation/eval/array-quals1.c000066400000000000000000000042141411531012200205540ustar00rootroot00000000000000typedef const int ci_t; typedef int ia_t[2]; typedef const int cia_t[2]; static const int ci__a[2]; static ci_t cit_a[2]; static const ia_t c_iat; static cia_t ciat_; static cia_t ciata[2]; static const void *const ok_ci__a = &ci__a; static void *const ko_ci__a = &ci__a; static const void *const ok_cit_a = &cit_a; static void *const ko_cit_a = &cit_a; static const void *const ok_c_iat = &c_iat; static void *const ko_c_iat = &c_iat; static const void *const ok_ciat_ = &ciat_; static void *const ko_ciat_ = &ciat_; static const void *const ok_ciata = &ciata; static void *const ko_ciata = &ciata; static volatile int vi__a[2]; static volatile void *const ok_vi__a = &vi__a; static void *const ko_vi__a = &vi__a; /* * check-name: array-quals1 * * check-error-start eval/array-quals1.c:12:38: warning: incorrect type in initializer (different modifiers) eval/array-quals1.c:12:38: expected void *static const [toplevel] ko_ci__a eval/array-quals1.c:12:38: got int const ( * )[2] eval/array-quals1.c:14:38: warning: incorrect type in initializer (different modifiers) eval/array-quals1.c:14:38: expected void *static const [toplevel] ko_cit_a eval/array-quals1.c:14:38: got int const [usertype] ( * )[2] eval/array-quals1.c:16:38: warning: incorrect type in initializer (different modifiers) eval/array-quals1.c:16:38: expected void *static const [toplevel] ko_c_iat eval/array-quals1.c:16:38: got int const ( * )[2] eval/array-quals1.c:18:38: warning: incorrect type in initializer (different modifiers) eval/array-quals1.c:18:38: expected void *static const [toplevel] ko_ciat_ eval/array-quals1.c:18:38: got int const ( * )[2] eval/array-quals1.c:20:38: warning: incorrect type in initializer (different modifiers) eval/array-quals1.c:20:38: expected void *static const [toplevel] ko_ciata eval/array-quals1.c:20:38: got int const [usertype] ( * )[2][2] eval/array-quals1.c:24:41: warning: incorrect type in initializer (different modifiers) eval/array-quals1.c:24:41: expected void *static const [toplevel] ko_vi__a eval/array-quals1.c:24:41: got int volatile ( * )[2] * check-error-end */ sparse-0.6.4/validation/eval/asm-degen.c000066400000000000000000000007501411531012200200730ustar00rootroot00000000000000#ifdef __CHECKER__ #define __percpu __attribute__((noderef)) #else #define __percpu #endif static __percpu int var; static __percpu int arr[4]; static void foo(void) { asm("" :: "r" (var)); } static void bar(void) { asm("" :: "r" (arr)); } static void baz(void) { asm("" :: "m" (var)); } static void qux(void) { asm("" :: "m" (arr)); } /* * check-name: asm-degen * * check-error-start eval/asm-degen.c:12:24: warning: dereference of noderef expression * check-error-end */ sparse-0.6.4/validation/eval/asm-memop.c000066400000000000000000000012121411531012200201200ustar00rootroot00000000000000extern int g; void fo0(int *p) { asm volatile ("op %0" :: "p" (&g)); } void fo1(int *p) { asm volatile ("op %0" :: "m" (g)); } void fo2(int *p) { asm volatile ("op %0" :: "p" (p)); } void fo3(int *p) { asm volatile ("op %0" :: "m" (*p)); } /* * check-name: eval-asm-memop * check-command: test-linearize -Wno-decl $file * * check-output-start fo0: .L0: asm "op %0" in: "p" (g) ret fo1: .L2: asm "op %0" in: "m" (g) ret fo2: .L4: asm "op %0" in: "p" (%arg1) ret fo3: .L6: asm "op %0" in: "m" (%arg1) ret * check-output-end */ sparse-0.6.4/validation/eval/assign-restricted-ok.c000066400000000000000000000006031411531012200222710ustar00rootroot00000000000000#ifdef __CHECKER__ #define __bitwise __attribute__((bitwise)) #else #define __bitwise #endif typedef __INT16_TYPE__ __bitwise __be16; static __be16 foo(void) { __be16 val = 0; return val; } /* * check-name: assign-restricted-ok * check-command: test-linearize -fdump-ir $file * * check-output-ignore * check-output-contains: store\\.16 * check-output-excludes: store\\.32 */ sparse-0.6.4/validation/eval/not-cast-bool.c000066400000000000000000000003261411531012200207130ustar00rootroot00000000000000static _Bool foo(void) { unsigned char c = 1; _Bool b = ~c; return b; } /* * check-name: not-cast-bool * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/eval/not-cast-float.c000066400000000000000000000003271411531012200210660ustar00rootroot00000000000000static int foo(void) { int i = 123; float x = ~i; return (x < 0); } /* * check-name: eval-bool-zext-neg * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/eval/premature-examination.c000066400000000000000000000007111411531012200225460ustar00rootroot00000000000000extern int i; int foo(void) { return *i; } int bar(void) { return i[0]; } int *qux(void) { return &i[0]; } /* * check-name: premature-examination * check-command: sparse -Wno-decl $file * * check-error-start eval/premature-examination.c:5:16: error: cannot dereference this type eval/premature-examination.c:10:17: error: cannot dereference this type eval/premature-examination.c:15:18: error: cannot dereference this type * check-error-end */ sparse-0.6.4/validation/eval/typeof0.c000066400000000000000000000002611411531012200176160ustar00rootroot00000000000000static int i; static typeof(i) *ptr; /* * check-name: eval-typeof0 * check-command: test-show-type $file * * check-output-ignore * check-output-excludes: unknown type */ sparse-0.6.4/validation/eval/undecl-no-indent.c000066400000000000000000000003561411531012200214000ustar00rootroot00000000000000inline void fun(void) { undecl(); } void foo(void); void foo(void) { fun(); fun(); } /* * check-name: undecl-no-indent * * check-error-start eval/undecl-no-indent.c:3:9: error: undefined identifier 'undecl' * check-error-end */ sparse-0.6.4/validation/eval/union-cast-no.c000066400000000000000000000004771411531012200207330ustar00rootroot00000000000000union u { int i; char x[8]; }; static union u foo(int i) { return (union u)i; } static union u bar(long l) { return (union u)l; } /* * check-name: union-cast-no * check-command: sparse -Wno-union-cast $file * * check-error-start eval/union-cast-no.c:13:17: warning: cast to non-scalar * check-error-end */ sparse-0.6.4/validation/eval/union-cast.c000066400000000000000000000005521411531012200203130ustar00rootroot00000000000000union u { int i; char x[8]; }; static union u foo(int a) { return (union u)a; } static union u bar(long a) { return (union u)a; } /* * check-name: union-cast * check-command: sparse -Wunion-cast $file * * check-error-start eval/union-cast.c:8:17: warning: cast to union type eval/union-cast.c:13:17: warning: cast to non-scalar * check-error-end */ sparse-0.6.4/validation/eval/unqual-cast.c000066400000000000000000000004111411531012200204620ustar00rootroot00000000000000#define cvr const volatile restrict _Static_assert([typeof((cvr int) 0)] == [int]); _Static_assert([typeof((cvr int *) 0)] == [cvr int *]); static int *function(volatile int x) { extern typeof((typeof(x)) (x)) y; return &y; } /* * check-name: unqual-cast */ sparse-0.6.4/validation/eval/unqual-comma.c000066400000000000000000000002701411531012200206270ustar00rootroot00000000000000#define __unqual_typeof(x) typeof(((void)0, (x))) int *foo(volatile int x); int *foo(volatile int x) { extern __unqual_typeof(x) y; return &y; } /* * check-name: unqual-comma */ sparse-0.6.4/validation/eval/unqual-postop.c000066400000000000000000000012351411531012200210610ustar00rootroot00000000000000static void test_volatile(void) { volatile int x = 0; int *pp; typeof(++x) v1; pp = &v1; // KO typeof(x++) v2; pp = &v2; // KO } /* * check-name: unqual-postop * check-command: sparse -Wno-declaration-after-statement $file * check-known-to-fail * * check-error-start eval/unqual-postop.c:6:40: warning: incorrect type in assignment (different modifiers) eval/unqual-postop.c:6:40: expected int *pp eval/unqual-postop.c:6:40: got int volatile * eval/unqual-postop.c:7:40: warning: incorrect type in assignment (different modifiers) eval/unqual-postop.c:7:40: expected int *pp eval/unqual-postop.c:7:40: got int volatile * * check-error-end */ sparse-0.6.4/validation/eval/unqual-stmt-expr.c000066400000000000000000000002661411531012200215030ustar00rootroot00000000000000#define __unqual_typeof(x) typeof(({ x; })) int *foo(volatile int x); int *foo(volatile int x) { extern __unqual_typeof(x) y; return &y; } /* * check-name: unqual-stmt-expr */ sparse-0.6.4/validation/eval/unqual02.c000066400000000000000000000012161411531012200177000ustar00rootroot00000000000000static void test_const(volatile int x) { const int x = 0; typeof(1?x:x) i3; i3 = 0; // should be OK typeof(+x) i4; i4 = 0; // should be OK typeof(-x) i5; i5 = 0; // should be OK typeof(!x) i6; i6 = 0; // should be OK typeof(x+x) i7; i7 = 0; // should be OK } static void test_volatile(void) { volatile int x = 0; int *pp; typeof(1?x:x) i3; pp = &i3; // should be OK typeof(+x) i4; pp = &i4; // should be OK typeof(-x) i5; pp = &i5; // should be OK typeof(!x) i6; pp = &i6; // should be OK typeof(x+x) i7; pp = &i7; // should be OK } /* * check-name: unqual02 * check-command: sparse -Wno-declaration-after-statement $file */ sparse-0.6.4/validation/expand/000077500000000000000000000000001411531012200164155ustar00rootroot00000000000000sparse-0.6.4/validation/expand/asm0.c000066400000000000000000000004641411531012200174250ustar00rootroot00000000000000static void foo(void) { asm("" :: "i" (42 & 3)); asm("" :: "i" (__builtin_constant_p(0))); } /* * check-name: expand-asm0 * check-command: test-linearize $file * * check-output-start foo: .L0: asm "" in: "i" ($2) asm "" in: "i" ($1) ret * check-output-end */ sparse-0.6.4/validation/expand/bad-shift.c000066400000000000000000000017021411531012200204220ustar00rootroot00000000000000#define MAX (sizeof(int) * __CHAR_BIT__) static int lmax(int a) { return 1 << MAX; } static int lneg(int a) { return 1 << -1; } static int rmax(int a) { return 1 >> MAX; } static int rneg(int a) { return 1 >> -1; } /* * check-name: bad-shift * check-command: test-linearize -Wno-decl $file * * check-output-start lmax: .L0: shl.32 %r1 <- $1, $32 ret.32 %r1 lneg: .L2: shl.32 %r3 <- $1, $0xffffffff ret.32 %r3 rmax: .L4: asr.32 %r5 <- $1, $32 ret.32 %r5 rneg: .L6: asr.32 %r7 <- $1, $0xffffffff ret.32 %r7 * check-output-end * * check-error-start expand/bad-shift.c:5:21: warning: shift too big (32) for type int expand/bad-shift.c:10:21: warning: shift count is negative (-1) expand/bad-shift.c:15:21: warning: shift too big (32) for type int expand/bad-shift.c:20:21: warning: shift count is negative (-1) * check-error-end */ sparse-0.6.4/validation/expand/builtin-expect.c000066400000000000000000000032621411531012200215200ustar00rootroot00000000000000int flia(long a) { return __builtin_expect(a, 1); } int flic(void) { return __builtin_expect(1L << 32 | 1, 1); } long fila(int a) { return __builtin_expect(a, 1); } long filc(void) { return __builtin_expect(1L << 32 | 1, 1); } long filu(void) { return __builtin_expect(0x80000000U, 1); } long fils(void) { return __builtin_expect((int)0x80000000, 1); } void *fptr(void *a) { return __builtin_expect(a, a); } /* * check-name: builtin-expect * check-command: test-linearize -m64 -Wno-decl $file * check-assert: sizeof(long) == 8 * * check-output-start flia: .L0: trunc.32 %r2 <- (64) %arg1 ret.32 %r2 flic: .L2: ret.32 $1 fila: .L4: sext.64 %r6 <- (32) %arg1 ret.64 %r6 filc: .L6: ret.64 $0x100000001 filu: .L8: ret.64 $0x80000000 fils: .L10: ret.64 $0xffffffff80000000 fptr: .L12: ret.64 %arg1 * check-output-end * * check-error-start expand/builtin-expect.c:33:33: warning: incorrect type in argument 1 (different base types) expand/builtin-expect.c:33:33: expected long expand/builtin-expect.c:33:33: got void *a expand/builtin-expect.c:33:36: warning: incorrect type in argument 2 (different base types) expand/builtin-expect.c:33:36: expected long expand/builtin-expect.c:33:36: got void *a expand/builtin-expect.c:33:32: warning: incorrect type in return expression (different base types) expand/builtin-expect.c:33:32: expected void * expand/builtin-expect.c:33:32: got long expand/builtin-expect.c:8:42: warning: cast truncates bits from constant value (100000001 becomes 1) * check-error-end */ sparse-0.6.4/validation/expand/builtin_constant_inline0.c000066400000000000000000000005361411531012200235620ustar00rootroot00000000000000static inline int is_const(long size) { return __builtin_constant_p(size) ? size : 0; } int foo(void) { return is_const(42); } /* * check-name: builtin_constant_inline0 * check-command: test-linearize -Wno-decl $file * * check-output-start foo: .L0: # call %r1 <- is_const, $42 ret.32 $42 * check-output-end */ sparse-0.6.4/validation/expand/builtin_constant_inline1.c000066400000000000000000000005631411531012200235630ustar00rootroot00000000000000static inline void fun(void) { } #define EXPR ({ fun(); 42; }) int bar(void) { // GCC doesn't consider EXPR as a constant return __builtin_constant_p(EXPR); } /* * check-name: builtin_constant_inline1 * check-command: test-linearize -Wno-decl $file * check-known-to-fail * * check-output-start bar: .L0: ret.32 $0 * check-output-end */ sparse-0.6.4/validation/expand/builtin_fpclassify.c000066400000000000000000000010351411531012200224510ustar00rootroot00000000000000enum { FP_NAN, FP_INF, FP_NOR, FP_SUB, FP_ZERO }; #define classify(X) __builtin_fpclassify(FP_NAN,FP_INF,FP_NOR,FP_SUB,FP_ZERO,X) int test(void) { if (classify(__builtin_nan("0")) != FP_NAN) return 0; if (classify(__builtin_inf("0")) != FP_INF) return 0; if (classify(1.0) != FP_NOR) return 0; if (classify(0.0) != FP_ZERO) return 0; return 1; } /* * check-name: builtin_fpclassify * check-command: test-linearize -Wno-decl $file * check-known-to-fail * * check-output-ignore * check-output-contains: ret\\..*\\$1 */ sparse-0.6.4/validation/expand/builtin_huge_val.c000066400000000000000000000010421411531012200220760ustar00rootroot00000000000000static float huge_valf(void) { return __builtin_huge_valf(); } static double huge_val(void) { return __builtin_huge_val(); } static long double huge_vall(void) { return __builtin_huge_vall(); } static float inff(void) { return __builtin_inff(); } static double inf(void) { return __builtin_inf(); } static long double infl(void) { return __builtin_infl(); } /* * check-name: builtin_huge_val expand * check-command: test-linearize -Wno-decl $file * check-known-to-fail * * check-output-ignore * check-output-excludes: call */ sparse-0.6.4/validation/expand/builtin_isdigit.c000066400000000000000000000003611411531012200217430ustar00rootroot00000000000000_Static_assert(__builtin_isdigit('0')); _Static_assert(__builtin_isdigit('9')); _Static_assert(!__builtin_isdigit(0)); _Static_assert(!__builtin_isdigit(' ')); _Static_assert(!__builtin_isdigit('z')); /* * check-name: builtin_isdigit */ sparse-0.6.4/validation/expand/builtin_isinf.c000066400000000000000000000005621411531012200214220ustar00rootroot00000000000000int test(void) { if (!__builtin_isinf(__builtin_inff())) return 0; if (!__builtin_isinf(__builtin_inf())) return 0; if (!__builtin_isinf(__builtin_infl())) return 0; return 1; } /* * check-name: builtin_isinf expand * check-command: test-linearize -Wno-decl $file * check-known-to-fail * * check-output-ignore * check-output-contains: ret\\..*\\$1 */ sparse-0.6.4/validation/expand/builtin_isnan.c000066400000000000000000000005731411531012200214240ustar00rootroot00000000000000int test(void) { if (!__builtin_isnan(__builtin_nanf("0"))) return 0; if (!__builtin_isnan(__builtin_nan("0"))) return 0; if (!__builtin_isnan(__builtin_nanl("0"))) return 0; return 1; } /* * check-name: builtin_isnan expand * check-command: test-linearize -Wno-decl $file * check-known-to-fail * * check-output-ignore * check-output-contains: ret\\..*\\$1 */ sparse-0.6.4/validation/expand/builtin_isnormal.c000066400000000000000000000005321411531012200221330ustar00rootroot00000000000000int test(void) { if (!__builtin_isnormal(1.0F)) return 0; if (!__builtin_isnormal(1.0)) return 0; if (!__builtin_isnormal(1.0L)) return 0; return 1; } /* * check-name: builtin_isnormal expand * check-command: test-linearize -Wno-decl $file * check-known-to-fail * * check-output-ignore * check-output-contains: ret\\..*\\$1 */ sparse-0.6.4/validation/expand/builtin_nan.c000066400000000000000000000005351411531012200210660ustar00rootroot00000000000000static float nanf(void) { return __builtin_nanf("0"); } static double nan(void) { return __builtin_nan("0"); } static long double nanl(void) { return __builtin_nanl("0"); } /* * check-name: builtin_nan expand * check-command: test-linearize -Wno-decl $file * check-known-to-fail * * check-output-ignore * check-output-excludes: call */ sparse-0.6.4/validation/expand/compound-literal.c000066400000000000000000000005371411531012200220440ustar00rootroot00000000000000#define SAME_TYPE(A, B) \ __builtin_types_compatible_p(A, B) struct s { int i; }; static void foo(struct s *p) { *p = (struct s) { .i = SAME_TYPE(int, int), }; } /* * check-name: compound-literal * check-command: test-linearize $file * * check-output-start foo: .L0: store.32 $1 -> 0[%arg1] ret * check-output-end */ sparse-0.6.4/validation/expand/constant-init-array.c000066400000000000000000000004441411531012200224710ustar00rootroot00000000000000int test_array(int i) { static const int a[3] = { 1, 2, 3, }; return a[1]; } /* * check-name: constant-init-array * check-command: test-linearize -Wno-decl -fdump-ir $file * * check-output-ignore * check-output-excludes: phisrc\\..*return.*\\$2 * check-output-contains: load\\. */ sparse-0.6.4/validation/expand/constant-init-nested-array.c000066400000000000000000000004631411531012200237520ustar00rootroot00000000000000int foo(void) { int a[2][3] = {{0, 1, 2},{3, 4, 5}}; return a[1][2]; } /* * check-name: constant-init-nested-array * check-command: test-linearize -Wno-decl -fdump-ir $file * check-known-to-fail * * check-output-ignore * check-output-contains: phisrc\\..*\\$5 * check-output-excludes: load\\. */ sparse-0.6.4/validation/expand/constant-init-nested-struct.c000066400000000000000000000005401411531012200241540ustar00rootroot00000000000000struct s { int a; struct { int b; int c; } s; }; int foo(void) { struct s s = {1, {2, 3}}; return s.s.c; } /* * check-name: constant-init-nested-struct * check-command: test-linearize -Wno-decl -fdump-ir $file * check-known-to-fail * * check-output-ignore * check-output-contains: phisrc\\..*\\$3 * check-output-excludes: load\\. */ sparse-0.6.4/validation/expand/constant-init-string.c000066400000000000000000000004611411531012200226600ustar00rootroot00000000000000char foo(void) { static const char s[] = "abc?"; return s[3]; } /* * check-name: constant-init-nested-array * check-command: test-linearize -Wno-decl -fdump-ir $file * check-known-to-fail * * check-output-ignore * check-output-contains: phisrc\\..*\\$63 * check-output-pattern(0,1): load\\. */ sparse-0.6.4/validation/expand/constant-union-flt2int.c000066400000000000000000000005561411531012200231260ustar00rootroot00000000000000union u { int i; float f; }; static int foo(void) { union u u = { .f = 0.123 }; return u.i; } /* * check-name: constant-union-float-to-int * check description: must not infer the int value from the float * check-command: test-linearize -fdump-ir $file * * check-output-ignore * check-output-pattern(1): setfval\\. * check-output-pattern(1): load\\. */ sparse-0.6.4/validation/expand/constant-union-int2flt.c000066400000000000000000000005051411531012200231200ustar00rootroot00000000000000union u { int i; float f; }; static float foo(void) { union u u = { .i = 3 }; return u.f; } /* * check-name: constant-union-int-to-float * check description: must not infer the float value from the int * check-command: test-linearize -fdump-ir $file * * check-output-ignore * check-output-pattern(1): load\\. */ sparse-0.6.4/validation/expand/constant-union-size.c000066400000000000000000000005421411531012200225110ustar00rootroot00000000000000union u { char c; float f; }; static int foo(void) { union u u = { .f = 0.123 }; return u.c; } /* * check-name: constant-union-size * check description: the size of the initializer doesn't match * check-command: test-linearize -fdump-ir $file * * check-output-ignore * check-output-contains: load\\. * check-output-excludes: ret\\..*\\$ */ sparse-0.6.4/validation/expand/cost-deref-nested.c000066400000000000000000000004101411531012200220670ustar00rootroot00000000000000struct s { struct { int u, v; } a, b; }; static const struct s s; static int foo(int c) { return c && s.b.v; } /* * check-name: cost-deref-nested * check-command: test-linearize -fdump-ir $file * * check-output-ignore * check-output-excludes: cbr */ sparse-0.6.4/validation/expand/default-init-array.c000066400000000000000000000005031411531012200222600ustar00rootroot00000000000000int test_array(int i) { static const int a[3] = { [0] = 1, [2] = 3, }; return a[1]; } /* * check-name: default-init-array * check-command: test-linearize -Wno-decl -fdump-ir $file * check-known-to-fail * * check-output-ignore * check-output-contains: phisrc\\..*return.*\\$0 * check-output-excludes: load\\. */ sparse-0.6.4/validation/expand/default-init-struct.c000066400000000000000000000005071411531012200224720ustar00rootroot00000000000000struct s { int a; int b; int c; }; int test_struct(void) { struct s s = { .a = 1, .c = 3, }; return s.b; } /* * check-name: default-init-struct * check-command: test-linearize -Wno-decl -fdump-ir $file * * check-output-ignore * check-output-contains: phisrc\\..*return.*\\$0 * check-output-excludes: load\\. */ sparse-0.6.4/validation/expand/function-pointer.c000066400000000000000000000004571411531012200220720ustar00rootroot00000000000000struct s { int (*fun)(void); }; inline struct s *inl(struct s *p) { 1 + 0; return p; } static void tst(struct s *s) { inl(s)->fun(); } /* * check-name: function-pointer * check-command: test-linearize -fdump-ir $file * * check-output-ignore * check-output-excludes: add\\.32.*\\$1, \\$0 */ sparse-0.6.4/validation/expand/union-cast.c000066400000000000000000000006751411531012200206510ustar00rootroot00000000000000union u { int i; struct s { int a; } s; }; int foo(void) { struct s s = { 3 }; union u u = (union u)s; return u.s.a; } /* * check-name: union-cast * check-command: test-linearize -Wno-decl -fdump-ir $file * check-known-to-fail * * check-output-ignore * check-output-excludes: load\\. * * check-error-start union-cast.c:11:22: warning: cast to non-scalar union-cast.c:11:22: warning: cast from non-scalar * check-error-end */ sparse-0.6.4/validation/extern-array.c000066400000000000000000000003711411531012200177240ustar00rootroot00000000000000extern 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.6.4/validation/extern-inline.c000066400000000000000000000006071411531012200200660ustar00rootroot00000000000000extern __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.6.4/validation/fdiag-prefix.c000066400000000000000000000003751411531012200176540ustar00rootroot00000000000000int a. /* * check-name: fdiag-prefix * check-command: sparse -fdiagnostic-prefix=prefix $file * * check-error-start fdiag-prefix.c:1:6: prefix: error: Expected ; at end of declaration fdiag-prefix.c:1:6: prefix: error: got . * check-error-end */ sparse-0.6.4/validation/field-overlap.c000066400000000000000000000003351411531012200200340ustar00rootroot00000000000000static 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.6.4/validation/field-override.c000066400000000000000000000052131411531012200202030ustar00rootroot00000000000000static 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.6.4/validation/flex-array-align.c000066400000000000000000000005321411531012200204440ustar00rootroot00000000000000struct s { __INT32_TYPE__ x; __INT16_TYPE__ y; unsigned char f[]; }; static int foo(struct s *s) { return (sizeof(*s) << 16) | __builtin_offsetof(typeof(*s), f); } /* * check-name: flex-array-align * check-command: test-linearize -Wno-flexible-array-sizeof $file * * check-output-ignore * check-output-contains: ret\\..*\\$0x80006 */ sparse-0.6.4/validation/flex-array-array.c000066400000000000000000000004001411531012200204620ustar00rootroot00000000000000struct s { int i; long f[]; }; static struct s a[2]; /* * check-name: flex-array-array * check-command: sparse -Wflexible-array-array $file * * check-error-start flex-array-array.c:6:18: warning: array of flexible structures * check-error-end */ sparse-0.6.4/validation/flex-array-error.c000066400000000000000000000007331411531012200205060ustar00rootroot00000000000000struct s { int i; long f[]; int j; }; union u { int i; long f[]; }; // trigger the examination of the offending types static int foo(struct s *s, union u *u) { return __builtin_offsetof(typeof(*s), i) + __builtin_offsetof(typeof(*u), i); } /* * check-name: flex-array-error * * check-error-start flex-array-error.c:3:14: error: flexible array member 'f' is not last flex-array-error.c:9:14: error: flexible array member 'f' in a union * check-error-end */ sparse-0.6.4/validation/flex-array-nested.c000066400000000000000000000010111411531012200206250ustar00rootroot00000000000000struct f { int i; long f[]; }; struct s { struct f f; }; union u { struct f f; }; // trigger the examination of the offending types static int foo(struct s *s, union u *u) { return __builtin_offsetof(typeof(*s), f) + __builtin_offsetof(typeof(*u), f); } /* * check-name: flex-array-nested * check-command: sparse -Wflexible-array-nested $file * * check-error-start flex-array-nested.c:7:18: warning: nested flexible array flex-array-nested.c:11:18: warning: nested flexible array * check-error-end */ sparse-0.6.4/validation/flex-array-sizeof.c000066400000000000000000000004511411531012200206510ustar00rootroot00000000000000struct s { int i; long f[]; }; static int foo(struct s *s) { return sizeof(*s); } /* * check-name: flex-array-sizeof * check-command: sparse -Wflexible-array-sizeof $file * * check-error-start flex-array-sizeof.c:8:16: warning: using sizeof on a flexible structure * check-error-end */ sparse-0.6.4/validation/flex-array-union-array-no.c000066400000000000000000000003121411531012200222240ustar00rootroot00000000000000#include "flex-array-union-array.h" /* * check-name: flex-array-union-no * check-command: sparse -Wflexible-array-array -Wno-flexible-array-union $file * * check-error-start * check-error-end */ sparse-0.6.4/validation/flex-array-union-array-yes.c000066400000000000000000000005041411531012200224130ustar00rootroot00000000000000#include "flex-array-union-array.h" /* * check-name: flex-array-union-yes * check-command: sparse -Wflexible-array-array -Wflexible-array-union $file * * check-error-start flex-array-union-array-yes.c: note: in included file: flex-array-union-array.h:11:17: warning: array of flexible structures * check-error-end */ sparse-0.6.4/validation/flex-array-union-array.h000066400000000000000000000001571411531012200216260ustar00rootroot00000000000000struct s_flex { int i; long f[]; }; union s { struct s_flex flex; char buf[200]; }; static union s a[2]; sparse-0.6.4/validation/fored_arg.c000066400000000000000000000003731411531012200172350ustar00rootroot00000000000000/* * 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.6.4/validation/foul-bitwise.c000066400000000000000000000013031411531012200177100ustar00rootroot00000000000000typedef 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.6.4/validation/foul-scalar.c000066400000000000000000000002441411531012200175120ustar00rootroot00000000000000#define __bitwise __attribute__((bitwise)) typedef unsigned short __bitwise __be16; static void foo(__be16 x) { if (~x) ; } /* * check-name: foul-scalar */ sparse-0.6.4/validation/fp-ops.c000066400000000000000000000016431411531012200165120ustar00rootroot00000000000000double fadd(double x, double y) { return x + y; } double fsub(double x, double y) { return x - y; } double fmul(double x, double y) { return x * y; } double fdiv(double x, double y) { return x / y; } double fneg(double x) { return -x; } _Bool ftst(double x) { return !x; } /* * check-name: floating-point ops * check-command: test-linearize -Wno-decl $file * check-output-start fadd: .L0: fadd.64 %r3 <- %arg1, %arg2 ret.64 %r3 fsub: .L2: fsub.64 %r7 <- %arg1, %arg2 ret.64 %r7 fmul: .L4: fmul.64 %r11 <- %arg1, %arg2 ret.64 %r11 fdiv: .L6: fdiv.64 %r15 <- %arg1, %arg2 ret.64 %r15 fneg: .L8: fneg.64 %r18 <- %arg1 ret.64 %r18 ftst: .L10: setfval.64 %r21 <- 0.000000e+00 fcmpoeq.1 %r23 <- %arg1, %r21 ret.1 %r23 * check-output-end */ sparse-0.6.4/validation/function-attribute-inner.c000066400000000000000000000003011411531012200222330ustar00rootroot00000000000000#define __noreturn __attribute__((__noreturn__)) void __noreturn fun(void); _Static_assert([void (__noreturn *)(void)] == [typeof(&fun)], ""); /* * check-name: function-attribute-inner */ sparse-0.6.4/validation/function-attribute-pointer.c000066400000000000000000000017501411531012200226110ustar00rootroot00000000000000#define __noreturn __attribute__((__noreturn__)) void set_die(void (*)(void)); void set_die_nr(__noreturn void (*)(void)); void die(void); void __noreturn die_nr(void); static void foo(void) { set_die(die); set_die(die_nr); set_die_nr(die_nr); set_die_nr(die); void (*fptr0)(void) = die; void (*fptr1)(void) = die_nr; __noreturn void (*fptr3)(void) = die_nr; __noreturn void (*fptr2)(void) = die; } /* * check-name: function-attribute-pointer * * check-error-start function-attribute-pointer.c:14:20: warning: incorrect type in argument 1 (different modifiers) function-attribute-pointer.c:14:20: expected void ( [noreturn] * )( ... ) function-attribute-pointer.c:14:20: got void ( * )( ... ) function-attribute-pointer.c:19:42: warning: incorrect type in initializer (different modifiers) function-attribute-pointer.c:19:42: expected void ( [noreturn] *fptr2 )( ... ) function-attribute-pointer.c:19:42: got void ( * )( ... ) * check-error-end */ sparse-0.6.4/validation/function-attribute-void-ptr.c000066400000000000000000000003121411531012200226660ustar00rootroot00000000000000#define __noreturn __attribute__((__noreturn__)) void fun(void *); void __noreturn die(void); static void foo(void) { void *ptr = die; fun(die); } /* * check-name: function-attribute-void-ptr */ sparse-0.6.4/validation/function-attribute.c000066400000000000000000000004141411531012200211270ustar00rootroot00000000000000#define __pure __attribute__((pure)) static __pure int funi(int val) { return val; } static __pure int *funp(int *ptr) { return ptr; } static void foo(int val, int *ptr) { int nbr = funi(val); int *res = funp(ptr); } /* * check-name: function-attribute */ sparse-0.6.4/validation/function-pointer-inheritance.c000066400000000000000000000002111411531012200230660ustar00rootroot00000000000000extern int foo(int f(int, void *)); int foo(int (*f)(int, void *)) { return 0; } /* * check-name: Function pointer inheritance */ sparse-0.6.4/validation/function-pointer-type.c000066400000000000000000000006431411531012200215670ustar00rootroot00000000000000extern int fun(void); void fa(void) { int (*f)(void); f = &fun; } void f0(void) { int (*f)(void); f = fun; } // C99,C11 6.3.2.1p4 void f1(void) { int (*f)(void); f = *fun; } // C99,C11 6.5.3.2p4 void f2(void) { int (*f)(void); f = **fun; } // C99,C11 6.5.3.2p4 void f3(void) { int (*f)(void); f = ***fun; } // C99,C11 6.5.3.2p4 /* * check-name: type of function pointers * check-command: sparse -Wno-decl $file */ sparse-0.6.4/validation/function-redecl-funattr.c000066400000000000000000000003551411531012200220470ustar00rootroot00000000000000#define __pure __attribute__((pure)) #define __noreturn __attribute__((noreturn)) int __pure p(int i); int p(int i) { return i; } void __noreturn n(void); void n(void) { while (1) ; } /* * check-name: function-redecl-funattr */ sparse-0.6.4/validation/function-redecl.c000066400000000000000000000104731411531012200203700ustar00rootroot00000000000000#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 (different base types): function-redecl.c:5:6: void extern [addressable] [toplevel] ret_type( ... ) function-redecl.c:4:5: note: previously declared as: function-redecl.c:4:5: int extern [addressable] [signed] [toplevel] ret_type( ... ) function-redecl.c:9:11: error: symbol 'ret_const' redeclared with different type (different modifiers): function-redecl.c:9:11: int extern const [addressable] [signed] [toplevel] ret_const( ... ) function-redecl.c:8:5: note: previously declared as: function-redecl.c:8:5: int extern [addressable] [signed] [toplevel] ret_const( ... ) function-redecl.c:13:13: error: symbol 'ret_as' redeclared with different type (different address spaces): function-redecl.c:13:13: void *extern [addressable] [toplevel] ret_as( ... ) function-redecl.c:12:6: note: previously declared as: function-redecl.c:12:6: void *extern [addressable] [toplevel] ret_as( ... ) function-redecl.c:17:12: error: symbol 'ret_mod' redeclared with different type (different modifiers): function-redecl.c:17:12: void const *extern [addressable] [toplevel] ret_mod( ... ) function-redecl.c:16:6: note: previously declared as: function-redecl.c:16:6: void *extern [addressable] [toplevel] ret_mod( ... ) function-redecl.c:21:6: error: symbol 'arg_type' redeclared with different type (incompatible argument 1 (different base types)): function-redecl.c:21:6: void extern [addressable] [toplevel] arg_type( ... ) function-redecl.c:20:6: note: previously declared as: function-redecl.c:20:6: void extern [addressable] [toplevel] arg_type( ... ) function-redecl.c:29:6: error: symbol 'arg_as' redeclared with different type (incompatible argument 1 (different address spaces)): function-redecl.c:29:6: void extern [addressable] [toplevel] arg_as( ... ) function-redecl.c:28:6: note: previously declared as: function-redecl.c:28:6: void extern [addressable] [toplevel] arg_as( ... ) function-redecl.c:33:6: error: symbol 'arg_mod' redeclared with different type (incompatible argument 1 (different modifiers)): function-redecl.c:33:6: void extern [addressable] [toplevel] arg_mod( ... ) function-redecl.c:32:6: note: previously declared as: function-redecl.c:32:6: void extern [addressable] [toplevel] arg_mod( ... ) function-redecl.c:37:6: error: symbol 'arg_more_arg' redeclared with different type (different argument counts): function-redecl.c:37:6: void extern [addressable] [toplevel] arg_more_arg( ... ) function-redecl.c:36:6: note: previously declared as: function-redecl.c:36:6: void extern [addressable] [toplevel] arg_more_arg( ... ) function-redecl.c:41:6: error: symbol 'arg_less_arg' redeclared with different type (different argument counts): function-redecl.c:41:6: void extern [addressable] [toplevel] arg_less_arg( ... ) function-redecl.c:40:6: note: previously declared as: function-redecl.c:40:6: void extern [addressable] [toplevel] arg_less_arg( ... ) function-redecl.c:45:6: error: symbol 'arg_vararg' redeclared with different type (incompatible variadic arguments): function-redecl.c:45:6: void extern [addressable] [toplevel] arg_vararg( ... ) function-redecl.c:44:6: note: previously declared as: function-redecl.c:44:6: void extern [addressable] [toplevel] arg_vararg( ... ) * check-error-end */ sparse-0.6.4/validation/function-redecl2.c000066400000000000000000000006051411531012200204460ustar00rootroot00000000000000extern void exit (int __status) __attribute__ ((__noreturn__)); int func0(int a) __attribute__ ((pure)); __attribute__ ((pure)) int func0(int a) { return 0; } __attribute__ ((noreturn)) void func1(int a); void func1(int a) { exit(0); } void func2(int a) __attribute__ ((noreturn)); __attribute__ ((noreturn)) void func2(int a) { exit(0); } /* * check-name: function-redecl2 */ sparse-0.6.4/validation/generic-bad0.c000066400000000000000000000015651411531012200175310ustar00rootroot00000000000000struct s; void foo(int n) { _Generic(n, default: 1, default: 2); _Generic(n, int[n]:0, default:1); _Generic(n, struct s:0, default:1); _Generic(n, void:0, default:1); _Generic(n, void (void):0, default:1); _Generic(&n, int:5, signed int:7, default:23); _Generic(n, void *:5); } /* * check-name: generic-bad0 * * check-error-start generic-bad0.c:5:33: warning: multiple default in generic expression generic-bad0.c:5:30: note: previous was here generic-bad0.c:6:25: warning: Variable length array is used. generic-bad0.c:6:21: error: variable length array type in generic selection generic-bad0.c:7:21: error: incomplete type in generic selection generic-bad0.c:8:21: error: incomplete type in generic selection generic-bad0.c:9:21: error: function type in generic selection generic-bad0.c:11:17: error: no generic selection for 'int [addressable] n' * check-error-end */ sparse-0.6.4/validation/generic-dr481.c000066400000000000000000000012201411531012200175510ustar00rootroot00000000000000static char const* a = _Generic("bla", char*: "blu"); static char const* b = _Generic("bla", char[4]: "blu"); static char const* c = _Generic((int const){ 0 }, int: "blu"); static char const* d = _Generic((int const){ 0 }, int const: "blu"); static char const* e = _Generic(+(int const){ 0 }, int: "blu"); static char const* f = _Generic(+(int const){ 0 }, int const: "blu"); /* * check-name: generic-dr481 * * check-error-start generic-dr481.c:2:32: error: no generic selection for 'char *' generic-dr481.c:4:32: error: no generic selection for 'int const [toplevel]' generic-dr481.c:6:32: error: no generic selection for 'int' * check-error-end */ sparse-0.6.4/validation/generic-functions.c000066400000000000000000000011101411531012200207150ustar00rootroot00000000000000void funf(float); void fund(double); void funl(long double); #define fung(X) _Generic(X, \ float: funf, \ default: fund, \ long double: funl) (X) #define TEST(name, T) \ static void test ## name(T a) { return fung(a); } TEST(f, float) TEST(d, double) TEST(l, long double) /* * check-name: generic-functions * check-command: test-linearize $file * * check-output-start testf: .L0: call funf, %arg1 ret testd: .L2: call fund, %arg1 ret testl: .L4: call funl, %arg1 ret * check-output-end */ sparse-0.6.4/validation/generic-schar.c000066400000000000000000000012141411531012200200120ustar00rootroot00000000000000#define typename(x) _Generic((x) 0, \ char: "char", \ signed char: "signed char", \ unsigned char: "unsigned char", \ default: "???") #define TEST(name, x) \ static const char *test_ ## name(void) { return typename(x); } TEST(char, char) TEST(schar, signed char) TEST(uchar, unsigned char) /* * check-name: generic-schar * check-command: test-linearize --arch=i386 -fsigned-char $file * check-known-to-fail * * check-output-start test_char: .L0: ret.32 "char" test_schar: .L2: ret.32 "signed char" test_uchar: .L4: ret.32 "unsigned char" * check-output-end */ sparse-0.6.4/validation/generic-typename.c000066400000000000000000000042511411531012200205400ustar00rootroot00000000000000#define typename(x) _Generic((x) 0, \ _Bool: "_Bool", \ char: "char", \ unsigned char: "unsigned char", \ short: "short", \ unsigned short: "unsigned short", \ int: "int", \ unsigned int: "unsigned int", \ long: "long", \ unsigned long: "unsigned long", \ long long: "long long", \ unsigned long long: "unsigned long long", \ float: "float", \ double: "double", \ long double: "long double", \ void *: "void *", \ char *: "char *", \ int *: "int *", \ default: "???") #define TEST(name, x) \ static const char *test_ ## name(void) { return typename(x); } TEST(bool, _Bool) TEST(char, char) TEST(uchar, unsigned char) TEST(short, short) TEST(ushort, unsigned short) TEST(int, int) TEST(uint, unsigned int) TEST(long, long) TEST(ulong, unsigned long) TEST(llong, long long) TEST(ullong, unsigned long long) TEST(float, float) TEST(double, double) TEST(ldouble, long double) TEST(vptr, void *) TEST(cptr, char *) TEST(iptr, int *) TEST(int128, __int128) /* * check-name: generic-typename * check-command: test-linearize --arch=i386 -fsigned-char $file * * check-output-start test_bool: .L0: ret.32 "_Bool" test_char: .L2: ret.32 "char" test_uchar: .L4: ret.32 "unsigned char" test_short: .L6: ret.32 "short" test_ushort: .L8: ret.32 "unsigned short" test_int: .L10: ret.32 "int" test_uint: .L12: ret.32 "unsigned int" test_long: .L14: ret.32 "long" test_ulong: .L16: ret.32 "unsigned long" test_llong: .L18: ret.32 "long long" test_ullong: .L20: ret.32 "unsigned long long" test_float: .L22: ret.32 "float" test_double: .L24: ret.32 "double" test_ldouble: .L26: ret.32 "long double" test_vptr: .L28: ret.32 "void *" test_cptr: .L30: ret.32 "char *" test_iptr: .L32: ret.32 "int *" test_int128: .L34: ret.32 "???" * check-output-end */ sparse-0.6.4/validation/goto-label.c000066400000000000000000000004601411531012200173270ustar00rootroot00000000000000void 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.6.4/validation/goto-reserved.c000066400000000000000000000003101411531012200200610ustar00rootroot00000000000000static void foo(void) { goto return; } /* * check-name: goto-reserved * * check-error-start goto-reserved.c:3:14: error: Trying to use reserved word 'return' as identifier * check-error-end */ sparse-0.6.4/validation/identifier_list.c000066400000000000000000000010431411531012200204550ustar00rootroot00000000000000typedef 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.6.4/validation/implicit-KR-arg-type0.c000066400000000000000000000004431411531012200212350ustar00rootroot00000000000000int foo(a, b) int a; { if (b) return a; } /* * check-name: implicit-KR-arg-type * check-command: sparse -Wno-decl -Wold-style-definition -Wno-implicit-int $file * * check-error-start implicit-KR-arg-type0.c:2:9: warning: non-ANSI definition of function 'foo' * check-error-end */ sparse-0.6.4/validation/implicit-KR-arg-type1.c000066400000000000000000000007531411531012200212420ustar00rootroot00000000000000static int foo(a, b) int a; { if (b) return a; } static int bar(a) { if (a) return a; } /* * check-name: implicit-KR-arg-type1 * check-command: sparse -Wold-style-definition -Wimplicit-int $file * * check-error-start implicit-KR-arg-type1.c:2:9: warning: non-ANSI definition of function 'foo' implicit-KR-arg-type1.c:1:19: warning: missing type declaration for parameter 'b' implicit-KR-arg-type1.c:8:16: error: missing type declaration for parameter 'a' * check-error-end */ sparse-0.6.4/validation/implicit-ret-type.c000066400000000000000000000006131411531012200206630ustar00rootroot00000000000000fun(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.6.4/validation/implicit-type.c000066400000000000000000000004561411531012200201000ustar00rootroot00000000000000extern 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.6.4/validation/inc-dec-float.c000066400000000000000000000005471411531012200177150ustar00rootroot00000000000000double fincpre(double a) { ++a; return a; } double fdecpre(double a) { --a; return a; } double fincpost(double a) { a++; return a; } double fdecpost(double a) { a--; return a; } /* * check-name: float inc & dec * check-command: test-linearize -Wno-decl $file * check-output-ignore * * check-output-excludes: \\$1$ * check-output-excludes: \\$-1$ */ sparse-0.6.4/validation/include-eval.c000066400000000000000000000001571411531012200176550ustar00rootroot00000000000000/* nothing */ /* * check-name: include-eval.c * check-command: sparse -include ./include-eval.inc $file */ sparse-0.6.4/validation/include-eval.inc000066400000000000000000000002311411531012200201750ustar00rootroot00000000000000typedef unsigned long long_t; inline static unsigned int ok(void) { return sizeof(long_t); } static unsigned int ko(void) { return sizeof(long_t); } sparse-0.6.4/validation/incomplete-struct.c000066400000000000000000000006401411531012200207630ustar00rootroot00000000000000struct s; void foo(struct s s) { } struct s bar(void) { struct s s; return s; } /* * check-name: incomplete struct * check-command: sparse -Wno-decl $file * check-known-to-fail * * check-error-start incomplete-struct.c:3:19: error: parameter 's' has incomplete type incomplete-struct.c:7:10: error: return type is incomplete incomplete-struct.c:9:11: error: 's' has incompelete type * check-error-end */ sparse-0.6.4/validation/infinite-loop0.c000066400000000000000000000002511411531012200201340ustar00rootroot00000000000000void foo(void) { int a = a || 0; if (a) ; } /* * check-name: internal infinite loop (0) * check-command: sparse -Wno-decl $file * check-timeout: */ sparse-0.6.4/validation/infinite-loop01.c000066400000000000000000000006641411531012200202250ustar00rootroot00000000000000void fnp(void) { int a; for (;;) a += 1; } void fnm(void) { int a; for (;;) a -= 1; } void fna(void) { int a; for (;;) a &= 1; } void fno(void) { int a; for (;;) a |= 1; } void fnx(void) { int a; for (;;) a ^= 1; } void fnl(void) { int a; for (;;) a <<= 1; } void fnr(void) { int a; for (;;) a >>= 1; } /* * check-name: infinite loop 01 * check-command: sparse -Wno-decl $file * check-timeout: */ sparse-0.6.4/validation/infinite-loop02.c000066400000000000000000000002301411531012200202130ustar00rootroot00000000000000void foo(void) { int a = 1; while ((a = !a)) ; } /* * check-name: infinite loop 02 * check-command: sparse -Wno-decl $file * check-timeout: */ sparse-0.6.4/validation/infinite-loop03.c000066400000000000000000000003351411531012200202220ustar00rootroot00000000000000static 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 * check-timeout: */ sparse-0.6.4/validation/infinite-loop04.c000066400000000000000000000003301411531012200202160ustar00rootroot00000000000000extern void use(char); static void foo(char *b) { while (b) { if (b++) continue; ++b; use(*b); &b; } } /* * check-name: internal infinite loop (4) * check-command: sparse $file * check-timeout: */ sparse-0.6.4/validation/init-char-array.c000066400000000000000000000006401411531012200202740ustar00rootroot00000000000000/* * 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.6.4/validation/init-char-array1.c000066400000000000000000000015071411531012200203600ustar00rootroot00000000000000/* * 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.6.4/validation/init-wstring.c000066400000000000000000000027551411531012200177510ustar00rootroot00000000000000static const __WCHAR_TYPE__ ok0[] = L"abc"; _Static_assert(sizeof(ok0) == 4 * sizeof(__WCHAR_TYPE__)); static const __WCHAR_TYPE__ ok1[] = (L"abc"); _Static_assert(sizeof(ok1) == 4 * sizeof(__WCHAR_TYPE__)); static const __WCHAR_TYPE__ ok2[] = { L"abc" }; _Static_assert(sizeof(ok2) == 4 * sizeof(__WCHAR_TYPE__)); static const __WCHAR_TYPE__ ok3[4] = L"abc"; _Static_assert(sizeof(ok3) == 4 * sizeof(__WCHAR_TYPE__)); static const __WCHAR_TYPE__ ok4[4] = (L"abc"); _Static_assert(sizeof(ok4) == 4 * sizeof(__WCHAR_TYPE__)); static const __WCHAR_TYPE__ ok5[4] = { (L"abc") }; _Static_assert(sizeof(ok5) == 4 * sizeof(__WCHAR_TYPE__)); static const __WCHAR_TYPE__ ok6[7] = L"abc"; _Static_assert(sizeof(ok6) == 7 * sizeof(__WCHAR_TYPE__)); static const __WCHAR_TYPE__ ok7[7] = (L"abc"); _Static_assert(sizeof(ok7) == 7 * sizeof(__WCHAR_TYPE__)); static const __WCHAR_TYPE__ ok8[7] = { (L"abc") }; _Static_assert(sizeof(ok8) == 7 * sizeof(__WCHAR_TYPE__)); static const __WCHAR_TYPE__ *ptr[] = { L"abc" }; _Static_assert(sizeof(ptr) == sizeof(void *)); static struct s { const __WCHAR_TYPE__ str[4]; } str = { L"xyz" }; static const __WCHAR_TYPE__ ko3[3] = L"abc"; static const __WCHAR_TYPE__ ko2[2] = L"abc"; /* * check-name: init-wstring * check-command: sparse -Winit-cstring $file * * check-error-start init-wstring.c:29:38: warning: too long initializer-string for array of char(no space for nul char) init-wstring.c:30:38: warning: too long initializer-string for array of char * check-error-end */ sparse-0.6.4/validation/init_cstring.c000066400000000000000000000006161411531012200200010ustar00rootroot00000000000000static struct alpha { char a[2]; } x = { .a = "ab" }; static const char str[2] = "abc"; /* * 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) init_cstring.c:4:28: warning: too long initializer-string for array of char * check-error-end */ sparse-0.6.4/validation/initializer-entry-defined-twice.c000066400000000000000000000024341411531012200234740ustar00rootroot00000000000000/* 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.6.4/validation/inline-generic.c000066400000000000000000000003771411531012200202010ustar00rootroot00000000000000extern int a, b; inline int c(void) { return a++; } inline int e(int d) { return 0; } inline unsigned f(void) { return e(_Generic(b, int: c())); } static int g(void) { return f(); } static int h(void) { return f(); } /* * check-name: inline-generic */ sparse-0.6.4/validation/inline_base0.c000066400000000000000000000011431411531012200176310ustar00rootroot00000000000000static inline int add(int a, int b) { return a + b; } int foo0(int x, int y) { return add(x, y); } int foo1(int x) { return add(x, 1); } int foo2(void) { return add(1, 2); } /* * check-name: inline_base0 * check-command: test-linearize -Wno-decl $file * * check-output-start foo0: .L0: add.32 %r5 <- %arg1, %arg2 # call %r6 <- add, %r1, %r2 ret.32 %r5 foo1: .L3: add.32 %r10 <- %arg1, $1 # call %r11 <- add, %r8, $1 ret.32 %r10 foo2: .L6: # call %r13 <- add, $1, $2 ret.32 $3 * check-output-end */ sparse-0.6.4/validation/inline_compound_literals.c000066400000000000000000000003131411531012200223600ustar00rootroot00000000000000struct 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.6.4/validation/inline_self.c000066400000000000000000000001571411531012200175740ustar00rootroot00000000000000static inline void foo(void) { foo(); } static void baz(void) { foo(); } /* * check-name: inline_self */ sparse-0.6.4/validation/int128.c000066400000000000000000000036371411531012200163400ustar00rootroot00000000000000typedef __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: mul\\.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.6.4/validation/integer-const-expr.c000066400000000000000000000040621411531012200210410ustar00rootroot00000000000000extern void *malloc(unsigned long); static inline __attribute__((__const__)) unsigned squarec(unsigned n) { return n*n; } static inline unsigned square(unsigned n) { return n*n; } static inline unsigned long long bignum(void) { return 1000000000000ULL; } static inline __attribute__((__const__)) unsigned long long bignumc(void) { return 1000000000000ULL; } // test if x is an integer constant expression [C99,C11 6.6p6] #define ICE_P(x) \ (__builtin_types_compatible_p(typeof(0?((void*)((long)(x)*0l)):(int*)1),int*)) #define CHX_P(X) __builtin_choose_expr(ICE_P(X), 1, 0) #define CST_P(X) __builtin_constant_p(ICE_P(X)) #define TEST(R, X) _Static_assert(ICE_P(X) == R, "ICE_P(" #X ") => " #R); \ _Static_assert(ICE_P(ICE_P(X)), "ICE_P2(" #X ")"); \ _Static_assert(CHX_P(X) == R, "CHX_P(" #X ") => " #R); \ _Static_assert(CST_P(X) == 1, "CST_P(" #X ")") int main(int argc, char *argv[]) { char fla[3]; char vla[argc++]; char **p, **q; int x = 5, y = 8; void *v; p = &argv[3]; q = &argv[6]; TEST(1, 4); TEST(1, sizeof(long)); TEST(1, 5ull - 3u); TEST(1, 3.2); TEST(1, sizeof(fla)); TEST(0, square(2)); TEST(0, square(argc)); TEST(0, squarec(2)); TEST(0, squarec(argc)); TEST(0, 1+argc-argc); TEST(0, 1+argc+argc+1-argc-argc); TEST(0, bignum() - 1); TEST(0, 0*bignum()); TEST(0, 0*bignumc()); TEST(0, sizeof(vla)); TEST(0, p); TEST(0, p < q); TEST(0, p++); TEST(0, main); TEST(0, malloc(8)); TEST(0, v = malloc(8)); TEST(0, v); TEST(0, x++); TEST(0, y++); TEST(0, (3, 2, 1)); TEST(0, ({x++; 0; })); TEST(0, ({square(y--); 0; })); TEST(0, (square(x), 3)); TEST(0, (squarec(x), 3)); TEST(0, ({squarec(x); 3;})); TEST(0, ({squarec(x);})); return 0; } /* * check-name: integer-const-expr * check-command: sparse -Wno-vla $file */ sparse-0.6.4/validation/integer-promotions.c000066400000000000000000000001631411531012200211460ustar00rootroot00000000000000static int add_char(void) { return (char) 127 + (char) 127 + (char) 2; } /* * check-name: Integer promotions */ sparse-0.6.4/validation/ioc-typecheck.c000066400000000000000000000003101411531012200200230ustar00rootroot00000000000000extern unsigned int __invalid_size_argument_for_IOC; static unsigned iocnrs[] = { [ 1 ? 0 : __invalid_size_argument_for_IOC ] = 1, }; /* * check-name: integer constant & conditional expression */ sparse-0.6.4/validation/kill-computedgoto.c000066400000000000000000000003431411531012200207440ustar00rootroot00000000000000void 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.6.4/validation/kill-cse.c000066400000000000000000000005331411531012200170060ustar00rootroot00000000000000int 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.6.4/validation/kill-insert-branch.c000066400000000000000000000003751411531012200207770ustar00rootroot00000000000000void 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.6.4/validation/kill-load.c000066400000000000000000000006541411531012200171570ustar00rootroot00000000000000int 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): load\\. */ sparse-0.6.4/validation/kill-phi-node.c000066400000000000000000000005511411531012200177370ustar00rootroot00000000000000void 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.6.4/validation/kill-phi-ttsbb.c000066400000000000000000000006371411531012200201350ustar00rootroot00000000000000int def(void); void use(int); static void 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.6.4/validation/kill-phi-ttsbb2.c000066400000000000000000000012621411531012200202120ustar00rootroot00000000000000extern 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.6.4/validation/kill-phisrc.c000066400000000000000000000004041411531012200175210ustar00rootroot00000000000000int 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.6.4/validation/kill-pure-call.c000066400000000000000000000006641411531012200201250ustar00rootroot00000000000000int 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.6.4/validation/kill-replaced-insn.c000066400000000000000000000016661411531012200207700ustar00rootroot00000000000000// 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: \\ asm "op %0" out: "=m" (%arg1) load.32 %r4 <- 0[%arg1] ret.32 %r4 * check-output-end */ sparse-0.6.4/validation/linear/asm-out0.c000066400000000000000000000005251411531012200202230ustar00rootroot00000000000000static void asm_out0(void) { int mem; asm volatile ("[%1] <= 0" : "=m" (mem)); } /* * check-name: asm-out0 * check-command: test-linearize -m64 -fdump-ir $file * * check-output-start asm_out0: .L0: symaddr.64 %r1 <- mem asm "[%1] <= 0" out: "=m" (%r1) br .L1 .L1: ret * check-output-end */ sparse-0.6.4/validation/linear/asm-toplevel.c000066400000000000000000000003031411531012200211600ustar00rootroot00000000000000__asm__("/* nothing */"); /* * check-name: asm-toplevel.c * check-command: test-linearize $file * check-known-to-fail * check-output-ignore * check-output-contains: asm *".. nothing .." */ sparse-0.6.4/validation/linear/bitfield-expand-deref.c000066400000000000000000000005661411531012200227050ustar00rootroot00000000000000struct s { int a:8; int b:8; }; int foo(void) { struct s x = { .a = 12, .b = 34, }; return x.b; } int bar(int a) { struct s x = { .a = 12, .b = a, }; return x.b; } /* * check-name: bitfield expand deref * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-excludes: ret\\..*\\$12 * check-output-contains: ret\\..*\\$34 */ sparse-0.6.4/validation/linear/bitfield-inc.c000066400000000000000000000003151411531012200211040ustar00rootroot00000000000000struct s { int f:5; }; void inc(struct s *p) { p->f++; } /* * check-name: bitfield-inc * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-excludes: add\\.5 */ sparse-0.6.4/validation/linear/bitfield-init-mask.c000066400000000000000000000010011411531012200222200ustar00rootroot00000000000000struct bfu { unsigned int a:11; unsigned int f:9; unsigned int z:3; }; struct bfu bfu_init_00_11(int a) { struct bfu bfu = { .a = a, }; return bfu; } struct bfu bfu_init_20_23(int a) { struct bfu bfu = { .z = a, }; return bfu; } /* * check-name: bitfield initializer mask * check-command: test-linearize -fdump-ir=linearize -Wno-decl $file * check-output-ignore * * check-output-contains: and\\..*fffff800\$ * check-output-contains: shl\\..* \\$20 * check-output-contains: and\\..*ff8fffff\$ */ sparse-0.6.4/validation/linear/bitfield-preinc.c000066400000000000000000000004331411531012200216140ustar00rootroot00000000000000struct s { int f:3; }; int preinc(void) { struct s s = { 7 }; return ++s.f; } /* * check-name: bitfield-preinc * check-description: ++X is equivalent to X+=1 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-contains: ret.32 *\\$0 */ sparse-0.6.4/validation/linear/bitfield-sign-default.c000066400000000000000000000003511411531012200227150ustar00rootroot00000000000000struct s { int f:2; }; static int getf(struct s s) { return s.f; } /* * check-name: bitfield-sign-default * check-command: test-linearize -fdump-ir=linearize $file * * check-output-ignore * check-output-contains: sext\\. */ sparse-0.6.4/validation/linear/bitfield-sign-signed.c000066400000000000000000000003731411531012200225460ustar00rootroot00000000000000struct s { int f:2; }; static int getf(struct s s) { return s.f; } /* * check-name: bitfield-sign-signed * check-command: test-linearize -fdump-ir=linearize -fsigned-bitfields $file * * check-output-ignore * check-output-contains: sext\\. */ sparse-0.6.4/validation/linear/bitfield-sign-unsigned.c000066400000000000000000000003771411531012200231150ustar00rootroot00000000000000struct s { int f:2; }; static int getf(struct s s) { return s.f; } /* * check-name: bitfield-sign-unsigned * check-command: test-linearize -fdump-ir=linearize -funsigned-bitfields $file * * check-output-ignore * check-output-contains: zext\\. */ sparse-0.6.4/validation/linear/bitfield-size.c000066400000000000000000000066071411531012200213170ustar00rootroot00000000000000struct u { unsigned int f:3; }; unsigned int upostinc(struct u *x) { return x->f++; } unsigned int upreinc(struct u *x) { return ++x->f; } void ucpy(struct u *d, const struct u *s) { d->f = s->f; } struct s { signed int f:3; }; int spostinc(struct s *x) { return x->f++; } int spreinc(struct s *x) { return ++x->f; } void scpy(struct s *d, const struct s *s) { d->f = s->f; } /* * check-name: bitfield-size * check-command: test-linearize -m64 -Wno-decl -fdump-ir $file * check-assert: sizeof(void *) == 8 * * check-output-start upostinc: .L0: store.64 %arg1 -> 0[x] load.64 %r1 <- 0[x] load.32 %r2 <- 0[%r1] trunc.3 %r3 <- (32) %r2 zext.32 %r4 <- (3) %r3 add.32 %r5 <- %r4, $1 trunc.3 %r6 <- (32) %r5 load.32 %r7 <- 0[%r1] zext.32 %r8 <- (3) %r6 and.32 %r9 <- %r7, $0xfffffff8 or.32 %r10 <- %r9, %r8 store.32 %r10 -> 0[%r1] zext.32 %r11 <- (3) %r4 phisrc.32 %phi1(return) <- %r11 br .L1 .L1: phi.32 %r12 <- %phi1(return) ret.32 %r12 upreinc: .L2: store.64 %arg1 -> 0[x] load.64 %r13 <- 0[x] load.32 %r14 <- 0[%r13] trunc.3 %r15 <- (32) %r14 zext.32 %r16 <- (3) %r15 add.32 %r17 <- %r16, $1 trunc.3 %r18 <- (32) %r17 load.32 %r19 <- 0[%r13] zext.32 %r20 <- (3) %r18 and.32 %r21 <- %r19, $0xfffffff8 or.32 %r22 <- %r21, %r20 store.32 %r22 -> 0[%r13] zext.32 %r23 <- (3) %r18 phisrc.32 %phi2(return) <- %r23 br .L3 .L3: phi.32 %r24 <- %phi2(return) ret.32 %r24 ucpy: .L4: store.64 %arg1 -> 0[d] store.64 %arg2 -> 0[s] load.64 %r25 <- 0[s] load.32 %r26 <- 0[%r25] trunc.3 %r27 <- (32) %r26 load.64 %r28 <- 0[d] load.32 %r29 <- 0[%r28] zext.32 %r30 <- (3) %r27 and.32 %r31 <- %r29, $0xfffffff8 or.32 %r32 <- %r31, %r30 store.32 %r32 -> 0[%r28] br .L5 .L5: ret spostinc: .L6: store.64 %arg1 -> 0[x] load.64 %r33 <- 0[x] load.32 %r34 <- 0[%r33] trunc.3 %r35 <- (32) %r34 sext.32 %r36 <- (3) %r35 add.32 %r37 <- %r36, $1 trunc.3 %r38 <- (32) %r37 load.32 %r39 <- 0[%r33] zext.32 %r40 <- (3) %r38 and.32 %r41 <- %r39, $0xfffffff8 or.32 %r42 <- %r41, %r40 store.32 %r42 -> 0[%r33] sext.32 %r43 <- (3) %r36 phisrc.32 %phi3(return) <- %r43 br .L7 .L7: phi.32 %r44 <- %phi3(return) ret.32 %r44 spreinc: .L8: store.64 %arg1 -> 0[x] load.64 %r45 <- 0[x] load.32 %r46 <- 0[%r45] trunc.3 %r47 <- (32) %r46 sext.32 %r48 <- (3) %r47 add.32 %r49 <- %r48, $1 trunc.3 %r50 <- (32) %r49 load.32 %r51 <- 0[%r45] zext.32 %r52 <- (3) %r50 and.32 %r53 <- %r51, $0xfffffff8 or.32 %r54 <- %r53, %r52 store.32 %r54 -> 0[%r45] sext.32 %r55 <- (3) %r50 phisrc.32 %phi4(return) <- %r55 br .L9 .L9: phi.32 %r56 <- %phi4(return) ret.32 %r56 scpy: .L10: store.64 %arg1 -> 0[d] store.64 %arg2 -> 0[s] load.64 %r57 <- 0[s] load.32 %r58 <- 0[%r57] trunc.3 %r59 <- (32) %r58 load.64 %r60 <- 0[d] load.32 %r61 <- 0[%r60] zext.32 %r62 <- (3) %r59 and.32 %r63 <- %r61, $0xfffffff8 or.32 %r64 <- %r63, %r62 store.32 %r64 -> 0[%r60] br .L11 .L11: ret * check-output-end */ sparse-0.6.4/validation/linear/bitfield-store.c000066400000000000000000000006471411531012200214770ustar00rootroot00000000000000int foo(void) { struct { int a:8; int b:16; int c:8; } s = { 0xff, 0x0000, 0xff }; return s.b = 0x56781234; } /* * check-name: bitfield-store * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-contains: ret\\..*\\$0x1234 * * check-error-start linear/bitfield-store.c:9:22: warning: cast truncates bits from constant value (56781234 becomes 1234) * check-error-end */ sparse-0.6.4/validation/linear/bool-cast-lp32.c000066400000000000000000000007531411531012200212220ustar00rootroot00000000000000extern int ffun(void); typedef void *vdp; typedef int *sip; static _Bool fvdp_i(vdp a) { return a; } static _Bool fvdp_e(vdp a) { return (_Bool)a; } static _Bool fsip_i(sip a) { return a; } static _Bool fsip_e(sip a) { return (_Bool)a; } static _Bool ffun_i(void) { return ffun; } static _Bool ffun_e(void) { return (_Bool)ffun; } /* * check-name: bool-cast-pointer * check-command: test-linearize -m32 -fdump-ir $file * * check-output-ignore * check-output-excludes: ptrtu\\. */ sparse-0.6.4/validation/linear/bool-cast-lp64.c000066400000000000000000000010201411531012200212130ustar00rootroot00000000000000extern int ffun(void); typedef void *vdp; typedef int *sip; static _Bool fvdp_i(vdp a) { return a; } static _Bool fvdp_e(vdp a) { return (_Bool)a; } static _Bool fsip_i(sip a) { return a; } static _Bool fsip_e(sip a) { return (_Bool)a; } static _Bool ffun_i(void) { return ffun; } static _Bool ffun_e(void) { return (_Bool)ffun; } /* * check-name: bool-cast-pointer * check-command: test-linearize -m64 -fdump-ir $file * check-assert: sizeof(void *) == 8 * * check-output-ignore * check-output-excludes: ptrtu\\. */ sparse-0.6.4/validation/linear/bool-cast.c000066400000000000000000000023731411531012200204440ustar00rootroot00000000000000extern int fun(void); typedef unsigned int u32; typedef int s32; typedef void *vdp; typedef int *sip; typedef double dbl; typedef unsigned short __attribute__((bitwise)) le16; static _Bool fs32_i(s32 a) { return a; } static _Bool fs32_e(s32 a) { return (_Bool)a; } static _Bool fu32_i(u32 a) { return a; } static _Bool fu32_e(u32 a) { return (_Bool)a; } static _Bool fvdp_i(vdp a) { return a; } static _Bool fvdp_e(vdp a) { return (_Bool)a; } static _Bool fsip_i(sip a) { return a; } static _Bool fsip_e(sip a) { return (_Bool)a; } static _Bool ffun_i(void) { return fun; } static _Bool ffun_e(void) { return (_Bool)fun; } static _Bool fres_i(le16 a) { return a; } static _Bool fres_e(le16 a) { return (_Bool)a; } static _Bool fdbl_i(dbl a) { return a; } static _Bool fdbl_e(dbl a) { return (_Bool)a; } /* * check-name: bool-cast * check-command: test-linearize -m64 -fdump-ir=linearize $file * check-assert: sizeof(void*) == 8 && sizeof(long) == 8 && sizeof(double) == 8 * * check-output-ignore * check-output-excludes: cast\\. * check-output-excludes: fcvt[us]\\. * check-output-excludes: ptrtu\\. * check-output-excludes: [sz]ext\\. * check-output-excludes: trunc\\. * check-output-pattern(12): setne\\. * check-output-pattern(2): fcmpune\\. */ sparse-0.6.4/validation/linear/bug-assign-op0.c000066400000000000000000000031651411531012200213140ustar00rootroot00000000000000int asr(int s) { s >>= 11U; return s; } unsigned int lsr(unsigned int u) { u >>= 11; return u; } int divr(int s, unsigned long long u) { extern int use(int, unsigned); int t = s; s = s / u; u = u / t; return use(s, u); } int sdivul(int s, unsigned long long u) { s /= u; // divu return s; } unsigned int udivsl(unsigned int u, long long s) { u /= s; // divs return u; } int uldivs(int s, unsigned long long u) { u /= s; // divu return u; } unsigned int sldivu(unsigned int u, long long s) { s /= u; // divs return s; } /* * check-name: bug-assign-op0 * check-command: test-linearize -Wno-decl $file * * check-output-start asr: .L0: asr.32 %r2 <- %arg1, $11 ret.32 %r2 lsr: .L2: lsr.32 %r6 <- %arg1, $11 ret.32 %r6 divr: .L4: sext.64 %r11 <- (32) %arg1 divu.64 %r13 <- %r11, %arg2 trunc.32 %r14 <- (64) %r13 divu.64 %r18 <- %arg2, %r11 trunc.32 %r21 <- (64) %r18 call.32 %r22 <- use, %r14, %r21 ret.32 %r22 sdivul: .L6: sext.64 %r26 <- (32) %arg1 divu.64 %r27 <- %r26, %arg2 trunc.32 %r28 <- (64) %r27 ret.32 %r28 udivsl: .L8: zext.64 %r33 <- (32) %arg1 divs.64 %r34 <- %r33, %arg2 trunc.32 %r35 <- (64) %r34 ret.32 %r35 uldivs: .L10: sext.64 %r39 <- (32) %arg1 divu.64 %r41 <- %arg2, %r39 trunc.32 %r43 <- (64) %r41 ret.32 %r43 sldivu: .L12: zext.64 %r46 <- (32) %arg1 divs.64 %r48 <- %arg2, %r46 trunc.32 %r50 <- (64) %r48 ret.32 %r50 * check-output-end */ sparse-0.6.4/validation/linear/builtin-fma.c000066400000000000000000000004421411531012200207630ustar00rootroot00000000000000double fma(double a, double x, double y) { return __builtin_fma(a, x, y); } /* * check-name: builtin-fma * check-command: test-linearize -Wno-decl $file * * check-output-start fma: .L0: fmadd.64 %r4 <- %arg1, %arg2, %arg3 ret.64 %r4 * check-output-end */ sparse-0.6.4/validation/linear/builtin_isdigit.c000066400000000000000000000003451411531012200217400ustar00rootroot00000000000000_Bool isdigit(int c) { return __builtin_isdigit(c) == (((unsigned) (c - '0')) <= 9); } /* * check-name: builtin_isdigit * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/linear/builtin_unreachable0.c000066400000000000000000000005201411531012200226300ustar00rootroot00000000000000int foo(int p) { if (p == 3) __builtin_unreachable(); return p; } /* * check-name: builtin_unreachable0 * check-command: test-linearize -Wno-decl $file * * check-output-start foo: .L0: seteq.32 %r2 <- %arg1, $3 cbr %r2, .L1, .L2 .L1: unreachable .L2: ret.32 %arg1 * check-output-end */ sparse-0.6.4/validation/linear/builtin_unreachable1.c000066400000000000000000000005261411531012200226370ustar00rootroot00000000000000void die(void); int foo(int c) { if (c) return 1; die(); __builtin_unreachable(); } /* * check-name: builtin_unreachable1 * check-command: test-linearize -Wno-decl $file * * check-output-start foo: .L0: cbr %arg1, .L1, .L2 .L1: ret.32 $1 .L2: call die unreachable * check-output-end */ sparse-0.6.4/validation/linear/call-basic.c000066400000000000000000000011771411531012200205540ustar00rootroot00000000000000extern int fun(int a); void symbol(int a) { fun(a); } void pointer0(int a, int (*fun)(int)) { fun(a); } void pointer1(int a, int (*fun)(int)) { (*fun)(a); } void builtin(int a) { __builtin_popcount(a); } /* * check-name: basic function calls * check-command: test-linearize -Wno-decl $file * * check-output-start symbol: .L0: call.32 %r2 <- fun, %arg1 ret pointer0: .L2: call.32 %r5 <- %arg2, %arg1 ret pointer1: .L4: call.32 %r8 <- %arg2, %arg1 ret builtin: .L6: call.32 %r10 <- __builtin_popcount, %arg1 ret * check-output-end */ sparse-0.6.4/validation/linear/call-builtin.c000066400000000000000000000010411411531012200211270ustar00rootroot00000000000000typedef unsigned int u32; u32 ff(u32 a) { return __builtin_popcount(a); } u32 f0(u32 a) { return (__builtin_popcount)(a); } u32 f1(u32 a) { return (*__builtin_popcount)(a); } // C99,C11 6.5.3.2p4 u32 f2(u32 a) { return (**__builtin_popcount)(a); } // C99,C11 6.5.3.2p4 u32 f3(u32 a) { return (***__builtin_popcount)(a); } // C99,C11 6.5.3.2p4 /* * check-name: builtin calls * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-excludes: load * check-output-pattern(5): call\\..*__builtin_.*, %arg1 */ sparse-0.6.4/validation/linear/call-casted-pointer.c000066400000000000000000000010301411531012200224000ustar00rootroot00000000000000typedef int (*fun_t)(void*); int foo(void *a, void *fun) { return ((fun_t)fun)(a); } int bar(void *a, void *fun) { return ((int (*)(void *))fun)(a); } int qux(void *a, void *fun) { return (*(fun_t)fun)(a); } int quz(void *a, void *fun) { return (*(int (*)(void *))fun)(a); } /* * check-name: call via casted function pointer * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-excludes: load * check-output-pattern(4): ptrcast\\..* %arg2 * check-output-pattern(4): call\\..* %arg1 */ sparse-0.6.4/validation/linear/call-complex-pointer.c000066400000000000000000000010361411531012200226120ustar00rootroot00000000000000int foo(int p, int (*f0)(int), int (*f1)(int), int arg) { return (p ? f0 : f1)(arg); } /* * check-name: call-complex-pointer * check-command: test-linearize -m64 -Wno-decl $file * check-assert: sizeof(void *) == 8 * * check-output-start foo: .L0: cbr %arg1, .L2, .L3 .L2: phisrc.64 %phi1 <- %arg2 br .L4 .L3: ptrcast.64 %r6 <- (64) %arg3 phisrc.64 %phi2 <- %r6 br .L4 .L4: phi.64 %r7 <- %phi1, %phi2 call.32 %r8 <- %r7, %arg4 ret.32 %r8 * check-output-end */ sparse-0.6.4/validation/linear/call-direct.c000066400000000000000000000006701411531012200207420ustar00rootroot00000000000000extern int fun(void); int ff(void) { return fun(); } int f0(void) { return (fun)(); } int f1(void) { return (*fun)(); } // C99,C11 6.5.3.2p4 int f2(void) { return (**fun)(); } // C99,C11 6.5.3.2p4 int f3(void) { return (***fun)(); } // C99,C11 6.5.3.2p4 /* * check-name: direct calls * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-excludes: load * check-output-pattern(5): call\\..* fun */ sparse-0.6.4/validation/linear/call-indirect.c000066400000000000000000000007411411531012200212700ustar00rootroot00000000000000int gg(int (*fun)(void)) { return fun(); } int g0(int (*fun)(void)) { return (fun)(); } int g1(int (*fun)(void)) { return (*fun)(); } // C99,C11 6.5.3.2p4 int g2(int (*fun)(void)) { return (**fun)(); } // C99,C11 6.5.3.2p4 int g3(int (*fun)(void)) { return (***fun)(); } // C99,C11 6.5.3.2p4 /* * check-name: indirect calls * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-excludes: load * check-output-pattern(5): call\\..* %arg1 */ sparse-0.6.4/validation/linear/call-inline.c000066400000000000000000000007631411531012200207510ustar00rootroot00000000000000static inline int fun(void) { return 42; } int fi(void) { return fun(); } int i0(void) { return (fun)(); } int i1(void) { return (*fun)(); } // C99,C11 6.5.3.2p4 int i2(void) { return (**fun)(); } // C99,C11 6.5.3.2p4 int i3(void) { return (***fun)(); } // C99,C11 6.5.3.2p4 /* * check-name: inline calls * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-excludes: load * check-output-excludes: \\tcall * check-output-pattern(5): ret\\..* \\$42 */ sparse-0.6.4/validation/linear/cast-constant-to-float.c000066400000000000000000000010231411531012200230540ustar00rootroot00000000000000typedef 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: setfval.64 %r1 <- -1.000000e+00 ret.64 %r1 f2: .L2: setfval.64 %r3 <- -1.000000e+00 ret.64 %r3 f3: .L4: setfval.64 %r5 <- -1.000000e+00 ret.64 %r5 * check-output-end */ sparse-0.6.4/validation/linear/cast-constants.c000066400000000000000000000134611411531012200215250ustar00rootroot00000000000000typedef 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-assert: sizeof(void *) == 8 && sizeof(long) == 8 && sizeof(double) == 8 * * 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: setfval.32 %r39 <- 1.230000e+02 ret.32 %r39 uint_2_float: .L78: setfval.32 %r41 <- 1.230000e+02 ret.32 %r41 long_2_float: .L80: setfval.32 %r43 <- 1.230000e+02 ret.32 %r43 ulong_2_float: .L82: setfval.32 %r45 <- 1.230000e+02 ret.32 %r45 double_2_float: .L84: setfval.32 %r47 <- 1.123000e+00 ret.32 %r47 int_2_double: .L86: setfval.64 %r49 <- 1.230000e+02 ret.64 %r49 uint_2_double: .L88: setfval.64 %r51 <- 1.230000e+02 ret.64 %r51 long_2_double: .L90: setfval.64 %r53 <- 1.230000e+02 ret.64 %r53 ulong_2_double: .L92: setfval.64 %r55 <- 1.230000e+02 ret.64 %r55 float_2_double: .L94: setfval.64 %r57 <- 1.123000e+00 ret.64 %r57 * check-output-end */ sparse-0.6.4/validation/linear/cast-volatile.c000066400000000000000000000004411411531012200213220ustar00rootroot00000000000000static int foo(volatile int *a, int v) { *a = v; return *a; } /* * check-name: cast-volatile * check-command: test-linearize -fdump-ir=linearize $file * * check-output-ignore * check-output-excludes: sext\\. * check-output-excludes: zext\\. * check-output-excludes: trunc\\. */ sparse-0.6.4/validation/linear/compound-literal00.c000066400000000000000000000004511411531012200221720ustar00rootroot00000000000000struct bfs { int a: 2; int b: 30; }; int foo(void) { return (struct bfs){ .a = 1, .b = 2}.b; } /* * check-name: compound-literal00.c * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-contains: ret\\..*\\$2 * check-error-end */ sparse-0.6.4/validation/linear/compound-literal01.c000066400000000000000000000004721411531012200221760ustar00rootroot00000000000000struct bfs { int a: 2; int b: 30; }; int foo(void) { struct bfs bf = { .a = 1, .b = 2 }; return (struct bfs[]){bf}[0].b; } /* * check-name: compound-literal01.c * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-contains: ret\\..*\\$2 */ sparse-0.6.4/validation/linear/compound-literal02.c000066400000000000000000000005151411531012200221750ustar00rootroot00000000000000struct bfs { int a: 2; int b: 30; }; int bar(void) { struct bfs bf = { .a = 1, .b = 4 }; return (struct bfs[]){bf, { .a = 3, .b = 6}}[1].b; } /* * check-name: compound-literal02.c * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-contains: ret\\..*\\$6 */ sparse-0.6.4/validation/linear/degen-array.c000066400000000000000000000006311411531012200207520ustar00rootroot00000000000000extern int a[3]; int (*fa(int i))[] { return &a; } int *f0(int i) { return &a[0]; } int *fd(int i) { return a; } /* * check-name: degen-array * check-command: test-linearize -m64 -Wno-decl $file * check-assert: sizeof(void *) == 8 * * check-output-start fa: .L0: ret.64 a f0: .L2: ret.64 a fd: .L4: ret.64 a * check-output-end */ sparse-0.6.4/validation/linear/degen-function.c000066400000000000000000000014561411531012200214670ustar00rootroot00000000000000extern int fun(int); typedef int (*fun_t)(int); fun_t fa(void) { return &fun; } fun_t f0(void) { return fun; } fun_t f1(void) { return *fun; } /* * check-name: degen-function * check-command: test-linearize -m64 -Wno-decl -fdump-ir=linearize $file * check-assert: sizeof(void *) == 8 * * check-output-start fa: .L0: symaddr.64 %r1 <- fun phisrc.64 %phi1(return) <- %r1 br .L1 .L1: phi.64 %r2 <- %phi1(return) ret.64 %r2 f0: .L2: symaddr.64 %r3 <- fun phisrc.64 %phi2(return) <- %r3 br .L3 .L3: phi.64 %r4 <- %phi2(return) ret.64 %r4 f1: .L4: symaddr.64 %r5 <- fun phisrc.64 %phi3(return) <- %r5 br .L5 .L5: phi.64 %r6 <- %phi3(return) ret.64 %r6 * check-output-end */ sparse-0.6.4/validation/linear/degen-log-not.c000066400000000000000000000010351411531012200212120ustar00rootroot00000000000000extern int arr[]; int test_arr_addr(int i) { if (!&arr) return 1; return 0; } int test_arr_addr0(int i) { if (!&arr[0]) return 1; return 0; } int test_arr_degen(int i) { if (!arr) return 1; return 0; } extern int fun(void); int test_fun_addr(int i) { if (!&fun) return 1; return 0; } int test_fun_degen(int i) { if (!fun) return 1; return 0; } /* * check-name: degenerate logical-not * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-excludes: load * check-output-excludes: VOID */ sparse-0.6.4/validation/linear/deref-ptr-ptr.c000066400000000000000000000007521411531012200212530ustar00rootroot00000000000000char *foo(char **pfmt) { return ++*pfmt; } /* * check-name: deref-ptr-ptr * check-command: test-linearize -m64 -Wno-decl $file * check-assert: sizeof(void *) == 8 * * check-output-excludes: load[^.] * check-output-contains: load\\. * check-output-excludes: store[^.] * check-output-contains: store\\. * * check-output-start foo: .L0: load.64 %r2 <- 0[%arg1] add.64 %r3 <- %r2, $1 store.64 %r3 -> 0[%arg1] ret.64 %r3 * check-output-end */ sparse-0.6.4/validation/linear/fp-vs-ptrcast.c000066400000000000000000000003261411531012200212660ustar00rootroot00000000000000float *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.6.4/validation/linear/fp2i-cast.c000066400000000000000000000013421411531012200203440ustar00rootroot00000000000000#if __SIZEOF_INT__ == __SIZEOF_FLOAT__ typedef signed int si; typedef unsigned int ui; #else #error "no float-sized integer type" #endif #if __SIZEOF_LONG_LONG__ == __SIZEOF_DOUBLE__ typedef signed long long sl; typedef unsigned long long ul; #else #error "no double-sized integer type" #endif si f2si(float a) { return a; } ui f2ui(float a) { return a; } sl f2sl(float a) { return a; } ul f2ul(float a) { return a; } si d2si(double a) { return a; } ui d2ui(double a) { return a; } sl d2sl(double a) { return a; } ul d2ul(double a) { return a; } /* * check-name: fp2i cast * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-pattern(4): fcvts\\. * check-output-pattern(4): fcvtu\\. */ sparse-0.6.4/validation/linear/goto-invalid.c000066400000000000000000000003601411531012200211470ustar00rootroot00000000000000static void foo(void) { goto return; } void bar(void) { goto neverland; } /* * check-name: goto-invalid * check-command: test-linearize -Wno-decl $file * * check-error-ignore * check-output-ignore * check-output-excludes: END */ sparse-0.6.4/validation/linear/goto-stmt-expr-conditional.c000066400000000000000000000005041411531012200237650ustar00rootroot00000000000000int t(void) { goto inside; return 1 ? 2 : ({ inside: return 3; 4; }); } void f(int x, int y) { 1 ? x : ({ a: y; }); goto a; } /* * check-name: goto-stmt-expr-conditional * check-command: test-linearize -Wno-decl $file * * check-error-ignore * check-output-ignore * check-output-excludes: END */ sparse-0.6.4/validation/linear/goto-stmt-expr-short-circuit.c000066400000000000000000000005611411531012200242640ustar00rootroot00000000000000int foo(int p) { goto inside; if (0 && ({ inside: return 1; 2; })) return 3; return 4; } int bar(int p) { if (0 && ({ inside: return 1; 2; })) return 3; goto inside; } /* * check-name: goto-stmt-expr-short-circuit * check-command: test-linearize -Wno-decl $file * * check-error-ignore * check-output-ignore * check-output-excludes: END */ sparse-0.6.4/validation/linear/inline-definition.c000066400000000000000000000006531411531012200221640ustar00rootroot00000000000000extern void use(void *); static inline int inl0(int a); static inline int inl1(int a); static inline int inl0(int a) { return a; } void foo(void) { use(inl0); use(inl1); } static inline int inl1(int a) { return a; } /* * check-name: inline-definition * check-command: test-linearize -Wno-decl $file * check-known-to-fail * * check-output-ignore * check-output-contains: inl0: * check-output-contains: inl1: */ sparse-0.6.4/validation/linear/join-cond-discard.c000066400000000000000000000004701411531012200220440ustar00rootroot00000000000000void abort(void) __attribute__((noreturn)); int bar(int a) { return a ? (abort(), 0) : 0; } int qux(int a) { return a ? (abort(), 0) : (abort(), 1); } /* * check-name: join-cond-discard * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-excludes: phisrc\\..*phi */ sparse-0.6.4/validation/linear/label-scope-cgoto.c000066400000000000000000000003221411531012200220500ustar00rootroot00000000000000#include /* * check-name: linear/label-scope-cgoto * check-command: test-linearize -Wno-decl -I. $file * * check-error-ignore * check-output-ignore * check-output-excludes: END */ sparse-0.6.4/validation/linear/label-stmt-dropped.c000066400000000000000000000005661411531012200222620ustar00rootroot00000000000000/* * 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; if (!r) goto start; return r; } /* * check-name: label-stmt-dropped * check-command: test-linearize $file * * check-output-ignore * check-output-contains: add * check-output-contains: %arg1 */ sparse-0.6.4/validation/linear/label-stmt-expr0.c000066400000000000000000000004161411531012200216550ustar00rootroot00000000000000int foo(void); int foo(void) { int r; r = ({ goto label; label: 1; }); return r; } /* * check-name: label-stmt-expr0 * check-command: test-linearize $file * check-output-ignore * * check-output-excludes: ret\\.32\$ * check-output-contains: ret\\.32 *\\$1 */ sparse-0.6.4/validation/linear/label-unreachable.c000066400000000000000000000004331411531012200221020ustar00rootroot00000000000000static int foo(int a) { goto label; switch(a) { default: label: break; } return 0; } /* * check-name: label-unreachable * check-command: test-linearize $file * * check-error-ignore * check-output-ignore * check-output-contains: ret\\. * check-output-excludes: END */ sparse-0.6.4/validation/linear/logical-phi0.c000066400000000000000000000010741411531012200210260ustar00rootroot00000000000000int a(void); int b(void); int c(void); static int laa(void) { return (a() && b()) && c(); } static int lao(void) { return (a() && b()) || c(); } static int loa(void) { return (a() || b()) && c(); } static int loo(void) { return (a() || b()) || c(); } static int raa(void) { return a() && (b() && c()); } static int rao(void) { return a() && (b() || c()); } static int roa(void) { return a() || (b() && c()); } static int roo(void) { return a() || (b() || c()); } /* * check-name: bad-logical-phi0 * check-command: sparse -vir -flinearize=last $file */ sparse-0.6.4/validation/linear/logical.c000066400000000000000000000120171411531012200201670ustar00rootroot00000000000000struct S { int :1; signed int s:2; unsigned int u:3; long l; double d; }; int os(int i, struct S *b) { return i || b->s; } int ou(int i, struct S *b) { return i || b->u; } int ol(int i, struct S *b) { return i || b->l; } int od(int i, struct S *b) { return i || b->d; } int as(int i, struct S *b) { return i && b->s; } int au(int i, struct S *b) { return i && b->u; } int al(int i, struct S *b) { return i && b->l; } int ad(int i, struct S *b) { return i && b->d; } /* * check-name: logical * check-command: test-linearize -m64 -fdump-ir -Wno-decl $file * check-assert: sizeof(void *) == 8 && sizeof(long) == 8 && sizeof(double) == 8 * * check-output-start os: .L0: store.32 %arg1 -> 0[i] store.64 %arg2 -> 0[b] load.32 %r2 <- 0[i] setne.1 %r3 <- %r2, $0 phisrc.32 %phi1 <- $1 cbr %r3, .L3, .L2 .L2: load.64 %r4 <- 0[b] load.32 %r5 <- 0[%r4] lsr.32 %r6 <- %r5, $1 trunc.2 %r7 <- (32) %r6 setne.1 %r8 <- %r7, $0 zext.32 %r9 <- (1) %r8 phisrc.32 %phi2 <- %r9 br .L3 .L3: phi.32 %r1 <- %phi1, %phi2 phisrc.32 %phi3(return) <- %r1 br .L1 .L1: phi.32 %r10 <- %phi3(return) ret.32 %r10 ou: .L4: store.32 %arg1 -> 0[i] store.64 %arg2 -> 0[b] load.32 %r12 <- 0[i] setne.1 %r13 <- %r12, $0 phisrc.32 %phi4 <- $1 cbr %r13, .L7, .L6 .L6: load.64 %r14 <- 0[b] load.32 %r15 <- 0[%r14] lsr.32 %r16 <- %r15, $3 trunc.3 %r17 <- (32) %r16 setne.1 %r18 <- %r17, $0 zext.32 %r19 <- (1) %r18 phisrc.32 %phi5 <- %r19 br .L7 .L7: phi.32 %r11 <- %phi4, %phi5 phisrc.32 %phi6(return) <- %r11 br .L5 .L5: phi.32 %r20 <- %phi6(return) ret.32 %r20 ol: .L8: store.32 %arg1 -> 0[i] store.64 %arg2 -> 0[b] load.32 %r22 <- 0[i] setne.1 %r23 <- %r22, $0 phisrc.32 %phi7 <- $1 cbr %r23, .L11, .L10 .L10: load.64 %r24 <- 0[b] load.64 %r25 <- 8[%r24] setne.1 %r26 <- %r25, $0 zext.32 %r27 <- (1) %r26 phisrc.32 %phi8 <- %r27 br .L11 .L11: phi.32 %r21 <- %phi7, %phi8 phisrc.32 %phi9(return) <- %r21 br .L9 .L9: phi.32 %r28 <- %phi9(return) ret.32 %r28 od: .L12: store.32 %arg1 -> 0[i] store.64 %arg2 -> 0[b] load.32 %r30 <- 0[i] setne.1 %r31 <- %r30, $0 phisrc.32 %phi10 <- $1 cbr %r31, .L15, .L14 .L14: load.64 %r32 <- 0[b] load.64 %r33 <- 16[%r32] setfval.64 %r34 <- 0.000000e+00 fcmpune.1 %r35 <- %r33, %r34 zext.32 %r36 <- (1) %r35 phisrc.32 %phi11 <- %r36 br .L15 .L15: phi.32 %r29 <- %phi10, %phi11 phisrc.32 %phi12(return) <- %r29 br .L13 .L13: phi.32 %r37 <- %phi12(return) ret.32 %r37 as: .L16: store.32 %arg1 -> 0[i] store.64 %arg2 -> 0[b] load.32 %r39 <- 0[i] setne.1 %r40 <- %r39, $0 phisrc.32 %phi13 <- $0 cbr %r40, .L18, .L19 .L18: load.64 %r41 <- 0[b] load.32 %r42 <- 0[%r41] lsr.32 %r43 <- %r42, $1 trunc.2 %r44 <- (32) %r43 setne.1 %r45 <- %r44, $0 zext.32 %r46 <- (1) %r45 phisrc.32 %phi14 <- %r46 br .L19 .L19: phi.32 %r38 <- %phi13, %phi14 phisrc.32 %phi15(return) <- %r38 br .L17 .L17: phi.32 %r47 <- %phi15(return) ret.32 %r47 au: .L20: store.32 %arg1 -> 0[i] store.64 %arg2 -> 0[b] load.32 %r49 <- 0[i] setne.1 %r50 <- %r49, $0 phisrc.32 %phi16 <- $0 cbr %r50, .L22, .L23 .L22: load.64 %r51 <- 0[b] load.32 %r52 <- 0[%r51] lsr.32 %r53 <- %r52, $3 trunc.3 %r54 <- (32) %r53 setne.1 %r55 <- %r54, $0 zext.32 %r56 <- (1) %r55 phisrc.32 %phi17 <- %r56 br .L23 .L23: phi.32 %r48 <- %phi16, %phi17 phisrc.32 %phi18(return) <- %r48 br .L21 .L21: phi.32 %r57 <- %phi18(return) ret.32 %r57 al: .L24: store.32 %arg1 -> 0[i] store.64 %arg2 -> 0[b] load.32 %r59 <- 0[i] setne.1 %r60 <- %r59, $0 phisrc.32 %phi19 <- $0 cbr %r60, .L26, .L27 .L26: load.64 %r61 <- 0[b] load.64 %r62 <- 8[%r61] setne.1 %r63 <- %r62, $0 zext.32 %r64 <- (1) %r63 phisrc.32 %phi20 <- %r64 br .L27 .L27: phi.32 %r58 <- %phi19, %phi20 phisrc.32 %phi21(return) <- %r58 br .L25 .L25: phi.32 %r65 <- %phi21(return) ret.32 %r65 ad: .L28: store.32 %arg1 -> 0[i] store.64 %arg2 -> 0[b] load.32 %r67 <- 0[i] setne.1 %r68 <- %r67, $0 phisrc.32 %phi22 <- $0 cbr %r68, .L30, .L31 .L30: load.64 %r69 <- 0[b] load.64 %r70 <- 16[%r69] setfval.64 %r71 <- 0.000000e+00 fcmpune.1 %r72 <- %r70, %r71 zext.32 %r73 <- (1) %r72 phisrc.32 %phi23 <- %r73 br .L31 .L31: phi.32 %r66 <- %phi22, %phi23 phisrc.32 %phi24(return) <- %r66 br .L29 .L29: phi.32 %r74 <- %phi24(return) ret.32 %r74 * check-output-end */ sparse-0.6.4/validation/linear/missing-insn-size.c000066400000000000000000000006611411531012200221450ustar00rootroot00000000000000int 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.6.4/validation/linear/missing-return0.c000066400000000000000000000002121411531012200216150ustar00rootroot00000000000000static int foo(int a) { if (a) return 1; } /* * check-name: missing-return0 * check-command: sparse -vir -flinearize=last $file */ sparse-0.6.4/validation/linear/missing-return1.c000066400000000000000000000002741411531012200216260ustar00rootroot00000000000000static inline int fun(int a) { if (a) return 1; } static int foo(int a) { return fun(a); } /* * check-name: missing-return1 * check-command: sparse -vir -flinearize=last $file */ sparse-0.6.4/validation/linear/missing-return2.c000066400000000000000000000002271411531012200216250ustar00rootroot00000000000000static int foo(int a) { switch (a) case 3: return 4; } /* * check-name: missing-return2 * check-command: sparse -vir -flinearize=last $file */ sparse-0.6.4/validation/linear/missing-return3.c000066400000000000000000000004201411531012200216210ustar00rootroot00000000000000static int foo(int a) { if (a) return; } static void ref(void) { } /* * check-name: missing-return3 * check-command: sparse -vir -flinearize=last $file * * check-error-start linear/missing-return3.c:4:17: error: return with no return value * check-error-end */ sparse-0.6.4/validation/linear/missing-return4.c000066400000000000000000000003321411531012200216240ustar00rootroot00000000000000static int foo(int a) { int r = a; r; } /* * check-name: missing-return4 * check-command: test-linearize -Wno-decl $file * * check-error-ignore * check-output-ignore * check-output-contains: ret\\..*UNDEF */ sparse-0.6.4/validation/linear/missing-return5.c000066400000000000000000000006101411531012200216240ustar00rootroot00000000000000int foo(int p) { if (p) return 0; } int bar(int p) { if (p) return 0; p++; } /* * check-name: missing/undef return * check-command: test-linearize -Wno-decl -fdump-ir=linearize $file * * check-output-ignore * check-output-pattern(2): phi\\..*,.* * check-output-pattern(2): phisrc\\..*\\$0 * check-output-pattern(2): phisrc\\..*UNDEF * check-output-excludes: ret\\..*\\$0 */ sparse-0.6.4/validation/linear/non-const-case.c000066400000000000000000000006411411531012200214040ustar00rootroot00000000000000static int foo(int a) { switch (a) { case 0: return a; case a: return 0; case (a - a): return 1; default: return a; } } static int bar(int a) { switch (a) { case 0: break; case a: a++; label: return a; } goto label; } /* * check-name: non-const-case * check-command: test-linearize -Wno-decl $file * * check-error-ignore * check-output-ignore * check-output-excludes:switch \\. */ sparse-0.6.4/validation/linear/noreturn-unreachable0.c000066400000000000000000000004331411531012200227570ustar00rootroot00000000000000extern void die(void) __attribute__((noreturn)); int foo(void) { die(); return 0; } /* * check-name: noreturn-unreachable0 * check-command: test-linearize -Wno-decl $file * * check-output-start foo: .L0: call die unreachable * check-output-end */ sparse-0.6.4/validation/linear/phi-order01.c000066400000000000000000000003051411531012200206040ustar00rootroot00000000000000int fun(void); static int foo(int a) { return a && fun(); } static int bar(int a) { return a || fun(); } /* * check-name: phi-order01 * check-command: sparse -vir -flinearize=last $file */ sparse-0.6.4/validation/linear/phi-order02.c000066400000000000000000000007211411531012200206070ustar00rootroot00000000000000int fun(void); static int foo(int a) { return 0 || fun(); } static int bar(int a) { return 1 || fun(); } static int baz(int a) { return 0 && fun(); } static int qux(int a) { return 1 && fun(); } static int oof(int a) { return fun() || 1; } static int rab(int a) { return fun() || 0; } static int zab(int a) { return fun() && 1; } static int xuq(int a) { return fun() && 0; } /* * check-name: phi-order02 * check-command: sparse -vir -flinearize=last $file */ sparse-0.6.4/validation/linear/phi-order03.c000066400000000000000000000002411411531012200206050ustar00rootroot00000000000000int fun(void); static int foo(void) { return ((0 || fun()) && fun()); } /* * check-name: phi-order03 * check-command: sparse -vir -flinearize=last $file */ sparse-0.6.4/validation/linear/phi-order04.c000066400000000000000000000002241411531012200206070ustar00rootroot00000000000000static void foo(int *b) { if (1) { int c; b = &c; } } /* * check-name: phi-order04 * check-command: sparse -vir -flinearize=last $file */ sparse-0.6.4/validation/linear/pointer-arith32.c000066400000000000000000000025131411531012200215070ustar00rootroot00000000000000char *cps(char *data, short pos) { data += pos; return data; } int *ipss(int *data, short pos) { data += pos; return data; } int *ipus(int *data, unsigned short pos) { data += pos; return data; } char *cpq(char *data, long long pos) { data += pos; return data; } int *ipq_ref(int *data, long long pos) { data = data + pos; return data; } int *ipq(int *data, long long pos) { data += pos; return data; } /* * check-name: pointer-arith32 * check-command: test-linearize -Wno-decl --arch=generic -m32 $file * * check-output-start cps: .L0: sext.32 %r2 <- (16) %arg2 add.32 %r5 <- %r2, %arg1 ret.32 %r5 ipss: .L2: sext.32 %r10 <- (16) %arg2 mul.32 %r11 <- %r10, $4 add.32 %r14 <- %r11, %arg1 ret.32 %r14 ipus: .L4: zext.32 %r19 <- (16) %arg2 mul.32 %r20 <- %r19, $4 add.32 %r23 <- %r20, %arg1 ret.32 %r23 cpq: .L6: trunc.32 %r28 <- (64) %arg2 add.32 %r31 <- %r28, %arg1 ret.32 %r31 ipq_ref: .L8: trunc.32 %r37 <- (64) %arg2 mul.32 %r38 <- %r37, $4 add.32 %r39 <- %r38, %arg1 ret.32 %r39 ipq: .L10: trunc.32 %r43 <- (64) %arg2 mul.32 %r44 <- %r43, $4 add.32 %r47 <- %r44, %arg1 ret.32 %r47 * check-output-end */ sparse-0.6.4/validation/linear/pointer-arith64.c000066400000000000000000000022101411531012200215060ustar00rootroot00000000000000char *cps(char *data, short pos) { data += pos; return data; } int *ipss(int *data, short pos) { data += pos; return data; } int *ipus(int *data, unsigned short pos) { data += pos; return data; } int *ipsi(int *data, int pos) { data += pos; return data; } int *ipui(int *data, unsigned int pos) { data += pos; return data; } /* * check-name: pointer-arith64 * check-command: test-linearize -Wno-decl --arch=generic -m64 $file * * check-output-start cps: .L0: sext.64 %r2 <- (16) %arg2 add.64 %r5 <- %r2, %arg1 ret.64 %r5 ipss: .L2: sext.64 %r10 <- (16) %arg2 mul.64 %r11 <- %r10, $4 add.64 %r14 <- %r11, %arg1 ret.64 %r14 ipus: .L4: zext.64 %r19 <- (16) %arg2 mul.64 %r20 <- %r19, $4 add.64 %r23 <- %r20, %arg1 ret.64 %r23 ipsi: .L6: sext.64 %r28 <- (32) %arg2 mul.64 %r29 <- %r28, $4 add.64 %r32 <- %r29, %arg1 ret.64 %r32 ipui: .L8: zext.64 %r37 <- (32) %arg2 mul.64 %r38 <- %r37, $4 add.64 %r41 <- %r38, %arg1 ret.64 %r41 * check-output-end */ sparse-0.6.4/validation/linear/range-op.c000066400000000000000000000005671411531012200202740ustar00rootroot00000000000000static void foo(int a) { __range__(a, 0, 8); } static void bar(int a, int b, int c) { __range__(a, b, c); } /* * check-name: range-op * check-command: test-linearize -Wno-decl $file * * check-output-start foo: .L0: range-check %arg1 between $0..$8 ret bar: .L2: range-check %arg1 between %arg2..%arg3 ret * check-output-end */ sparse-0.6.4/validation/linear/shift-assign1.c000066400000000000000000000136171411531012200212440ustar00rootroot00000000000000typedef __INT16_TYPE__ s16; typedef __INT32_TYPE__ s32; typedef __INT64_TYPE__ s64; typedef __UINT16_TYPE__ u16; typedef __UINT32_TYPE__ u32; typedef __UINT64_TYPE__ u64; s16 s16s16(s16 a, s16 b) { a >>= b; return a; } s16 s16s32(s16 a, s32 b) { a >>= b; return a; } s16 s16s64(s16 a, s64 b) { a >>= b; return a; } s16 s16u16(s16 a, u16 b) { a >>= b; return a; } s16 s16u32(s16 a, u32 b) { a >>= b; return a; } s16 s16u64(s16 a, u64 b) { a >>= b; return a; } s32 s32s16(s32 a, s16 b) { a >>= b; return a; } s32 s32s32(s32 a, s32 b) { a >>= b; return a; } s32 s32s64(s32 a, s64 b) { a >>= b; return a; } s32 s32u16(s32 a, u16 b) { a >>= b; return a; } s32 s32u32(s32 a, u32 b) { a >>= b; return a; } s32 s32u64(s32 a, u64 b) { a >>= b; return a; } s64 s64s16(s64 a, s16 b); s64 s64s32(s64 a, s32 b); s64 s64s64(s64 a, s64 b) { a >>= b; return a; } s64 s64u16(s64 a, u16 b) { a >>= b; return a; } s64 s64u32(s64 a, u32 b) { a >>= b; return a; } s64 s64u64(s64 a, u64 b) { a >>= b; return a; } u16 u16s16(u16 a, s16 b) { a >>= b; return a; } u16 u16s32(u16 a, s32 b) { a >>= b; return a; } u16 u16s64(u16 a, s64 b) { a >>= b; return a; } u16 u16u16(u16 a, u16 b) { a >>= b; return a; } u16 u16u32(u16 a, u32 b) { a >>= b; return a; } u16 u16u64(u16 a, u64 b) { a >>= b; return a; } u32 u32s16(u32 a, s16 b) { a >>= b; return a; } u32 u32s32(u32 a, s32 b) { a >>= b; return a; } u32 u32s64(u32 a, s64 b) { a >>= b; return a; } u32 u32u16(u32 a, u16 b) { a >>= b; return a; } u32 u32u32(u32 a, u32 b) { a >>= b; return a; } u32 u32u64(u32 a, u64 b) { a >>= b; return a; } u64 u64s16(u64 a, s16 b); u64 u64s32(u64 a, s32 b); u64 u64s64(u64 a, s64 b) { a >>= b; return a; } u64 u64u16(u64 a, u16 b) { a >>= b; return a; } u64 u64u32(u64 a, u32 b) { a >>= b; return a; } u64 u64u64(u64 a, u64 b) { a >>= b; return a; } /* * check-name: shift-assign1 * check-command: test-linearize -Wno-decl $file * * check-output-start s16s16: .L0: sext.32 %r2 <- (16) %arg2 sext.32 %r4 <- (16) %arg1 asr.32 %r5 <- %r4, %r2 trunc.16 %r6 <- (32) %r5 ret.16 %r6 s16s32: .L2: sext.32 %r11 <- (16) %arg1 asr.32 %r12 <- %r11, %arg2 trunc.16 %r13 <- (32) %r12 ret.16 %r13 s16s64: .L4: trunc.32 %r17 <- (64) %arg2 sext.32 %r19 <- (16) %arg1 asr.32 %r20 <- %r19, %r17 trunc.16 %r21 <- (32) %r20 ret.16 %r21 s16u16: .L6: zext.32 %r25 <- (16) %arg2 sext.32 %r27 <- (16) %arg1 asr.32 %r28 <- %r27, %r25 trunc.16 %r29 <- (32) %r28 ret.16 %r29 s16u32: .L8: sext.32 %r34 <- (16) %arg1 asr.32 %r35 <- %r34, %arg2 trunc.16 %r36 <- (32) %r35 ret.16 %r36 s16u64: .L10: trunc.32 %r40 <- (64) %arg2 sext.32 %r42 <- (16) %arg1 asr.32 %r43 <- %r42, %r40 trunc.16 %r44 <- (32) %r43 ret.16 %r44 s32s16: .L12: sext.32 %r48 <- (16) %arg2 asr.32 %r50 <- %arg1, %r48 ret.32 %r50 s32s32: .L14: asr.32 %r55 <- %arg1, %arg2 ret.32 %r55 s32s64: .L16: trunc.32 %r59 <- (64) %arg2 asr.32 %r61 <- %arg1, %r59 ret.32 %r61 s32u16: .L18: zext.32 %r65 <- (16) %arg2 asr.32 %r67 <- %arg1, %r65 ret.32 %r67 s32u32: .L20: asr.32 %r72 <- %arg1, %arg2 ret.32 %r72 s32u64: .L22: trunc.32 %r76 <- (64) %arg2 asr.32 %r78 <- %arg1, %r76 ret.32 %r78 s64s64: .L24: asr.64 %r83 <- %arg1, %arg2 ret.64 %r83 s64u16: .L26: zext.64 %r88 <- (16) %arg2 asr.64 %r90 <- %arg1, %r88 ret.64 %r90 s64u32: .L28: zext.64 %r94 <- (32) %arg2 asr.64 %r96 <- %arg1, %r94 ret.64 %r96 s64u64: .L30: asr.64 %r101 <- %arg1, %arg2 ret.64 %r101 u16s16: .L32: sext.32 %r105 <- (16) %arg2 zext.32 %r107 <- (16) %arg1 asr.32 %r108 <- %r107, %r105 trunc.16 %r109 <- (32) %r108 ret.16 %r109 u16s32: .L34: zext.32 %r114 <- (16) %arg1 asr.32 %r115 <- %r114, %arg2 trunc.16 %r116 <- (32) %r115 ret.16 %r116 u16s64: .L36: trunc.32 %r120 <- (64) %arg2 zext.32 %r122 <- (16) %arg1 asr.32 %r123 <- %r122, %r120 trunc.16 %r124 <- (32) %r123 ret.16 %r124 u16u16: .L38: zext.32 %r128 <- (16) %arg2 zext.32 %r130 <- (16) %arg1 asr.32 %r131 <- %r130, %r128 trunc.16 %r132 <- (32) %r131 ret.16 %r132 u16u32: .L40: zext.32 %r137 <- (16) %arg1 asr.32 %r138 <- %r137, %arg2 trunc.16 %r139 <- (32) %r138 ret.16 %r139 u16u64: .L42: trunc.32 %r143 <- (64) %arg2 zext.32 %r145 <- (16) %arg1 asr.32 %r146 <- %r145, %r143 trunc.16 %r147 <- (32) %r146 ret.16 %r147 u32s16: .L44: sext.32 %r151 <- (16) %arg2 lsr.32 %r153 <- %arg1, %r151 ret.32 %r153 u32s32: .L46: lsr.32 %r158 <- %arg1, %arg2 ret.32 %r158 u32s64: .L48: trunc.32 %r162 <- (64) %arg2 lsr.32 %r164 <- %arg1, %r162 ret.32 %r164 u32u16: .L50: zext.32 %r168 <- (16) %arg2 lsr.32 %r170 <- %arg1, %r168 ret.32 %r170 u32u32: .L52: lsr.32 %r175 <- %arg1, %arg2 ret.32 %r175 u32u64: .L54: trunc.32 %r179 <- (64) %arg2 lsr.32 %r181 <- %arg1, %r179 ret.32 %r181 u64s64: .L56: lsr.64 %r186 <- %arg1, %arg2 ret.64 %r186 u64u16: .L58: zext.64 %r191 <- (16) %arg2 lsr.64 %r193 <- %arg1, %r191 ret.64 %r193 u64u32: .L60: zext.64 %r197 <- (32) %arg2 lsr.64 %r199 <- %arg1, %r197 ret.64 %r199 u64u64: .L62: lsr.64 %r204 <- %arg1, %arg2 ret.64 %r204 * check-output-end */ sparse-0.6.4/validation/linear/shift-assign2.c000066400000000000000000000017541411531012200212440ustar00rootroot00000000000000typedef __INT16_TYPE__ s16; typedef __INT32_TYPE__ s32; typedef __INT64_TYPE__ s64; typedef __UINT16_TYPE__ u16; typedef __UINT32_TYPE__ u32; typedef __UINT64_TYPE__ u64; s64 s64s16(s64 a, s16 b) { a >>= b; return a; } s64 s64s32(s64 a, s32 b) { a >>= b; return a; } u64 u64s16(u64 a, s16 b) { a >>= b; return a; } u64 u64s32(u64 a, s32 b) { a >>= b; return a; } /* * check-name: shift-assign2 * check-command: test-linearize -Wno-decl $file * * check-output-start s64s16: .L0: sext.32 %r2 <- (16) %arg2 zext.64 %r3 <- (32) %r2 asr.64 %r5 <- %arg1, %r3 ret.64 %r5 s64s32: .L2: zext.64 %r9 <- (32) %arg2 asr.64 %r11 <- %arg1, %r9 ret.64 %r11 u64s16: .L4: sext.32 %r15 <- (16) %arg2 zext.64 %r16 <- (32) %r15 lsr.64 %r18 <- %arg1, %r16 ret.64 %r18 u64s32: .L6: zext.64 %r22 <- (32) %arg2 lsr.64 %r24 <- %arg1, %r22 ret.64 %r24 * check-output-end */ sparse-0.6.4/validation/linear/struct-init-full.c000066400000000000000000000007001411531012200217760ustar00rootroot00000000000000struct 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.6.4/validation/linear/struct-init-partial.c000066400000000000000000000011041411531012200224670ustar00rootroot00000000000000struct 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.6.4/validation/linear/unexamined-base-type.c000066400000000000000000000015501411531012200226010ustar00rootroot00000000000000# define __force __attribute__((force)) struct s { int a; }; static int foo(struct s *s) { return (*((typeof(s->a) __force *) &s->a)) & 1; } static void bar(struct s *d, struct s *s1, struct s *s2) { *d = *s1, *d = *s2; } /* * check-name: unexamined base type * check-command: test-linearize -Wno-decl $file * check-description: * Test case for missing examine in evaluate_dereference()'s * target base type. In this case, the loaded value has a * a null size, giving the wrongly generated code for foo(): * ptrcast.64 %r3 <- (64) %arg1 * load %r4 <- 0[%r3] * ^^^ !! WRONG !! * cast.32 %r5 <- (0) %r4 * ^^^ !! WRONG !! * and.32 %r6 <- %r5, $1 * ret.32 %r6 * * check-output-ignore * check-output-excludes: load[^.] * check-output-excludes: cast\\..*(0) * check-output-excludes: store[^.] */ sparse-0.6.4/validation/local-label.c000066400000000000000000000003261411531012200174520ustar00rootroot00000000000000void 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.6.4/validation/logical.c000066400000000000000000000003131411531012200167110ustar00rootroot00000000000000extern 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.6.4/validation/mem2reg/000077500000000000000000000000001411531012200164745ustar00rootroot00000000000000sparse-0.6.4/validation/mem2reg/address-used00.c000066400000000000000000000004131411531012200213610ustar00rootroot00000000000000int foo(int **g, int j) { int i = 1; int *a; int **p; a = &i; p = &a; *p[0] = 0; return i; } /* * check-name: address-used00 * check-command: test-linearize -Wno-decl -fdump-ir=final $file * check-output-ignore * check-output-excludes: ret\\..* \\$1 */ sparse-0.6.4/validation/mem2reg/alias-distinct.c000066400000000000000000000003561411531012200215540ustar00rootroot00000000000000extern 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.6.4/validation/mem2reg/alias-mixed.c000066400000000000000000000005201411531012200210320ustar00rootroot00000000000000extern 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.6.4/validation/mem2reg/alias-same.c000066400000000000000000000003351411531012200206550ustar00rootroot00000000000000extern 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.6.4/validation/mem2reg/asm-reload0.c000066400000000000000000000003531411531012200207450ustar00rootroot00000000000000static int asm_reload(void) { int mem = 0; asm volatile ("[%1] <= 1" : "=m" (mem)); return mem; } /* * check-name: asm-reload0 * check-command: test-linearize $file * * check-output-ignore * check-output-contains: load\\. */ sparse-0.6.4/validation/mem2reg/broken-phi02.c000066400000000000000000000007461411531012200210470ustar00rootroot00000000000000int foo(int a, int b) { int x; int i; if (a) i = 0; else i = 1; x = 0; if (b) x = i; return x; } /* * check-name: broken-phi02 * check-description: * This is an indirect test to check correctness of phi-node placement. * The misplaced phi-node for 'i' (not at the meet point but where 'i' * is used) causes a missed select-conversion at later stage. * * check-command: test-linearize -Wno-decl $file * check-output-ignore * check-output-contains: select\\. */ sparse-0.6.4/validation/mem2reg/broken-phi03.c000066400000000000000000000010401411531012200210340ustar00rootroot00000000000000int foo(int a, int b) { int x; int i; switch (a) { case 0: i = 0; break; case 1: i = 1; break; default: i = -1; break; } x = 0; if (b) x = i; return x; } /* * check-name: broken-phi03 * check-description: * This is an indirect test to check correctness of phi-node placement. * The misplaced phi-node for 'i' (not at the meet point but where 'i' * is used) causes a missed select-conversion at later stage. * * check-command: test-linearize -Wno-decl $file * check-output-ignore * check-output-contains: select\\. */ sparse-0.6.4/validation/mem2reg/cond-expr.c000066400000000000000000000004211411531012200205340ustar00rootroot00000000000000int fun(int); int foo(int a, int b, int c) { return a ? fun(b) : fun(c); } /* * check-name: cond-expr * check-command: test-linearize -Wno-decl -fdump-ir=mem2reg $file * check-output-ignore * check-output-pattern(1): phi\\. * check-output-pattern(2): phisrc\\. */ sparse-0.6.4/validation/mem2reg/cond-expr5.c000066400000000000000000000005341411531012200206260ustar00rootroot00000000000000int foo(int p, int q, int a) { if (p) a = 0; if (q) a = 1; return a; } /* * check-name: cond-expr5 * check-command: test-linearize -Wno-decl -fdump-ir=mem2reg $file * * check-output-ignore * check-output-excludes: load\\. * check-output-excludes: store\\. * check-output-pattern(2): phi\\. * check-output-pattern(4): phisrc\\. */ sparse-0.6.4/validation/mem2reg/dead-phisrc.c000066400000000000000000000003311411531012200210200ustar00rootroot00000000000000static void foo(void) { extern int *a; if (a || *a) ; if (a[0] || a[1]) ; } /* * check-name: dead-phisrc * check-command: test-linearize $file * * check-output-ignore * check-output-excludes: phisrc */ sparse-0.6.4/validation/mem2reg/global-direct-undef.c000066400000000000000000000005101411531012200224430ustar00rootroot00000000000000int a, c, d; int foo(void) { int b, e; if (a) b = c; else b = d; if (c) a = b; if (b) e = a; return e; } /* * check-name: global direct undef * check-command: test-linearize -Wno-decl -fdump-ir=mem2reg $file * check-output-ignore * check-output-pattern(4,5): load\\. * check-output-pattern(1): store\\. */ sparse-0.6.4/validation/mem2reg/global-direct.c000066400000000000000000000005061411531012200213510ustar00rootroot00000000000000int a, c, d; int foo(void) { int b, e = 0; if (a) b = c; else b = d; if (c) a = b; if (b) e = a; return e; } /* * check-name: global direct * check-command: test-linearize -Wno-decl -fdump-ir=mem2reg $file * check-output-ignore * check-output-pattern(4,5): load\\. * check-output-pattern(1): store\\. */ sparse-0.6.4/validation/mem2reg/global-loop.c000066400000000000000000000004511411531012200210470ustar00rootroot00000000000000struct s { int c; int a[]; } s; int f; void fun(void); void foo(void) { for (f = 1;;) if (s.a[f]) fun(); } /* * check-name: global var as loop index * check-command: test-linearize -Wno-decl -fdump-ir=mem2reg $file * check-output-ignore * check-output-contains: load\\..*\\[f\\] */ sparse-0.6.4/validation/mem2reg/global-noalias.c000066400000000000000000000004651411531012200215310ustar00rootroot00000000000000int a, b, c, d, e; void foo(void) { if (a) b = c; else b = d; if (c) a = b; if (b) e = a; } /* * check-name: global no-alias * check-command: test-linearize -Wno-decl -fdump-ir=mem2reg $file * check-output-ignore * check-output-pattern(4,7): load\\. * check-output-pattern(4): store\\. */ sparse-0.6.4/validation/mem2reg/global-pointer.c000066400000000000000000000005761411531012200215660ustar00rootroot00000000000000int a, c, d; int foo_ptr(void) { int b, *bp = &b; int e, *ep = &e; if (a) *bp = c; else *bp = d; if (c) a = *bp; if (b) e = a; return e; } /* * check-name: global pointer * check-command: test-linearize -Wno-decl -fdump-ir=mem2reg $file * check-known-to-fail * check-output-ignore * check-output-pattern(4,5): load\\. * check-output-pattern(3): store\\. */ sparse-0.6.4/validation/mem2reg/if-direct.c000066400000000000000000000004361411531012200205110ustar00rootroot00000000000000int foo(int c, int a, int b) { int l; if (c) l = a; else l = b; return l; } /* * check-name: if-then-else direct * check-command: test-linearize -Wno-decl -fdump-ir=mem2reg $file * check-output-ignore * check-output-excludes: load\\. * check-output-contains: phi\\. */ sparse-0.6.4/validation/mem2reg/if-pointer.c000066400000000000000000000005511411531012200207150ustar00rootroot00000000000000int foo(int c, int a, int b) { int l, *p = &l; if (c) *p = a; else *p = b; return l + *p; } /* * check-name: if-then-else pointer * check-command: test-linearize -Wno-decl -fdump-ir=mem2reg $file * check-known-to-fail * check-output-ignore * check-output-excludes: load\\. * check-output-excludes: store\\. * check-output-contains: phi\\. */ sparse-0.6.4/validation/mem2reg/init-global-array.c000066400000000000000000000004771411531012200221650ustar00rootroot00000000000000struct s { int a[2]; }; static struct s s; static int sarray(void) { s.a[1] = 1; return s.a[1]; } /* * check-name: init global array * check-command: test-linearize $file * check-output-ignore * check-output-excludes: load\\. * check-output-pattern(1): store\\. * check-output-pattern(1): ret.32 *\\$1 */ sparse-0.6.4/validation/mem2reg/init-local-array.c000066400000000000000000000005751411531012200220160ustar00rootroot00000000000000static int array(void) { int a[2]; a[1] = 1; a[0] = 0; return a[1]; } static int sarray(void) { struct { int a[2]; } s; s.a[1] = 1; s.a[0] = 0; return s.a[1]; } /* * check-name: init local array * check-command: test-linearize $file * check-output-ignore * check-output-excludes: load * check-output-excludes: store * check-output-pattern(2): ret.32 *\\$1 */ sparse-0.6.4/validation/mem2reg/init-local-union0.c000066400000000000000000000004501411531012200221000ustar00rootroot00000000000000double uintfloat(void) { union { int a; double f; } s; s.a = 1; return s.f; } /* * check-name: init-local union 0 * check-command: test-linearize -Wno-decl -fdump-ir=mem2reg $file * check-output-ignore * check-output-pattern(1): store\\.32 * check-output-pattern(1): load\\.64 */ sparse-0.6.4/validation/mem2reg/init-local-union1.c000066400000000000000000000007161411531012200221060ustar00rootroot00000000000000double uintfloat(void) { union { int a; double f; } s; s.a = 1; return s.f; } int uarray(void) { union { double d; int a[2]; } s; s.d = 1; return s.a[0]; } /* * check-name: init-local union 1 * check-command: test-linearize -Wno-decl -fdump-ir=mem2reg $file * check-output-ignore * check-output-pattern(1): store\\.32 * check-output-pattern(1): load\\.64 * check-output-pattern(1): store\\.64 * check-output-pattern(1): load\\.32 */ sparse-0.6.4/validation/mem2reg/init-local32.c000066400000000000000000000005401411531012200210370ustar00rootroot00000000000000int ssimple(void) { struct { int a; } s; s.a = 1; return s.a; } double sdouble(void) { struct { double a; } s; s.a = 1.23; return s.a; } /* * check-name: init-local32 * check-command: test-linearize -Wno-decl -m32 -fdump-ir=mem2reg $file * check-output-ignore * check-output-excludes: load\\. * check-output-excludes: store\\. */ sparse-0.6.4/validation/mem2reg/init-local64.c000066400000000000000000000005401411531012200210440ustar00rootroot00000000000000int ssimple(void) { struct { int a; } s; s.a = 1; return s.a; } double sdouble(void) { struct { double a; } s; s.a = 1.23; return s.a; } /* * check-name: init-local64 * check-command: test-linearize -Wno-decl -m64 -fdump-ir=mem2reg $file * check-output-ignore * check-output-excludes: load\\. * check-output-excludes: store\\. */ sparse-0.6.4/validation/mem2reg/load-dead.c000066400000000000000000000003621411531012200204530ustar00rootroot00000000000000int fun(int); static inline int fake(void) { } static void foo(int a) { 0 || fun((a, fake(), a)); } /* * check-name: load-dead * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-excludes: VOID */ sparse-0.6.4/validation/mem2reg/load-deadborn.c000066400000000000000000000001151411531012200213300ustar00rootroot00000000000000static void foo(int a) { return; a; } /* * check-name: load-deadborn */ sparse-0.6.4/validation/mem2reg/loop00.c000066400000000000000000000004221411531012200177470ustar00rootroot00000000000000int loop00(int n) { int i, r = 0; for (i = 1; i <= n; ++i) r += i; return r; } /* * check-name: loop00 * check-command: test-linearize -Wno-decl -fdump-ir=mem2reg $file * check-output-ignore * check-output-excludes: store\\. * check-output-excludes: load\\. */ sparse-0.6.4/validation/mem2reg/loop01-global.c000066400000000000000000000004731411531012200212140ustar00rootroot00000000000000extern int g; void fun(void); void loop01(void) { int i; for (i = 0; i <= 2;) if (g) fun(); } /* * check-name: loop01 global * check-command: test-linearize -Wno-decl -fdump-ir=mem2reg $file * check-output-ignore * check-output-excludes: load\\..*\\[i\\] * check-output-contains: load\\..*\\[g\\] */ sparse-0.6.4/validation/mem2reg/loop02-array.c000066400000000000000000000005101411531012200210630ustar00rootroot00000000000000 int foo(int i[]) { int j = 1; i[0] = 6; do { if (i[0] != 6) i[0]++; i[0]++; } while (i[0] != j); return j; } /* * check-name: loop02 array * check-command: test-linearize -Wno-decl -fdump-ir=mem2reg $file * check-output-ignore * check-output-pattern(0,4): load\\. * check-output-pattern(1,3): store\\. */ sparse-0.6.4/validation/mem2reg/loop02-global.c000066400000000000000000000004001411531012200212030ustar00rootroot00000000000000int i; int foo(void) { int j = 1; i = 6; do { if (i != 6) i++; i++; } while (i != j); return j; } /* * check-name: loop02 global * check-command: test-linearize -Wno-decl $file * check-output-ignore * check-output-excludes: load\\. */ sparse-0.6.4/validation/mem2reg/loop02-local.c000066400000000000000000000004241411531012200210430ustar00rootroot00000000000000 int foo(void) { int j = 1; int i = 6; do { if (i != 6) i++; i++; } while (i != j); return j; } /* * check-name: loop02 pointer * check-command: test-linearize -Wno-decl -fdump-ir=mem2reg $file * * check-output-ignore * check-output-excludes: load\\. */ sparse-0.6.4/validation/mem2reg/loop02-pointer.c000066400000000000000000000005031411531012200214270ustar00rootroot00000000000000 int foo(int *i) { int j = 1; *i = 6; do { if (*i != 6) (*i)++; (*i)++; } while (*i != j); return j; } /* * check-name: loop02 pointer * check-command: test-linearize -Wno-decl -fdump-ir=mem2reg $file * check-output-ignore * check-output-pattern(0,4): load\\. * check-output-pattern(1,3): store\\. */ sparse-0.6.4/validation/mem2reg/missing-return.c000066400000000000000000000006221411531012200216260ustar00rootroot00000000000000int f1(void) { if (1) return 1; } int f0(void) { if (0) return 0; } int fx(int p) { if (p) return 0; } int bar(int p) { if (p) return 0; p++; } /* * check-name: missing-return * check-command: test-linearize -m32 -fdump-ir=mem2reg -Wno-decl $file * check-known-to-fail * * check-output-ignore * check-output-pattern(1): ret.32 *\\$1 * check-output-pattern(3): ret.32 *UNDEF */ sparse-0.6.4/validation/mem2reg/not-same-memop0.c000066400000000000000000000011731411531012200215600ustar00rootroot00000000000000struct s { int:16; short f:6; }; static short local(struct s s) { return s.f; } static void foo(struct s s) { while (s.f) ; } /* * check-name: not-same-memop0 * check-command: test-linearize -Wno-decl -fdump-ir=mem2reg $file * * check-output-start local: .L0: store.32 %arg1 -> 0[s] load.16 %r1 <- 2[s] trunc.6 %r2 <- (16) %r1 sext.16 %r3 <- (6) %r2 ret.16 %r3 foo: .L2: store.32 %arg1 -> 0[s] br .L6 .L6: load.16 %r5 <- 2[s] trunc.6 %r6 <- (16) %r5 setne.1 %r7 <- %r6, $0 cbr %r7, .L6, .L5 .L5: ret * check-output-end */ sparse-0.6.4/validation/mem2reg/packed-bitfield.c000066400000000000000000000007111411531012200216460ustar00rootroot00000000000000struct s { int:16; int f:16; } __attribute__((__packed__)); static void foo(struct s s) { while (s.f) ; } /* * check-name: packed-bitfield * check-command: test-linearize -fmem2reg $file * * check-output-contains: store.32 * check-output-contains: load.16 * * check-output-start foo: .L0: store.32 %arg1 -> 0[s] br .L4 .L4: load.16 %r1 <- 2[s] cbr %r1, .L4, .L3 .L3: ret * check-output-end */ sparse-0.6.4/validation/mem2reg/quadra00.c000066400000000000000000000006541411531012200202620ustar00rootroot00000000000000#define TEST(N) \ do { \ d = b + a[N]; \ if (d < b) \ c++; \ b = d; \ } while (0) int foo(int *a, int b, int c) { int d; TEST(0); TEST(1); TEST(2); return d + c; } /* * check-name: quadratic phisrc * check-command: test-linearize -Wno-decl $file * check-output-ignore * check-output-excludes: phi\\..*, .*, .* * check-output-excludes: phi\\..*, .*, .*, .* * check-output-pattern(6): phisrc\\. */ sparse-0.6.4/validation/mem2reg/quadra01.c000066400000000000000000000010601411531012200202530ustar00rootroot00000000000000#include "repeat.h" void use(void *, void *, void *, void *); void *def(void); #define BLOCK(n) { \ void *label; \ use(&&w##n, &&x##n, &&y##n, &&z##n); \ w##n: label = def(); goto *label; \ x##n: label = def(); goto *label; \ y##n: label = def(); goto *label; \ z##n: label = def(); goto *label; \ } static void foo(void) { REPEAT2(5, BLOCK) } /* * check-name: quadratic @ liveness * check-command: test-linearize -I. $file * check-timeout: * * check-output-ignore * check-output-excludes: phi\\. * check-output-excludes: phisrc\\. */ sparse-0.6.4/validation/mem2reg/quadra02.c000066400000000000000000000005031411531012200202550ustar00rootroot00000000000000#include "repeat.h" #define PAT(X) int a##X = X; static void foo(void) { REPEAT2(12, PAT) } /* * check-name: quadratic vars * check-command: test-linearize -I. $file * check-timeout: * * check-output-ignore * check-output-excludes: phi\\. * check-output-excludes: phisrc\\. * check-output-excludes: store\\. */ sparse-0.6.4/validation/mem2reg/reload-aliasing.c000066400000000000000000000010321411531012200216670ustar00rootroot00000000000000extern int g, h; void f00(int *s) { g = *s; h = *s; } void f01(int *a, int *b, int *s) { *a = *s; *b = *s; } /* * check-name: reload-aliasing.c * check-command: test-linearize -Wno-decl $file * * check-output-start f00: .L0: load.32 %r2 <- 0[%arg1] store.32 %r2 -> 0[g] load.32 %r4 <- 0[%arg1] store.32 %r4 -> 0[h] ret f01: .L2: load.32 %r6 <- 0[%arg3] store.32 %r6 -> 0[%arg1] load.32 %r9 <- 0[%arg3] store.32 %r9 -> 0[%arg2] ret * check-output-end */ sparse-0.6.4/validation/mem2reg/short-load.c000066400000000000000000000006221411531012200207140ustar00rootroot00000000000000#ifdef __SIZEOF_INT__ == 4 typedef unsigned int u32; #endif #ifdef __SIZEOF_SHORT__ == 2 typedef unsigned short u16; #endif union u { u32 a; u16 b; }; void bar(u16, union u); void foo(u16 val) { union u u; u.b = val; bar(u.b, u); } /* * check-name: short-load * check-command: test-linearize -Wno-decl -fdump-ir=mem2reg $file * check-output-ignore * check-output-contains: load\\.32 */ sparse-0.6.4/validation/mem2reg/store-deadborn.c000066400000000000000000000001221411531012200215430ustar00rootroot00000000000000static void foo(int a) { return; a = 0; } /* * check-name: store-deadborn */ sparse-0.6.4/validation/mem2reg/stray-phisrc.c000066400000000000000000000005071411531012200212720ustar00rootroot00000000000000static int foo(int **g) { int i = 1; int *a[2]; int **p; a[1] = &i; if (g) p = g; else p = &a[0]; p += 1; // will point to a[1] = &i if (!g) **p = 0; return i; } /* * check-name: stray phisrc * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-excludes: phisrc\\. */ sparse-0.6.4/validation/mem2reg/struct.c000066400000000000000000000004641411531012200201700ustar00rootroot00000000000000struct s { int a; int b; }; int f0(void) { struct s s; s.a = 0; s.b = 1; return s.a; } int f1(void) { struct s s; s.a = 1; s.b = 0; return s.b; } /* * check-name: struct * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-pattern(2): ret.32 *\\$0 */ sparse-0.6.4/validation/mem2reg/undef00.c000066400000000000000000000005711411531012200201040ustar00rootroot00000000000000static int badr(void) { int *a; return *a; } static void badw(int v) { int *a; *a = v; } /* * check-name: undef00 * check-command: test-linearize -fdump-ir=mem2reg $file * check-output-ignore * check-output-pattern(1): load\\. * check-output-pattern(1): load\\..*\\[UNDEF\\] * check-output-pattern(1): store\\. * check-output-pattern(1): store\\..*\\[UNDEF\\] */ sparse-0.6.4/validation/mem2reg/undef01.c000066400000000000000000000004121411531012200200770ustar00rootroot00000000000000static void foo(void) { int *b; for (;;) *b++ = 0; } /* * check-name: undef01 * check-command: sparse -Wmaybe-uninitialized $file * check-known-to-fail * * check-error-start crazy04.c:3:13: warning: variable 'b' may be uninitialized * check-error-end */ sparse-0.6.4/validation/mem2reg/unused-var.c000066400000000000000000000003661411531012200207360ustar00rootroot00000000000000int foo(int a) { switch (a) { int u = 1; default: return a; } } /* * check-name: unused-var * check-command: test-linearize -Wno-decl $file * * check-output-start foo: .L0: ret.32 %arg1 * check-output-end */ sparse-0.6.4/validation/mem2reg/volatile-store00.c000066400000000000000000000005171411531012200217540ustar00rootroot00000000000000void foo(volatile int *p) { *p = 0; *p = 0; } void bar(void) { extern volatile int i; i = 0; i = 0; } void baz(void) { volatile int i; i = 0; i = 0; } /* * check-name: keep volatile stores * check-command: test-linearize -Wno-decl -fdump-ir=mem2reg $file * check-output-ignore * check-output-pattern(1,6): store\\. */ sparse-0.6.4/validation/member_of_typeof.c000066400000000000000000000003361411531012200206250ustar00rootroot00000000000000static 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.6.4/validation/memops-volatile.c000066400000000000000000000005261411531012200204220ustar00rootroot00000000000000static int foo(volatile int *a, int v) { *a = v; *a = 0; return *a; } /* * check-name: memops-volatile * check-command: test-linearize $file * * check-output-ignore * check-output-contains: store\\..*%arg2 -> 0\\[%arg1] * check-output-contains: store\\..*\\$0 -> 0\\[%arg1] * check-output-contains: load\\..*%r.* <- 0\\[%arg1] */ sparse-0.6.4/validation/memops/000077500000000000000000000000001411531012200164365ustar00rootroot00000000000000sparse-0.6.4/validation/memops/kill-dead-loads00.c000066400000000000000000000004511411531012200216700ustar00rootroot00000000000000void fun(void); void foo(int *p) { for (*p; *p; *p) { l: fun(); } if (0) goto l; } /* * check-name: kill-dead-loads00 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-excludes: phi\\. * check-output-pattern(1): load\\. * check-output-end */ sparse-0.6.4/validation/memops/kill-dead-store-parent0.c000066400000000000000000000003311411531012200231260ustar00rootroot00000000000000void foo(int *ptr, int p) { if (p) *ptr = 1; *ptr = 0; } /* * check-name: kill-dead-store-parent0 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-pattern(1): store */ sparse-0.6.4/validation/memops/kill-dead-store-parent2.c000066400000000000000000000005221411531012200231320ustar00rootroot00000000000000int ladder02(int *ptr, int p, int x) { *ptr = x++; if (p) goto l11; else goto l12; l11: *ptr = x++; goto l20; l12: *ptr = x++; goto l20; l20: *ptr = x++; return *ptr; } /* * check-name: kill-dead-store-parent2 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-pattern(1): store */ sparse-0.6.4/validation/memops/kill-redundant-store0.c000066400000000000000000000003111411531012200227240ustar00rootroot00000000000000void foo(int *ptr) { int i = *ptr; *ptr = i; } /* * check-name: kill-redundant-store0 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-excludes: store */ sparse-0.6.4/validation/memops/partial-load00.c000066400000000000000000000005661411531012200213220ustar00rootroot00000000000000union u { double d; int i[2]; }; void use(union u); int foo(double x, double y) { union u u; int r; u.d = x; r = u.i[0]; u.d = y; use(u); return r; } /* * check-name: partial-load00 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-contains: store\\. * check-output-contains: load\\. * check-output-returns: %r2 */ sparse-0.6.4/validation/memops/type-punning-flt2int.c000066400000000000000000000004711411531012200226210ustar00rootroot00000000000000union u { int i; float f; }; static int foo(void) { union u u = { .f = 0.123 }; return u.i; } /* * check-name: type-punning-float-to-int * check description: must not infer the int value from the float * check-command: test-linearize $file * * check-output-ignore * check-output-contains: load\\. */ sparse-0.6.4/validation/memops/type-punning-int2flt.c000066400000000000000000000004671411531012200226260ustar00rootroot00000000000000union u { int i; float f; }; static float foo(void) { union u u = { .i = 3 }; return u.f; } /* * check-name: type-punning-int-to-float * check description: must not infer the float value from the int * check-command: test-linearize $file * * check-output-ignore * check-output-contains: load\\. */ sparse-0.6.4/validation/missing-ident.c000066400000000000000000000007251411531012200200600ustar00rootroot00000000000000int [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.6.4/validation/missing-return.c000066400000000000000000000005341411531012200202720ustar00rootroot00000000000000int foo(int a) { } int bar(int a) { if (a) return 0; } /* * check-name: missing return * check-command: sparse -Wno-decl $file * check-known-to-fail * * check-error-start missing-return.c:3:1: warning: control reaches end of non-void function missing-return.c:9:1: warning: control reaches end of non-void function * check-error-end */ sparse-0.6.4/validation/multi-input.c000066400000000000000000000002601411531012200175670ustar00rootroot00000000000000int a = 1; int foo(void) {} static int b = 1; static int bar(void) {} /* * check-name: multi-input * check-command: sparse -Wno-decl $file $file * check-known-to-fail */ sparse-0.6.4/validation/multi_typedef.c000066400000000000000000000003611411531012200201540ustar00rootroot00000000000000typedef 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.6.4/validation/nested-declarator.c000066400000000000000000000014531411531012200207050ustar00rootroot00000000000000typedef 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.6.4/validation/nested-declarator2.c000066400000000000000000000023301411531012200207620ustar00rootroot00000000000000typedef 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.6.4/validation/nested-functions.c000066400000000000000000000011001411531012200205620ustar00rootroot00000000000000static int test_ok(int a, int b) { int nested_ok(int i) { return i * 2; } return nested_ok(b); } static int test_ko(int a, int b) { int nested_ko(int i) { return i * 2 + a; } return nested_ko(b); } static int test_inline(int a, int b) { inline int nested(int i) { return i * 2; } return nested(b); } static int test_inline_ko(int a, int b) { inline int nested(int i) { return i * 2 + a; } return nested(b); } /* * check-name: nested-functions * * check-error-start nested-functions.c:32:32: warning: unreplaced symbol 'a' * check-error-end */ sparse-0.6.4/validation/nocast.c000066400000000000000000000120371411531012200165740ustar00rootroot00000000000000#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; /* 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; } static void assign_value(void) { ulong_nc_t good_assign_self = t; unsigned long good_assign_sametype = t; } /* * 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.6.4/validation/noderef.c000066400000000000000000000014131411531012200167230ustar00rootroot00000000000000# 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.6.4/validation/non-pointer-null.c000066400000000000000000000002771411531012200205300ustar00rootroot00000000000000static 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.6.4/validation/old-initializer-nowarn.c000066400000000000000000000002471411531012200217060ustar00rootroot00000000000000struct 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.6.4/validation/old-initializer.c000066400000000000000000000003271411531012200204030ustar00rootroot00000000000000struct 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.6.4/validation/old-style-definition0.c000066400000000000000000000003071411531012200214240ustar00rootroot00000000000000extern int foo(int a, void *b); int foo(a, b) int a; void *b; { if (b) return a; } /* * check-name: old-stype-definition disabled * check-command: sparse -Wno-old-style-definition $file */ sparse-0.6.4/validation/old-style-definition1.c000066400000000000000000000004721411531012200214300ustar00rootroot00000000000000extern int foo(int a, void *b); int foo(a, b) int a; void *b; { if (b) return a; } /* * check-name: old-stype-definition enabled * check-command: sparse -Wold-style-definition $file * * check-error-start old-style-definition1.c:4:9: warning: non-ANSI definition of function 'foo' * check-error-end */ sparse-0.6.4/validation/optim/000077500000000000000000000000001411531012200162665ustar00rootroot00000000000000sparse-0.6.4/validation/optim/address-used01.c000066400000000000000000000004131411531012200211540ustar00rootroot00000000000000int foo(int **g, int j) { int i = 1; int *a; int **p; a = &i; p = &a; *p[0] = 0; return i; } /* * check-name: address-used01 * check-command: test-linearize -Wno-decl -fdump-ir=final $file * check-output-ignore * check-output-contains: ret\\..* \\$0 */ sparse-0.6.4/validation/optim/and-extend.c000066400000000000000000000007501411531012200204630ustar00rootroot00000000000000typedef unsigned short u16; typedef short s16; typedef unsigned int u32; typedef int s32; u32 ufoo(u32 x) { u16 i = ((u16)x) & 0x7fffU; return i; } u32 sfoo(u32 x) { s16 i = ((s16)x) & 0x7fff; return i; } /* * check-name: and-extend * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-excludes: trunc\\. * check-output-excludes: zext\\. * check-output-excludes: sext\\. * check-output-contains: and\\.32.*0x7fff */ sparse-0.6.4/validation/optim/and-lsr.c000066400000000000000000000004471411531012200177770ustar00rootroot00000000000000// (x & M) >> S to (x >> S) & (M >> S) unsigned int foo(unsigned int x) { return (x & 0xffff) >> 12; } /* * check-name: and-lsr * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-contains: and\\..*\\$15 * check-output-excludes: and\\..*\\$0xffff */ sparse-0.6.4/validation/optim/and-or-bf0.c000066400000000000000000000004721411531012200202620ustar00rootroot00000000000000struct s { int f:3; }; void foo(struct s *p, int a) { p->f = 1; p->f = a; } void bar(struct s *p, int a) { p->f = a; p->f = 1; } /* * check-name: and-or-bf0 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-pattern(3): and\\. * check-output-pattern(2): or\\. */ sparse-0.6.4/validation/optim/and-or-bf1.c000066400000000000000000000004331411531012200202600ustar00rootroot00000000000000struct s { int :2; int f:3; }; void foo(struct s *d, const struct s *s, int a) { d->f = s->f | a; } /* * check-name: and-or-bf1 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-pattern(2): and\\. * check-output-pattern(2): or\\. */ sparse-0.6.4/validation/optim/and-or-bf2.c000066400000000000000000000004511411531012200202610ustar00rootroot00000000000000struct s { char a:3; char b:3; char c:2; }; void foo(struct s *p) { p->a = 1; p->b = 2; p->c = 3; } /* * check-name: and-or-bf2 * check-command: test-linearize -Wno-decl $file * * check-output-start foo: .L0: store.8 $209 -> 0[%arg1] ret * check-output-end */ sparse-0.6.4/validation/optim/and-or-bfs.c000066400000000000000000000006441411531012200203660ustar00rootroot00000000000000struct s { signed int :2; signed int f:3; }; int bfs(struct s s, int a) { s.f = a; return s.f; } /* * check-name: and-or-bfs * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-pattern(1): trunc\\. * check-output-pattern(1): sext\\. * check-output-excludes: and\\. * check-output-excludes: or\\. * check-output-excludes: shl\\. * check-output-excludes: lsr\\. */ sparse-0.6.4/validation/optim/and-or-bfu.c000066400000000000000000000005351411531012200203670ustar00rootroot00000000000000struct u { unsigned int :2; unsigned int f:3; }; int bfu(struct u s, int a) { s.f = a; return s.f; } /* * check-name: and-or-bfu * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-pattern(1): and\\. * check-output-excludes: or\\. * check-output-excludes: shl\\. * check-output-excludes: lsr\\. */ sparse-0.6.4/validation/optim/and-or-bfx.c000066400000000000000000000004111411531012200203630ustar00rootroot00000000000000struct s { int f:3; }; void foo(struct s *p, int a, int b) { p->f = a; p->f = b; } /* * check-name: and-or-bfx * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-pattern(2): and\\. * check-output-pattern(1): or\\. */ sparse-0.6.4/validation/optim/and-or-constant0.c000066400000000000000000000003101411531012200215130ustar00rootroot00000000000000int foo(int x) { return (x | 0xfffff000) & 0xfff; } /* * check-name: and-or-constant0 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-excludes: or\\. */ sparse-0.6.4/validation/optim/and-or-constant1.c000066400000000000000000000004241411531012200215220ustar00rootroot00000000000000int foo(int x) { return (x | 0x000fffff) & 0xfff; } /* * check-name: or-and-constant1 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-contains: ret\\..*\\$0xfff * check-output-excludes: and\\. * check-output-excludes: or\\. */ sparse-0.6.4/validation/optim/and-or-constant2.c000066400000000000000000000004011411531012200215160ustar00rootroot00000000000000int foo(int x) { return (x | 0xfffffff0) & 0xfff; } /* * check-name: and-or-constant2 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-contains: or\\..*\\$0xff0 * check-output-excludes: or\\..*\\$0xfffffff0 */ sparse-0.6.4/validation/optim/and-or-crash.c000066400000000000000000000002151411531012200207060ustar00rootroot00000000000000static unsigned a(unsigned b, unsigned c) { (c << 1 | b & 1 << 1) >> 1; } /* * check-name: catch crashes during AND-OR simplifications */ sparse-0.6.4/validation/optim/and-or-lsr0.c000066400000000000000000000003451411531012200204720ustar00rootroot00000000000000int foo(int a, int b) { return ((a & 0x00000fff) | b) >> 12; } /* * check-name: and-or-lsr0 * check-command: test-linearize -Wno-decl $file * check-known-to-fail * * check-output-ignore * check-output-excludes: or\\. */ sparse-0.6.4/validation/optim/and-or-lsr1.c000066400000000000000000000003631411531012200204730ustar00rootroot00000000000000int foo(int a, int b) { return ((a & 0xfffff000) | b) >> 12; } /* * check-name: and-or-lsr1 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-pattern(0): and\\. * check-output-pattern(1): or\\. */ sparse-0.6.4/validation/optim/and-or-lsr2.c000066400000000000000000000004131411531012200204700ustar00rootroot00000000000000int foo(int x, int y) { return ((x & 0xf0ffffff) | y) >> 12; } /* * check-name: and-or-lsr2 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-contains: and\\..*\\$0xf0fff * check-output-excludes: and\\..*\\$0xf0ffffff */ sparse-0.6.4/validation/optim/and-or-lsrx.c000066400000000000000000000004361411531012200206030ustar00rootroot00000000000000unsigned int foo(unsigned int x, unsigned int y, unsigned int a) { return ((x & y) | (a & 0x0fff)) >> 12; } /* * check-name: and-or-lsrx * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-pattern(1): and\\. * check-output-excludes: or\\. */ sparse-0.6.4/validation/optim/and-or-mask.c000066400000000000000000000003471411531012200205470ustar00rootroot00000000000000int foo(int a, int b) { return ((a & 7) | (b & 3)) & 8; } /* * check-name: and-or-mask * check-command: test-linearize -Wno-decl $file * * check-output-start foo: .L0: ret.32 $0 * check-output-end */ sparse-0.6.4/validation/optim/and-or-mask0.c000066400000000000000000000003211411531012200206170ustar00rootroot00000000000000int foo(int a, int b) { return ((a & 0xfffff000) | b) & 0xfff; } /* * check-name: and-or-mask0 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-excludes: or\\. */ sparse-0.6.4/validation/optim/and-or-mask1.c000066400000000000000000000003661411531012200206310ustar00rootroot00000000000000int foo(int a, int b) { return ((a & 0x0fffffff) | b) & 0xfff; } /* * check-name: and-or-mask1 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-pattern(1): and\\. * check-output-pattern(1): or\\. */ sparse-0.6.4/validation/optim/and-or-mask2.c000066400000000000000000000004141411531012200206240ustar00rootroot00000000000000int foo(int x, int y) { return ((x & 0xffffff0f) | y) & 0xfff; } /* * check-name: and-or-mask2 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-contains: and\\..*\\$0xf0f * check-output-excludes: and\\..*\\$0xffffff0f */ sparse-0.6.4/validation/optim/and-or-mask3s.c000066400000000000000000000007371411531012200210200ustar00rootroot00000000000000#define W 3 #define S 8 #define M (W << S) static inline int fun(unsigned int x, unsigned int y) { return ((x & M) | (y << S)) >> S; } short foo(unsigned int x, unsigned int y) { return fun(x, y) & W; } /* * check-name: and-or-mask3s * check-command: test-linearize -Wno-decl $file * check-known-to-fail * * check-output-ignore * check-output-pattern(1): lsr\\. * check-output-pattern(1): or\\. * check-output-pattern(1): and\\. * check-output-excludes: shl\\. */ sparse-0.6.4/validation/optim/and-or-mask3u.c000066400000000000000000000007351411531012200210200ustar00rootroot00000000000000#define W 3 #define S 8 #define M (W << S) static inline int fun(unsigned int x, unsigned int y) { return ((x & M) | (y << S)) >> S; } int foo(unsigned int x, unsigned int y) { return fun(x, y) & W; } /* * check-name: and-or-mask3u * check-command: test-linearize -Wno-decl $file * check-known-to-fail * * check-output-ignore * check-output-pattern(1): lsr\\. * check-output-pattern(1): or\\. * check-output-pattern(1): and\\. * check-output-excludes: shl\\. */ sparse-0.6.4/validation/optim/and-or-mask4.c000066400000000000000000000007341411531012200206330ustar00rootroot00000000000000#define W 3 #define S 8 #define M (W << S) static inline int fun(unsigned int x, unsigned int y) { return ((x & W) | (y >> S)) << S; } int foo(unsigned int x, unsigned int y) { return fun(x, y) & M; } /* * check-name: and-or-mask4 * check-command: test-linearize -Wno-decl $file * check-known-to-fail * * check-output-ignore * check-output-pattern(1): shl\\. * check-output-pattern(1): or\\. * check-output-pattern(1): and\\. * check-output-excludes: lsr\\. */ sparse-0.6.4/validation/optim/and-or-maskx.c000066400000000000000000000003761411531012200207410ustar00rootroot00000000000000int foo(int x, int y, int a) { return ((x & y) | (a & 0xf000)) & 0x0fff; } /* * check-name: and-or-maskx * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-pattern(2): and\\. * check-output-excludes: or\\. */ sparse-0.6.4/validation/optim/and-or-shl0.c000066400000000000000000000003161411531012200204560ustar00rootroot00000000000000int foo(int a, int b) { return ((a & 0xfff00000) | b) << 12; } /* * check-name: and-or-shl0 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-excludes: or\\. */ sparse-0.6.4/validation/optim/and-or-shl1.c000066400000000000000000000003631411531012200204610ustar00rootroot00000000000000int foo(int a, int b) { return ((a & 0x000fffff) | b) << 12; } /* * check-name: and-or-shl1 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-pattern(0): and\\. * check-output-pattern(1): or\\. */ sparse-0.6.4/validation/optim/and-or-shl2.c000066400000000000000000000004131411531012200204560ustar00rootroot00000000000000int foo(int x, int y) { return ((x & 0xffffff0f) | y) << 12; } /* * check-name: and-or-shl2 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-contains: and\\..*\\$0xfff0f * check-output-excludes: and\\..*\\$0xffffff0f */ sparse-0.6.4/validation/optim/and-or-shlx.c000066400000000000000000000004421411531012200205660ustar00rootroot00000000000000unsigned int foo(unsigned int x, unsigned int y, unsigned int a) { return ((x & y) | (a & 0xfff00000)) << 12; } /* * check-name: and-or-shlx * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-pattern(1): and\\. * check-output-excludes: or\\. */ sparse-0.6.4/validation/optim/and-or-trunc0.c000066400000000000000000000003461411531012200210260ustar00rootroot00000000000000char foo(int x, int y) { return (x & 0xff00) | y; } /* * check-name: and-or-trunc0 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-excludes: and\\. * check-output-excludes: or\\. */ sparse-0.6.4/validation/optim/and-or-trunc1.c000066400000000000000000000003061411531012200210230ustar00rootroot00000000000000char foo(int x, int y) { return (x & 0xffff) | y; } /* * check-name: and-or-trunc1 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-excludes: and\\. */ sparse-0.6.4/validation/optim/and-or-trunc2.c000066400000000000000000000003611411531012200210250ustar00rootroot00000000000000char foo(int x, int y) { return (x & 0xff07) | y; } /* * check-name: and-or-trunc2 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-pattern(1): and\\. * check-output-pattern(1): and\\..*\\$7 */ sparse-0.6.4/validation/optim/and-or-truncx.c000066400000000000000000000003651411531012200211370ustar00rootroot00000000000000char foo(int x, int y, int b) { return (x & y) | (b & 0xff00); } /* * check-name: and-or-truncx * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-pattern(1): and\\. * check-output-excludes: or\\. */ sparse-0.6.4/validation/optim/and-trunc.c000066400000000000000000000005631411531012200203310ustar00rootroot00000000000000short smask(short x) { return x & (short) 0x7fff; } short umask(unsigned short x) { return x & (unsigned short) 0x7fff; } /* * check-name: and-trunc * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-excludes: sext\\. * check-output-excludes: zext\\. * check-output-excludes: trunc\\. * check-output-contains: and\\.16 */ sparse-0.6.4/validation/optim/bad-phisrc1.c000066400000000000000000000003651411531012200205330ustar00rootroot00000000000000void foo(int a, int b) { if (b) while ((a += 5) > a) ; } /* * check-name: bad-phisrc1 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-excludes: phi\\. * check-output-excludes: phisource\\. */ sparse-0.6.4/validation/optim/bad-phisrc1a.c000066400000000000000000000004731411531012200206740ustar00rootroot00000000000000int def(void); int fun4(struct xfrm_state *net, int cnt) { int err = 0; if (err) goto out; for (; net;) err = def(); if (cnt) out: return err; return 0; } /* * check-name: bad-phisrc1a * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-contains: select\\. */ sparse-0.6.4/validation/optim/bad-phisrc2.c000066400000000000000000000003501411531012200205260ustar00rootroot00000000000000int bad_phisrc2(int p, int a, int r) { if (p) r = a; else if (r) ; return r; } /* * check-name: bad-phisrc2 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-contains: select\\. */ sparse-0.6.4/validation/optim/bad-phisrc3.c000066400000000000000000000004301411531012200205260ustar00rootroot00000000000000void foo(void) { int c = 1; switch (3) { case 0: do { ; case 3: ; } while (c++); } } /* * check-name: bad-phisrc3 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-pattern(2): phisrc\\. * check-output-pattern(1): phi\\. */ sparse-0.6.4/validation/optim/binops-same-args.c000066400000000000000000000024711411531012200216050ustar00rootroot00000000000000typedef 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.6.4/validation/optim/bitfield-init-zero.c000066400000000000000000000024551411531012200221400ustar00rootroot00000000000000struct bfu { unsigned int a:11; unsigned int f:9; unsigned int :2; unsigned int z:3; }; struct bfu bfuu_init(unsigned int a) { struct bfu bf = { .f = a, }; return bf; } struct bfu bfus_init(int a) { struct bfu bf = { .f = a, }; return bf; } unsigned int bfu_get0(void) { struct bfu bf = { }; return bf.f; } struct bfs { signed int a:11; signed int f:9; signed int :2; signed int z:3; }; struct bfs bfsu_init(unsigned int a) { struct bfs bf = { .f = a, }; return bf; } struct bfs bfss_init(int a) { struct bfs bf = { .f = a, }; return bf; } int bfs_get0(void) { struct bfs bf = { }; return bf.f; } /* * check-name: bitfield implicit init zero * check-command: test-linearize -Wno-decl $file * * check-output-start bfuu_init: .L0: and.32 %r4 <- %arg1, $511 shl.32 %r5 <- %r4, $11 ret.32 %r5 bfus_init: .L2: and.32 %r13 <- %arg1, $511 shl.32 %r14 <- %r13, $11 ret.32 %r14 bfu_get0: .L4: ret.32 $0 bfsu_init: .L6: and.32 %r27 <- %arg1, $511 shl.32 %r28 <- %r27, $11 ret.32 %r28 bfss_init: .L8: and.32 %r36 <- %arg1, $511 shl.32 %r37 <- %r36, $11 ret.32 %r37 bfs_get0: .L10: ret.32 $0 * check-output-end */ sparse-0.6.4/validation/optim/bitfield-size.c000066400000000000000000000016461411531012200211730ustar00rootroot00000000000000struct 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; } /* * check-name: bitfield size * check-command: test-linearize -Wno-decl $file * check-output-ignore * * check-output-excludes: and\\..*\\$960 * check-output-excludes: zext\\. * check-output-pattern(4): and\\..*\\$15 * check-output-pattern(4): sext\\. * check-output-pattern(4): trunc\\.4 * check-output-pattern(4): lsr\\..*\\$6 */ sparse-0.6.4/validation/optim/bitfield-store-load0.c000066400000000000000000000010371411531012200223440ustar00rootroot00000000000000int ufoo(unsigned int a) { struct u { unsigned int :2; unsigned int a:3; } bf; bf.a = a; return bf.a; } int sfoo(int a) { struct s { signed int :2; signed int a:3; } bf; bf.a = a; return bf.a; } /* * check-name: optim store/load bitfields * check-command: test-linearize -Wno-decl $file * * check-output-start ufoo: .L0: and.32 %r11 <- %arg1, $7 ret.32 %r11 sfoo: .L2: trunc.3 %r16 <- (32) %arg1 sext.32 %r23 <- (3) %r16 ret.32 %r23 * check-output-end */ sparse-0.6.4/validation/optim/bitfield-store-loads.c000066400000000000000000000006661411531012200224560ustar00rootroot00000000000000struct s { unsigned char :2; unsigned char f:3; }; int foo(struct s s, int a) { s.f = a; return s.f; } /* * check-name: bitfield-store-load signed * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-excludes: shl\\. * check-output-excludes: lsr\\. * check-output-excludes: or\\. * check-output-excludes: [sz]ext\\. * check-output-excludes: trunc\\. * check-output-pattern(1): and\\. */ sparse-0.6.4/validation/optim/bitfield-store-loadu.c000066400000000000000000000005561411531012200224560ustar00rootroot00000000000000struct s { unsigned int :2; unsigned int f:3; }; int foo(struct s s, int a) { s.f = a; return s.f; } /* * check-name: bitfield-store-load unsigned * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-excludes: shl\\. * check-output-excludes: lsr\\. * check-output-excludes: or\\. * check-output-pattern(1): and\\. */ sparse-0.6.4/validation/optim/bits-not-zero.c000066400000000000000000000006561411531012200211550ustar00rootroot00000000000000int or_not0(int a) { return a | ~0; } int and_not0(int a) { return a & ~0; } int xor_not0(int a) { return a ^ ~0; } /* * check-name: bool-not-zero * check-command: test-linearize -Wno-decl $file * * check-output-start or_not0: .L0: ret.32 $0xffffffff and_not0: .L2: ret.32 %arg1 xor_not0: .L4: not.32 %r8 <- %arg1 ret.32 %r8 * check-output-end */ sparse-0.6.4/validation/optim/bool-context-fp.c000066400000000000000000000032651411531012200214600ustar00rootroot00000000000000#define bool _Bool bool bfimp(float a) { return a; } bool bfexp(float a) { return (bool)a; } bool bfnot(float a) { return !a; } int ifnot(float a) { return !a; } bool bfior(float a, float b) { return a || b; } int ifior(float a, float b) { return a || b; } bool bfand(float a, float b) { return a && b; } int ifand(float a, float b) { return a && b; } /* * check-name: bool context fp * check-command: test-linearize -Wno-decl $file * * check-output-start bfimp: .L0: setfval.32 %r2 <- 0.000000e+00 fcmpune.1 %r3 <- %arg1, %r2 ret.1 %r3 bfexp: .L2: setfval.32 %r6 <- 0.000000e+00 fcmpune.1 %r7 <- %arg1, %r6 ret.1 %r7 bfnot: .L4: setfval.32 %r10 <- 0.000000e+00 fcmpoeq.1 %r12 <- %arg1, %r10 ret.1 %r12 ifnot: .L6: setfval.32 %r15 <- 0.000000e+00 fcmpoeq.32 %r16 <- %arg1, %r15 ret.32 %r16 bfior: .L8: setfval.32 %r19 <- 0.000000e+00 fcmpune.1 %r20 <- %arg1, %r19 fcmpune.1 %r23 <- %arg2, %r19 or.1 %r24 <- %r20, %r23 ret.1 %r24 ifior: .L10: setfval.32 %r29 <- 0.000000e+00 fcmpune.1 %r30 <- %arg1, %r29 fcmpune.1 %r33 <- %arg2, %r29 or.1 %r34 <- %r30, %r33 zext.32 %r35 <- (1) %r34 ret.32 %r35 bfand: .L12: setfval.32 %r38 <- 0.000000e+00 fcmpune.1 %r39 <- %arg1, %r38 fcmpune.1 %r42 <- %arg2, %r38 and.1 %r43 <- %r39, %r42 ret.1 %r43 ifand: .L14: setfval.32 %r48 <- 0.000000e+00 fcmpune.1 %r49 <- %arg1, %r48 fcmpune.1 %r52 <- %arg2, %r48 and.1 %r53 <- %r49, %r52 zext.32 %r54 <- (1) %r53 ret.32 %r54 * check-output-end */ sparse-0.6.4/validation/optim/bool-context.c000066400000000000000000000004211411531012200210440ustar00rootroot00000000000000#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): setne\\..* %arg[12] */ sparse-0.6.4/validation/optim/bool-eq0.c000066400000000000000000000004341411531012200200510ustar00rootroot00000000000000int beq0(int a) { return a == 0; } int bnotne0(int a) { return !(a != 0); } int bnot(int a) { return !a; } /* * check-name: bool-eq0 * check-command: test-linearize -Wno-decl $file * check-output-ignore * * check-output-excludes: setne\\. * check-output-contains: seteq\\. */ sparse-0.6.4/validation/optim/bool-int-bool.c000066400000000000000000000005041411531012200211050ustar00rootroot00000000000000_Bool beq0(_Bool a) { return (a == 0); } _Bool beq1(_Bool a) { return (a == 1); } _Bool bne0(_Bool a) { return (a != 0); } _Bool bne1(_Bool a) { return (a != 1); } /* * check-name: bool - int - bool constants * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-excludes: cast\\. */ sparse-0.6.4/validation/optim/bool-ne0.c000066400000000000000000000004421411531012200200450ustar00rootroot00000000000000int bne0(int a) { return a != 0; } int bnoteq0(int a) { return !(a == 0); } int bnotnot(int a) { return !(!a); } /* * check-name: bool-ne0 * check-command: test-linearize -Wno-decl $file * check-output-ignore * * check-output-excludes: seteq\\. * check-output-contains: setne\\. */ sparse-0.6.4/validation/optim/bool-neq0.c000066400000000000000000000004441411531012200202300ustar00rootroot00000000000000int bneq0(int a) { return a != 0; } int bnoteq0(int a) { return !(a == 0); } int bnotnot(int a) { return !(!a); } /* * check-name: bool-neq0 * check-command: test-linearize -Wno-decl $file * check-output-ignore * * check-output-excludes: seteq\\. * check-output-contains: setne\\. */ sparse-0.6.4/validation/optim/bool-same-args.c000066400000000000000000000004471411531012200212470ustar00rootroot00000000000000static 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.6.4/validation/optim/bool-sext-test.c000066400000000000000000000005171411531012200213260ustar00rootroot00000000000000_Bool eqs0( signed char a) { return a == 0; } _Bool eqs1( signed char a) { return a == 1; } _Bool nes0( signed char a) { return a != 0; } _Bool nes1( signed char a) { return a != 1; } /* * check-name: bool-sext-test * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-excludes: sext\\. */ sparse-0.6.4/validation/optim/bool-simplify.c000066400000000000000000000013761411531012200212260ustar00rootroot00000000000000int 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; } // try again but with something true but != 1 int and_2(int a) { return a && 2; } int or_2(int a) { return a || 2; } /* * check-name: bool-simplify * check-command: test-linearize -Wno-decl $file * * check-output-start and_0: .L0: ret.32 $0 and_1: .L2: setne.32 %r9 <- %arg1, $0 ret.32 %r9 or_0: .L4: setne.32 %r14 <- %arg1, $0 ret.32 %r14 or_1: .L6: ret.32 $1 and_2: .L8: setne.32 %r25 <- %arg1, $0 ret.32 %r25 or_2: .L10: ret.32 $1 * check-output-end */ sparse-0.6.4/validation/optim/bool-simplify2.c000066400000000000000000000101741411531012200213040ustar00rootroot00000000000000typedef unsigned int uint; typedef _Bool bool; static uint ini(uint a) { return !a; } static bool bni(uint a) { return !a; } static uint ioii(uint a, uint b) { return a || b; } static uint iaii(uint a, uint b) { return a && b; } static bool boii(uint a, uint b) { return a || b; } static bool baii(uint a, uint b) { return a && b; } static uint ioiii(uint a, uint b, uint c) { return a || b || c; } static uint iaiii(uint a, uint b, uint c) { return a && b && c; } static bool boiii(uint a, uint b, uint c) { return a || b || c; } static bool baiii(uint a, uint b, uint c) { return a && b && c; } static uint inb(bool a) { return !a; } static bool bnb(bool a) { return !a; } static uint iobb(bool a, bool b) { return a || b; } static uint iabb(bool a, bool b) { return a && b; } static bool bobb(bool a, bool b) { return a || b; } static bool babb(bool a, bool b) { return a && b; } static uint iobbb(bool a, bool b, bool c) { return a || b || c; } static uint iabbb(bool a, bool b, bool c) { return a && b && c; } static bool bobbb(bool a, bool b, bool c) { return a || b || c; } static bool babbb(bool a, bool b, bool c) { return a && b && c; } /* * check-name: bool-simplify2 * check-command: test-linearize $file * * check-output-pattern(20): setne\\. * check-output-pattern(4): seteq\\. * check-output-pattern(8): zext\\. * check-output-pattern(12): and * check-output-pattern(12): or * check-output-end * * check-output-start ini: .L0: seteq.32 %r2 <- %arg1, $0 ret.32 %r2 bni: .L2: seteq.1 %r6 <- %arg1, $0 ret.1 %r6 ioii: .L4: setne.1 %r9 <- %arg1, $0 setne.1 %r11 <- %arg2, $0 or.1 %r12 <- %r9, %r11 zext.32 %r13 <- (1) %r12 ret.32 %r13 iaii: .L6: setne.1 %r16 <- %arg1, $0 setne.1 %r18 <- %arg2, $0 and.1 %r19 <- %r16, %r18 zext.32 %r20 <- (1) %r19 ret.32 %r20 boii: .L8: setne.1 %r23 <- %arg1, $0 setne.1 %r25 <- %arg2, $0 or.1 %r26 <- %r23, %r25 ret.1 %r26 baii: .L10: setne.1 %r31 <- %arg1, $0 setne.1 %r33 <- %arg2, $0 and.1 %r34 <- %r31, %r33 ret.1 %r34 ioiii: .L12: setne.1 %r39 <- %arg1, $0 setne.1 %r41 <- %arg2, $0 or.1 %r42 <- %r39, %r41 setne.1 %r46 <- %arg3, $0 or.1 %r47 <- %r42, %r46 zext.32 %r48 <- (1) %r47 ret.32 %r48 iaiii: .L14: setne.1 %r51 <- %arg1, $0 setne.1 %r53 <- %arg2, $0 and.1 %r54 <- %r51, %r53 setne.1 %r58 <- %arg3, $0 and.1 %r59 <- %r54, %r58 zext.32 %r60 <- (1) %r59 ret.32 %r60 boiii: .L16: setne.1 %r63 <- %arg1, $0 setne.1 %r65 <- %arg2, $0 or.1 %r66 <- %r63, %r65 setne.1 %r70 <- %arg3, $0 or.1 %r71 <- %r66, %r70 ret.1 %r71 baiii: .L18: setne.1 %r76 <- %arg1, $0 setne.1 %r78 <- %arg2, $0 and.1 %r79 <- %r76, %r78 setne.1 %r83 <- %arg3, $0 and.1 %r84 <- %r79, %r83 ret.1 %r84 inb: .L20: seteq.32 %r89 <- %arg1, $0 ret.32 %r89 bnb: .L22: seteq.1 %r93 <- %arg1, $0 ret.1 %r93 iobb: .L24: or.1 %r97 <- %arg1, %arg2 zext.32 %r98 <- (1) %r97 ret.32 %r98 iabb: .L26: and.1 %r102 <- %arg1, %arg2 zext.32 %r103 <- (1) %r102 ret.32 %r103 bobb: .L28: or.1 %r107 <- %arg1, %arg2 ret.1 %r107 babb: .L30: and.1 %r113 <- %arg1, %arg2 ret.1 %r113 iobbb: .L32: or.1 %r119 <- %arg1, %arg2 or.1 %r123 <- %r119, %arg3 zext.32 %r124 <- (1) %r123 ret.32 %r124 iabbb: .L34: and.1 %r128 <- %arg1, %arg2 and.1 %r132 <- %r128, %arg3 zext.32 %r133 <- (1) %r132 ret.32 %r133 bobbb: .L36: or.1 %r137 <- %arg1, %arg2 or.1 %r141 <- %r137, %arg3 ret.1 %r141 babbb: .L38: and.1 %r147 <- %arg1, %arg2 and.1 %r151 <- %r147, %arg3 ret.1 %r151 * check-output-end */ sparse-0.6.4/validation/optim/bool-zext-test.c000066400000000000000000000005171411531012200213350ustar00rootroot00000000000000_Bool equ0(unsigned char a) { return a == 0; } _Bool equ1(unsigned char a) { return a == 1; } _Bool neu0(unsigned char a) { return a != 0; } _Bool neu1(unsigned char a) { return a != 1; } /* * check-name: bool-zext-test * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-excludes: zext\\. */ sparse-0.6.4/validation/optim/call-complex-pointer.c000066400000000000000000000004561411531012200224750ustar00rootroot00000000000000int foo(int p, int (*f0)(int), int (*f1)(int), int arg) { return (p ? f0 : f1)(arg); } /* * check-name: call-complex-pointer * check-command: test-linearize -Wno-decl $file * check-known-to-fail * * check-output-ignore * check-output-excludes: ptrcast\\. * check-output-contains: select\\. */ sparse-0.6.4/validation/optim/call-inlined.c000066400000000000000000000005001411531012200207600ustar00rootroot00000000000000static const char messg[] = "def"; static inline int add(int a, int b) { return a + b; } int foo(int a, int b, int p) { if (p) { add(a + b, 1); return p; } return 0; } /* * check-name: call-inlined * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: %arg3 */ sparse-0.6.4/validation/optim/canonical-abs.c000066400000000000000000000005301411531012200211220ustar00rootroot00000000000000_Bool abs0(int a) { return (a < 0 ? -a : a) == (a >= 0 ? a : -a); } _Bool abs1(int a) { return (a < 0 ? -a : a) == (a > 0 ? a : -a); } _Bool abs2(int a) { return (a < 0 ? -a : a) == (a <= 0 ? -a : a); } /* * check-name: canonical-abs1 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/canonical-add.c000066400000000000000000000017001411531012200211050ustar00rootroot00000000000000int xpc_add_ypc(int x, int y) { return (x + 1) + (y + 1); } int xmc_add_ypc(int x, int y) { return (x - 1) + (y + 1); } int xpc_add_ymc(int x, int y) { return (x + 1) + (y - 1); } int xmc_add_ymc(int x, int y) { return (x - 1) + (y - 1); } int xpc_sub_ypc(int x, int y) { return (x + 1) - (y + 1); } int xmc_sub_ypc(int x, int y) { return (x - 1) - (y + 1); } int xpc_sub_ymc(int x, int y) { return (x + 1) - (y - 1); } int xmc_sub_ymc(int x, int y) { return (x - 1) - (y - 1); } /* * check-name: canonical-add * check-description: * 1) verify that constants in add/sub chains are * pushed at the right of the whole chain. * For example '(a + 1) + b' must be canonicalized into '(a + b) + 1' * This is needed for '(a + 1) + b - 1' to be simplified into '(a + b)' * * check-command: test-linearize -Wno-decl $file * check-known-to-fail * check-output-ignore * check-output-excludes: \\$1 * check-output-excludes: \\$-1 */ sparse-0.6.4/validation/optim/canonical-arg.c000066400000000000000000000004451411531012200211330ustar00rootroot00000000000000int def(void); int canon_arg_arg(int a, int b) { return (a + b) == (b + a); } int canon_arg_reg(int a) { int b = def(); return (a + b) == (b + a); } /* * check-name: canonical-arg * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/canonical-cmp-zero.c000066400000000000000000000020171411531012200221130ustar00rootroot00000000000000int f00(int x) { return x >= 0; } int f01(int x) { return x > -1; } int f02(int x) { return x < 1; } int f03(int x) { return x <= 0; } int f10(int x) { return x < 16; } int f11(int x) { return x <= 15; } int f20(int x) { return x > -9; } int f21(int x) { return x >= -8; } /* * check-name: canonical-cmp-zero * check-command: test-linearize -Wno-decl $file * * check-output-start f00: .L0: setge.32 %r2 <- %arg1, $0 ret.32 %r2 f01: .L2: setge.32 %r5 <- %arg1, $0 ret.32 %r5 f02: .L4: setle.32 %r8 <- %arg1, $0 ret.32 %r8 f03: .L6: setle.32 %r11 <- %arg1, $0 ret.32 %r11 f10: .L8: setle.32 %r14 <- %arg1, $15 ret.32 %r14 f11: .L10: setle.32 %r17 <- %arg1, $15 ret.32 %r17 f20: .L12: setge.32 %r20 <- %arg1, $0xfffffff8 ret.32 %r20 f21: .L14: setge.32 %r23 <- %arg1, $0xfffffff8 ret.32 %r23 * check-output-end */ sparse-0.6.4/validation/optim/canonical-cmp.c000066400000000000000000000017561411531012200211470ustar00rootroot00000000000000typedef signed int sint; typedef unsigned int uint; sint seq(sint p, sint a) { return (123 == p) == (p == 123); } sint sne(sint p, sint a) { return (123 != p) == (p != 123); } sint slt(sint p, sint a) { return (123 > p) == (p < 123); } sint sle(sint p, sint a) { return (123 >= p) == (p <= 123); } sint sge(sint p, sint a) { return (123 <= p) == (p >= 123); } sint sgt(sint p, sint a) { return (123 < p) == (p > 123); } uint ueq(uint p, uint a) { return (123 == p) == (p == 123); } uint une(uint p, uint a) { return (123 != p) == (p != 123); } uint ubt(uint p, uint a) { return (123 > p) == (p < 123); } uint ube(uint p, uint a) { return (123 >= p) == (p <= 123); } uint uae(uint p, uint a) { return (123 <= p) == (p >= 123); } uint uat(uint p, uint a) { return (123 < p) == (p > 123); } /* * check-name: canonical-cmp * check-description: check that constants move rightside * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-excludes: \\$123, */ sparse-0.6.4/validation/optim/canonical-cmpe-minmax.c000066400000000000000000000007141411531012200225740ustar00rootroot00000000000000#define SMAX __INT_MAX__ #define SMIN (-__INT_MAX__-1) int le_smax(int a) { return (a <= (SMAX - 1)) == (a != SMAX); } int gt_smax(int a) { return (a > (SMAX - 1)) == (a == SMAX); } int lt_smin(int a) { return (a < (SMIN + 1)) == (a == SMIN); } int ge_smin(int a) { return (a >= (SMIN + 1)) == (a != SMIN); } /* * check-name: canonical-cmpe-minmax * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/canonical-cmps-minmax.c000066400000000000000000000006641411531012200226160ustar00rootroot00000000000000#define SMAX __INT_MAX__ #define SMIN (-__INT_MAX__-1) int lt_smax(int a) { return (a < SMAX) == (a != SMAX); } int ge_smax(int a) { return (a >= SMAX) == (a == SMAX); } int le_smin(int a) { return (a <= SMIN) == (a == SMIN); } int gt_smin(int a) { return (a > SMIN) == (a != SMIN); } /* * check-name: canonical-cmps-minmax * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/canonical-cmps-sel.c000066400000000000000000000010611411531012200221000ustar00rootroot00000000000000_Bool sel_lts(int a, int b, int x, int y) { return ((a < b) ? x : y) == ((a >= b) ? y : x); } _Bool sel_les(int a, int b, int x, int y) { return ((a <= b) ? x : y) == ((a > b) ? y : x); } _Bool sel_ltu(unsigned int a, unsigned int b, int x, int y) { return ((a < b) ? x : y) == ((a >= b) ? y : x); } _Bool sel_leu(unsigned int a, unsigned int b, int x, int y) { return ((a <= b) ? x : y) == ((a > b) ? y : x); } /* * check-name: canonical-cmps-sel * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/canonical-cmps.c000066400000000000000000000007161411531012200213250ustar00rootroot00000000000000_Bool lt_p(int a) { return (a > 0) == (a >= 1); } _Bool ge_p(int a) { return (a <= 0) == (a < 1); } _Bool lt_m(int a) { return (a < 0) == (a <= -1); } _Bool ge_m(int a) { return (a >= 0) == (a > -1); } _Bool lt_x(int a) { return (a <= 1234) == (a < 1235); } _Bool ge_x(int a) { return (a >= 1234) == (a > 1233); } /* * check-name: canonical-cmps * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/canonical-cmpu.c000066400000000000000000000007221411531012200213240ustar00rootroot00000000000000// canonicalize to == or != int cmp_ltu_eq0(unsigned int x) { return (x < 1) == (x == 0); } int cmp_geu_ne0(unsigned int x) { return (x >= 1) == (x != 0); } // canonicalize to the smaller value int cmp_ltu(unsigned int x) { return (x < 256) == (x <= 255); } int cmp_geu(unsigned int x) { return (x >= 256) == (x > 255); } /* * check-name: canonical-cmpu * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/canonical-fcmp.c000066400000000000000000000037541411531012200213150ustar00rootroot00000000000000extern double g; int fcmp_eq(double a) { return (g == a); } int fcmp_ne(double a) { return (g != a); } int fcmp_gt(double a) { return (g > a); } int fcmp_ge(double a) { return (g >= a); } int fcmp_le(double a) { return (g <= a); } int fcmp_lt(double a) { return (g < a); } int nfcmp_ne(double a) { return !(g == a); } int nfcmp_eq(double a) { return !(g != a); } int nfcmp_le(double a) { return !(g > a); } int nfcmp_lt(double a) { return !(g >= a); } int nfcmp_gt(double a) { return !(g <= a); } int nfcmp_ge(double a) { return !(g < a); } /* * check-name: canonical-cmp * check-command: test-linearize -Wno-decl $file * * check-output-excludes: \\$123, * * check-output-start fcmp_eq: .L0: load.64 %r1 <- 0[g] fcmpoeq.32 %r3 <- %r1, %arg1 ret.32 %r3 fcmp_ne: .L2: load.64 %r5 <- 0[g] fcmpune.32 %r7 <- %r5, %arg1 ret.32 %r7 fcmp_gt: .L4: load.64 %r9 <- 0[g] fcmpogt.32 %r11 <- %r9, %arg1 ret.32 %r11 fcmp_ge: .L6: load.64 %r13 <- 0[g] fcmpoge.32 %r15 <- %r13, %arg1 ret.32 %r15 fcmp_le: .L8: load.64 %r17 <- 0[g] fcmpole.32 %r19 <- %r17, %arg1 ret.32 %r19 fcmp_lt: .L10: load.64 %r21 <- 0[g] fcmpolt.32 %r23 <- %r21, %arg1 ret.32 %r23 nfcmp_ne: .L12: load.64 %r25 <- 0[g] fcmpune.32 %r28 <- %r25, %arg1 ret.32 %r28 nfcmp_eq: .L14: load.64 %r30 <- 0[g] fcmpoeq.32 %r33 <- %r30, %arg1 ret.32 %r33 nfcmp_le: .L16: load.64 %r35 <- 0[g] fcmpule.32 %r38 <- %r35, %arg1 ret.32 %r38 nfcmp_lt: .L18: load.64 %r40 <- 0[g] fcmpult.32 %r43 <- %r40, %arg1 ret.32 %r43 nfcmp_gt: .L20: load.64 %r45 <- 0[g] fcmpugt.32 %r48 <- %r45, %arg1 ret.32 %r48 nfcmp_ge: .L22: load.64 %r50 <- 0[g] fcmpuge.32 %r53 <- %r50, %arg1 ret.32 %r53 * check-output-end */ sparse-0.6.4/validation/optim/canonical-mul.c000066400000000000000000000012341411531012200211540ustar00rootroot00000000000000#define uint unsigned int uint xtc_umul_ytc(uint x, uint y) { return (x * 3) * (y * 2); } /* * check-name: canonical-muldiv * check-description: * 1) verify that constants in mul chains are * pushed at the right of the whole chain. * For example '(a * 3) * b' must be canonicalized into '(a * b) * 3' * This is needed in general for constant simplification; * for example, for: * '(a * 3) * (b * 2)' * to be simplified into: * '(a * b) * 6' * * check-command: test-linearize -Wno-decl $file * check-known-to-fail * check-output-ignore * * check-output-excludes: \\$3 * check-output-excludes: \\$2 * check-output-contains: \\$6 */ sparse-0.6.4/validation/optim/canonical-not.c000066400000000000000000000003101411531012200211510ustar00rootroot00000000000000int canon_not(int a, int b) { return (a & ~b) == (~b & a); } /* * check-name: canonical-not * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/canonical-sub-cte.c000066400000000000000000000003161411531012200217210ustar00rootroot00000000000000int sub_cte(int x) { return (x - 1) != (x + -1); } /* * check-name: canonical-sub-cte * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-contains: ret\\..*\\$0 */ sparse-0.6.4/validation/optim/cast-kinds.c000066400000000000000000000153721411531012200205020ustar00rootroot00000000000000typedef 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; } static float float_2_float(float a) { return a; } static double double_2_double(double a) { return a; } /* * check-name: cast-kinds * check-command: test-linearize -Wno-int-to-pointer-cast -Wno-pointer-to-int-cast -m64 $file * check-assert: sizeof(void *) == 8 && sizeof(long) == 8 && sizeof(double) == 8 * * check-output-start uint_2_int: .L0: ret.32 %arg1 long_2_int: .L2: trunc.32 %r4 <- (64) %arg1 ret.32 %r4 ulong_2_int: .L4: trunc.32 %r7 <- (64) %arg1 ret.32 %r7 vptr_2_int: .L6: trunc.32 %r10 <- (64) %arg1 ret.32 %r10 iptr_2_int: .L8: trunc.32 %r14 <- (64) %arg1 ret.32 %r14 float_2_int: .L10: fcvts.32 %r17 <- (32) %arg1 ret.32 %r17 double_2_int: .L12: fcvts.32 %r20 <- (64) %arg1 ret.32 %r20 int_2_uint: .L14: ret.32 %arg1 long_2_uint: .L16: trunc.32 %r25 <- (64) %arg1 ret.32 %r25 ulong_2_uint: .L18: trunc.32 %r28 <- (64) %arg1 ret.32 %r28 vptr_2_uint: .L20: trunc.32 %r31 <- (64) %arg1 ret.32 %r31 iptr_2_uint: .L22: trunc.32 %r35 <- (64) %arg1 ret.32 %r35 float_2_uint: .L24: fcvtu.32 %r38 <- (32) %arg1 ret.32 %r38 double_2_uint: .L26: fcvtu.32 %r41 <- (64) %arg1 ret.32 %r41 int_2_long: .L28: sext.64 %r44 <- (32) %arg1 ret.64 %r44 uint_2_long: .L30: zext.64 %r47 <- (32) %arg1 ret.64 %r47 ulong_2_long: .L32: ret.64 %arg1 vptr_2_long: .L34: ret.64 %arg1 iptr_2_long: .L36: ret.64 %arg1 float_2_long: .L38: fcvts.64 %r57 <- (32) %arg1 ret.64 %r57 double_2_long: .L40: fcvts.64 %r60 <- (64) %arg1 ret.64 %r60 int_2_ulong: .L42: sext.64 %r63 <- (32) %arg1 ret.64 %r63 uint_2_ulong: .L44: zext.64 %r66 <- (32) %arg1 ret.64 %r66 long_2_ulong: .L46: ret.64 %arg1 vptr_2_ulong: .L48: ret.64 %arg1 iptr_2_ulong: .L50: ret.64 %arg1 float_2_ulong: .L52: fcvtu.64 %r76 <- (32) %arg1 ret.64 %r76 double_2_ulong: .L54: fcvtu.64 %r79 <- (64) %arg1 ret.64 %r79 int_2_vptr: .L56: sext.64 %r82 <- (32) %arg1 ret.64 %r82 uint_2_vptr: .L58: zext.64 %r85 <- (32) %arg1 ret.64 %r85 long_2_vptr: .L60: ret.64 %arg1 ulong_2_vptr: .L62: ret.64 %arg1 iptr_2_vptr: .L64: ret.64 %arg1 int_2_iptr: .L66: sext.64 %r94 <- (32) %arg1 ret.64 %r94 uint_2_iptr: .L68: zext.64 %r98 <- (32) %arg1 ret.64 %r98 long_2_iptr: .L70: ret.64 %arg1 ulong_2_iptr: .L72: ret.64 %arg1 vptr_2_iptr: .L74: ptrcast.64 %r108 <- (64) %arg1 ret.64 %r108 int_2_float: .L76: scvtf.32 %r111 <- (32) %arg1 ret.32 %r111 uint_2_float: .L78: ucvtf.32 %r114 <- (32) %arg1 ret.32 %r114 long_2_float: .L80: scvtf.32 %r117 <- (64) %arg1 ret.32 %r117 ulong_2_float: .L82: ucvtf.32 %r120 <- (64) %arg1 ret.32 %r120 double_2_float: .L84: fcvtf.32 %r123 <- (64) %arg1 ret.32 %r123 int_2_double: .L86: scvtf.64 %r126 <- (32) %arg1 ret.64 %r126 uint_2_double: .L88: ucvtf.64 %r129 <- (32) %arg1 ret.64 %r129 long_2_double: .L90: scvtf.64 %r132 <- (64) %arg1 ret.64 %r132 ulong_2_double: .L92: ucvtf.64 %r135 <- (64) %arg1 ret.64 %r135 float_2_double: .L94: fcvtf.64 %r138 <- (32) %arg1 ret.64 %r138 float_2_float: .L96: ret.32 %arg1 double_2_double: .L98: ret.64 %arg1 * check-output-end */ sparse-0.6.4/validation/optim/cast-nop.c000066400000000000000000000004201411531012200201520ustar00rootroot00000000000000static long p2l(long *p) { return (long) p; } static long *l2p(long l) { return (long*)l; } /* * check-name: cast-nop * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-excludes: utptr\\. * check-output-excludes: ptrtu\\. */ sparse-0.6.4/validation/optim/cgoto01.c000066400000000000000000000005671411531012200177160ustar00rootroot00000000000000void abort(void) __attribute__((__noreturn__)); int foo(int a) { void *label; if (a == a) label = &&L1; else label = &&L2; goto *label; L1: return 0; L2: abort(); } /* * check-name: cgoto01 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-excludes: set\\. * check-output-excludes: jmp * check-output-excludes: call */ sparse-0.6.4/validation/optim/cgoto02.c000066400000000000000000000003501411531012200177050ustar00rootroot00000000000000int foo(int a) { void *label = a ? &&l1 : &&l2; goto *label; l1: return a; l2: return 0; } /* * check-name: cgoto02 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: %arg1 */ sparse-0.6.4/validation/optim/cmp-and-pow2.c000066400000000000000000000004221411531012200206340ustar00rootroot00000000000000#define M 32 _Bool eq(int a) { return ((a & M) != M) == ((a & M) == 0); } _Bool ne(int a) { return ((a & M) == M) == ((a & M) != 0); } /* * check-name: cmp-and-pow2 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/cmp-op-type.c000066400000000000000000000003571411531012200206110ustar00rootroot00000000000000extern int get(void); static int array[8192]; static int foo(void) { int n = -1; if (n < 0) n = get(); return array[n]; } /* * check-name: cmp-op-type * check-command: test-linearize -Wno-decl $file * * check-output-ignore */ sparse-0.6.4/validation/optim/cmp-sext-sext.c000066400000000000000000000010761411531012200211570ustar00rootroot00000000000000#define T(TYPE) __##TYPE##_TYPE__ #define cmp(TYPE, X, OP, Y) ((T(TYPE)) X OP (T(TYPE)) Y) #define TEST(T1, T2, X, OP, Y) cmp(T1, X, OP, Y) == cmp(T2, X, OP, Y) #define ARGS(TYPE) T(TYPE) a, T(TYPE)b _Bool cmpe_sext_sext(ARGS(INT32)) { return TEST(UINT64, UINT32, a, ==, b); } _Bool cmps_sext_sext(ARGS(INT32)) { return TEST( INT64, INT32, a, < , b); } _Bool cmpu_sext_sext(ARGS(INT32)) { return TEST(UINT64, UINT32, a, < , b); } /* * check-name: cmp-sext-sext * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/cmp-sext-simm.c000066400000000000000000000054061411531012200211420ustar00rootroot00000000000000#define sext(X) ((long long) (X)) #define POS (1LL << 31) #define NEG (-POS - 1) static int lt_ge0(int x) { return (sext(x) < (POS + 0)) == 1; } static int lt_ge1(int x) { return (sext(x) < (POS + 1)) == 1; } static int lt_ge2(int x) { return (sext(x) < (POS + 2)) == 1; } static int lt_gex(int x) { return (sext(x) < (POS<< 1)) == 1; } static int lt_gey(int x) { return (sext(x) < (POS<< 3)) == 1; } static int le_ge0(int x) { return (sext(x) <= (POS + 0)) == 1; } static int le_ge1(int x) { return (sext(x) <= (POS + 1)) == 1; } static int le_ge2(int x) { return (sext(x) <= (POS + 2)) == 1; } static int le_gex(int x) { return (sext(x) <= (POS<< 1)) == 1; } static int le_gey(int x) { return (sext(x) <= (POS<< 3)) == 1; } static int ge_ge0(int x) { return (sext(x) >= (POS + 0)) == 0; } static int ge_ge1(int x) { return (sext(x) >= (POS + 1)) == 0; } static int ge_ge2(int x) { return (sext(x) >= (POS + 2)) == 0; } static int ge_gex(int x) { return (sext(x) >= (POS<< 1)) == 0; } static int ge_gey(int x) { return (sext(x) >= (POS<< 3)) == 0; } static int gt_ge0(int x) { return (sext(x) > (POS + 0)) == 0; } static int gt_ge1(int x) { return (sext(x) > (POS + 1)) == 0; } static int gt_ge2(int x) { return (sext(x) > (POS + 2)) == 0; } static int gt_gex(int x) { return (sext(x) > (POS<< 1)) == 0; } static int gt_gey(int x) { return (sext(x) > (POS<< 3)) == 0; } static int lt_lt0(int x) { return (sext(x) < (NEG - 0)) == 0; } static int lt_lt1(int x) { return (sext(x) < (NEG - 1)) == 0; } static int lt_lt2(int x) { return (sext(x) < (NEG - 2)) == 0; } static int lt_ltx(int x) { return (sext(x) < (NEG<< 1)) == 0; } static int lt_lty(int x) { return (sext(x) < (NEG<< 3)) == 0; } static int le_lt0(int x) { return (sext(x) <= (NEG - 0)) == 0; } static int le_lt1(int x) { return (sext(x) <= (NEG - 1)) == 0; } static int le_lt2(int x) { return (sext(x) <= (NEG - 2)) == 0; } static int le_ltx(int x) { return (sext(x) <= (NEG<< 1)) == 0; } static int le_lty(int x) { return (sext(x) <= (NEG<< 3)) == 0; } static int ge_lt0(int x) { return (sext(x) >= (NEG - 0)) == 1; } static int ge_lt1(int x) { return (sext(x) >= (NEG - 1)) == 1; } static int ge_lt2(int x) { return (sext(x) >= (NEG - 2)) == 1; } static int ge_ltx(int x) { return (sext(x) >= (NEG<< 1)) == 1; } static int ge_lty(int x) { return (sext(x) >= (NEG<< 3)) == 1; } static int gt_lt0(int x) { return (sext(x) > (NEG - 0)) == 1; } static int gt_lt1(int x) { return (sext(x) > (NEG - 1)) == 1; } static int gt_lt2(int x) { return (sext(x) > (NEG - 2)) == 1; } static int gt_ltx(int x) { return (sext(x) > (NEG<< 1)) == 1; } static int gt_lty(int x) { return (sext(x) > (NEG<< 3)) == 1; } /* * check-name: cmp-sext-simm * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/cmp-sext-uimm.c000066400000000000000000000016571411531012200211500ustar00rootroot00000000000000#define sext(X) ((unsigned long long) (X)) #define POS (1ULL << 31) #define NEG ((unsigned long long) -POS) int sext_ltu_p2(int x) { return (sext(x) < (POS + 2)) == (x >= 0); } int sext_ltu_p1(int x) { return (sext(x) < (POS + 1)) == (x >= 0); } int sext_ltu_p0(int x) { return (sext(x) < (POS + 0)) == (x >= 0); } int sext_leu_p1(int x) { return (sext(x) <= (POS + 1)) == (x >= 0); } int sext_leu_p0(int x) { return (sext(x) <= (POS + 0)) == (x >= 0); } int sext_geu_m1(int x) { return (sext(x) >= (NEG - 1)) == (x < 0); } int sext_geu_m2(int x) { return (sext(x) >= (NEG - 2)) == (x < 0); } int sext_gtu_m1(int x) { return (sext(x) > (NEG - 1)) == (x < 0); } int sext_gtu_m2(int x) { return (sext(x) > (NEG - 2)) == (x < 0); } int sext_gtu_m3(int x) { return (sext(x) > (NEG - 3)) == (x < 0); } /* * check-name: cmp-sext-uimm * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/cmp-sext.c000066400000000000000000000016631411531012200202000ustar00rootroot00000000000000#define T(TYPE) __##TYPE##_TYPE__ #define cmp(TYPE, X, OP, Y) ((T(TYPE)) X OP (T(TYPE)) Y) #define TEST(T1, T2, X, OP, Y) cmp(T1, X, OP, Y) == cmp(T2, X, OP, Y) #define ARGS(TYPE) T(TYPE) a, T(TYPE)b _Bool cmpe_sextp(ARGS(INT32)) { return TEST(UINT64, UINT32, a, ==, 0x7fffffff); } _Bool cmps_sextp(ARGS(INT32)) { return TEST( INT64, INT32, a, < , 0x7fffffff); } _Bool cmpu_sextp(ARGS(INT32)) { return TEST(UINT64, UINT32, a, < , 0x7fffffff); } _Bool cmpe_sextn(ARGS(INT32)) { return TEST(UINT64, UINT32, a, ==, -1); } _Bool cmps_sextn(ARGS(INT32)) { return TEST( INT64, INT32, a, < , -1); } _Bool cmpu_sextn(ARGS(INT32)) { return TEST(UINT64, UINT32, a, < , -1); } _Bool cmpltu_sext(int a) { return (a < 0x80000000ULL) == (a >= 0); } _Bool cmpgtu_sext(int a) { return (a >= 0x80000000ULL) == (a < 0); } /* * check-name: cmp-sext * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/cmp-type0.c000066400000000000000000000003051411531012200202460ustar00rootroot00000000000000static int foo(long long a) { return 0LL < (0x80000000LL + (a - a)); } /* * check-name: cmp-type0 * check-command: test-linearize $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/cmp-type1.c000066400000000000000000000003131411531012200202460ustar00rootroot00000000000000int foo(void) { int r; long n; n = 0; return n < 2147483648U; } /* * check-name: cmp-type1 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/cmp-zext-simm.c000066400000000000000000000015511411531012200211460ustar00rootroot00000000000000#define ZEXT(X) ((long long)(X)) #define BITS ((long long)(~0U)) int zext_ult(unsigned int x) { return (ZEXT(x) < (BITS + 1)) == 1; } int zext_ule(unsigned int x) { return (ZEXT(x) <= (BITS + 0)) == 1; } int zext_uge(unsigned int x) { return (ZEXT(x) >= (BITS + 1)) == 0; } int zext_ugt(unsigned int x) { return (ZEXT(x) > (BITS + 0)) == 0; } int zext_0le(unsigned int x) { return (ZEXT(x) <= 0) == (x == 0); } int zext_0ge(unsigned int x) { return (ZEXT(x) > 0) == (x != 0); } int zext_llt(unsigned int x) { return (ZEXT(x) < -1) == 0; } int zext_lle(unsigned int x) { return (ZEXT(x) <= -1) == 0; } int zext_lge(unsigned int x) { return (ZEXT(x) >= -1) == 1; } int zext_lgt(unsigned int x) { return (ZEXT(x) > -1) == 1; } /* * check-name: cmp-zext-simm * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/cmp-zext-uimm0.c000066400000000000000000000013741411531012200212330ustar00rootroot00000000000000#define zext(X) ((unsigned long long) (X)) #define MAX (1ULL << 32) #define TEST(X,OP,VAL) (zext(X) OP (VAL)) == (X OP (VAL)) int zext_ltu_0(unsigned int x) { return TEST(x, < , MAX); } int zext_ltu_m(unsigned int x) { return TEST(x, < , MAX - 1); } int zext_lte_0(unsigned int x) { return TEST(x, <=, MAX); } int zext_lte_m(unsigned int x) { return TEST(x, <=, MAX - 1); } int zext_gte_0(unsigned int x) { return TEST(x, >=, MAX); } int zext_gte_m(unsigned int x) { return TEST(x, >=, MAX - 1); } int zext_gtu_0(unsigned int x) { return TEST(x, > , MAX); } int zext_gtu_m(unsigned int x) { return TEST(x, > , MAX - 1); } /* * check-name: cmp-zext-uimm0 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/cmp-zext-uimm1.c000066400000000000000000000007641411531012200212360ustar00rootroot00000000000000#define zext(X) ((unsigned long long) (X)) #define BITS ((1ULL << 32) - 1) int zext_lt_p(unsigned int x) { return (zext(x) < (BITS + 1)) == 1; } int zext_le_p(unsigned int x) { return (zext(x) <= (BITS )) == 1; } int zext_ge_p(unsigned int x) { return (zext(x) >= (BITS + 1)) == 0; } int zext_gt_p(unsigned int x) { return (zext(x) > (BITS )) == 0; } /* * check-name: cmp-zext-uimm1 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/cmp-zext-uimm2.c000066400000000000000000000026131411531012200212320ustar00rootroot00000000000000#define zext(X) ((unsigned long long) (X)) int zext_ltu_q(unsigned x) { return (zext(x) < 0x100000001UL) == 1; } int zext_ltu_p(unsigned x) { return (zext(x) < 0x100000000UL) == 1; } int zext_ltu_0(unsigned x) { return (zext(x) < 0x0ffffffffUL) == (x < 0xffffffff); } int zext_ltu_m(unsigned x) { return (zext(x) < 0x0fffffffeUL) == (x < 0xfffffffe); } int zext_leu_q(unsigned x) { return (zext(x) <= 0x100000001UL) == 1; } int zext_leu_p(unsigned x) { return (zext(x) <= 0x100000000UL) == 1; } int zext_leu_0(unsigned x) { return (zext(x) <= 0x0ffffffffUL) == 1; } int zext_leu_m(unsigned x) { return (zext(x) <= 0x0fffffffeUL) == (x <= 0xfffffffe); } int zext_geu_q(unsigned x) { return (zext(x) >= 0x100000001UL) == 0; } int zext_geu_p(unsigned x) { return (zext(x) >= 0x100000000UL) == 0; } int zext_geu_0(unsigned x) { return (zext(x) >= 0x0ffffffffUL) == (x >= 0xffffffff); } int zext_geu_m(unsigned x) { return (zext(x) >= 0x0fffffffeUL) == (x >= 0xfffffffe); } int zext_gtu_q(unsigned x) { return (zext(x) > 0x100000001UL) == 0; } int zext_gtu_p(unsigned x) { return (zext(x) > 0x100000000UL) == 0; } int zext_gtu_0(unsigned x) { return (zext(x) > 0x0ffffffffUL) == 0; } int zext_gtu_m(unsigned x) { return (zext(x) > 0x0fffffffeUL) == (x > 0xfffffffe); } /* * check-name: cmp-zext-uimm2 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/cmp-zext-zext.c000066400000000000000000000011011411531012200211620ustar00rootroot00000000000000#define T(TYPE) __##TYPE##_TYPE__ #define cmp(TYPE, X, OP, Y) ((T(TYPE)) X OP (T(TYPE)) Y) #define TEST(T1, T2, X, OP, Y) cmp(T1, X, OP, Y) == cmp(T2, X, OP, Y) #define ARGS(TYPE) T(TYPE) a, T(TYPE)b _Bool cmpe_zext_zext(ARGS(UINT32)) { return TEST(UINT64, UINT32, a, ==, b); } _Bool cmps_zext_zext(ARGS(UINT32)) { return TEST( INT64, UINT32, a, < , b); } _Bool cmpu_zext_zext(ARGS(UINT32)) { return TEST(UINT64, UINT32, a, < , b); } /* * check-name: cmp-zext-zext * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/cmp-zext.c000066400000000000000000000011101411531012200201720ustar00rootroot00000000000000#define T(TYPE) __##TYPE##_TYPE__ #define cmp(TYPE, X, OP, Y) ((T(TYPE)) X OP (T(TYPE)) Y) #define TEST(T1, T2, X, OP, Y) cmp(T1, X, OP, Y) == cmp(T2, X, OP, Y) #define ARGS(TYPE) T(TYPE) a, T(TYPE)b _Bool cmpe_zext(ARGS(UINT32)) { return TEST(UINT64, UINT32, a, ==, 0xffffffff); } _Bool cmps_zext(ARGS(UINT32)) { return TEST( INT64, UINT32, a, < , 0xffffffff); } _Bool cmpu_zext(ARGS(UINT32)) { return TEST(UINT64, UINT32, a, < , 0xffffffff); } /* * check-name: cmp-zext * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/cmpe-and0.c000066400000000000000000000004071411531012200201770ustar00rootroot00000000000000int cmpe_and_eq(int a) { return ((a & 0xff00) == 0xff01) + 1; } int cmpe_and_ne(int a) { return ((a & 0xff00) != 0xff01) + 0; } /* * check-name: cmpe-and0 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/cmpe-or0.c000066400000000000000000000003501411531012200200520ustar00rootroot00000000000000int cmp_eq(int a) { return ((a | 1) != 0) + 0; } int cmp_ne(int a) { return ((a | 1) == 0) + 1; } /* * check-name: cmpe-or0 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/cmps-and0.c000066400000000000000000000013401411531012200202120ustar00rootroot00000000000000#define MINUS_ONE -1 #define MASK 32 int cmps_and_lt_lt0(int a) { return ((a & MASK) < MINUS_ONE) + 1; } int cmps_and_lt_gtm(int a) { return ((a & MASK) < (MASK + 1)) + 0; } int cmps_and_le_lt0(int a) { return ((a & MASK) <= MINUS_ONE) + 1; } int cmps_and_le_gtm(int a) { return ((a & MASK) <= (MASK + 1)) + 0; } int cmps_and_gt_lt0(int a) { return ((a & MASK) > MINUS_ONE) + 0; } int cmps_and_gt_gtm(int a) { return ((a & MASK) > (MASK + 1)) + 1; } int cmps_and_ge_lt0(int a) { return ((a & MASK) >= MINUS_ONE) + 0; } int cmps_and_ge_gtm(int a) { return ((a & MASK) >= (MASK + 1)) + 1; } /* * check-name: cmps-and0 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/cmps-minmax.c000066400000000000000000000005761411531012200206730ustar00rootroot00000000000000#define SMAX __INT_MAX__ #define SMIN (-__INT_MAX__-1) int lt_smin(int a) { return (a < SMIN) + 1; } int le_smax(int a) { return (a <= SMAX) + 0; } int ge_smin(int a) { return (a >= SMIN) + 0; } int gt_smax(int a) { return (a > SMAX) + 1; } /* * check-name: cmps-minmax * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/cmps-or0.c000066400000000000000000000012741411531012200200760ustar00rootroot00000000000000#define EQ(X) + (X == 0) #define SIGN (1 << 31) #define MASK (SIGN | 32) int cmps_ior_lt_x(int a) { return ((a | MASK) < 4) EQ(1); } int cmps_ior_lt_0(int a) { return ((a | MASK) < 0) EQ(1); } int cmps_ior_le_x(int a) { return ((a | MASK) <= 4) EQ(1); } int cmps_ior_le_0(int a) { return ((a | MASK) <= 0) EQ(1); } int cmps_ior_ge_x(int a) { return ((a | MASK) >= 4) EQ(0); } int cmps_ior_ge_0(int a) { return ((a | MASK) >= 0) EQ(0); } int cmps_ior_gt_x(int a) { return ((a | MASK) > 4) EQ(0); } int cmps_ior_gt_0(int a) { return ((a | MASK) > 0) EQ(0); } /* * check-name: cmps-or0 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/cmps0-and0.c000066400000000000000000000004421411531012200202740ustar00rootroot00000000000000#define M 32 int cmps_and_sle0(int a) { return ((a & M) <= 0) == ((a & M) == 0); } int cmps_and_sgt0(int a) { return ((a & M) > 0) == ((a & M) != 0); } /* * check-name: cmps0-and * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/cmpu-and0.c000066400000000000000000000010771411531012200202230ustar00rootroot00000000000000#define MASK 32U int cmps_and_ltu_gt(int a) { return ((a & MASK) < (MASK + 1)) + 0; } int cmps_and_leu_gt(int a) { return ((a & MASK) <= (MASK + 1)) + 0; } int cmps_and_leu_eq(int a) { return ((a & MASK) <= (MASK + 0)) + 0; } int cmps_and_geu_gt(int a) { return ((a & MASK) >= (MASK + 1)) + 1; } int cmps_and_gtu_gt(int a) { return ((a & MASK) > (MASK + 1)) + 1; } int cmps_and_gtu_eq(int a) { return ((a & MASK) > (MASK + 0)) + 1; } /* * check-name: cmpu-and0 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/cmpu-or0.c000066400000000000000000000011361411531012200200750ustar00rootroot00000000000000#define EQ(X) + (X == 0) #define MASK 32U int cmpu_ior_lt_lt(int a) { return ((a | MASK) < (MASK - 1)) EQ(0); } int cmpu_ior_lt_eq(int a) { return ((a | MASK) < (MASK )) EQ(0); } int cmpu_ior_le_lt(int a) { return ((a | MASK) <= (MASK - 1)) EQ(0); } int cmpu_ior_ge_lt(int a) { return ((a | MASK) >= (MASK - 1)) EQ(1); } int cmpu_ior_ge_eq(int a) { return ((a | MASK) >= (MASK )) EQ(1); } int cmpu_ior_gt_lt(int a) { return ((a | MASK) > (MASK - 1)) EQ(1); } /* * check-name: cmpu-or0 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/cse-arg01.c000066400000000000000000000002741411531012200201170ustar00rootroot00000000000000int foo(int a, int b) { return (a < b) == (b > a); } /* * check-name: cse-arg01 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/cse-cmp-next.c000066400000000000000000000004051411531012200207340ustar00rootroot00000000000000void foo(int p, int i, int f, int *ref, int *dst, int *src) { if (p) f = ref[i]; if (f) dst[i] = src[i]; } /* * check-name: cse-cmp-next * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-pattern(1,2): mul\\. */ sparse-0.6.4/validation/optim/cse-commutativity.c000066400000000000000000000013761411531012200221270ustar00rootroot00000000000000static 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.6.4/validation/optim/cse-dual-compare.c000066400000000000000000000026001411531012200215510ustar00rootroot00000000000000static 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.6.4/validation/optim/cse-fcmp.c000066400000000000000000000003751411531012200201340ustar00rootroot00000000000000extern void fun(void); int foo(double a, double b) { if (a < b) fun(); if (a < b) return 1; return 0; } /* * check-name: cse-fcmp * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-pattern(1): fcmp */ sparse-0.6.4/validation/optim/cse-label.c000066400000000000000000000002741411531012200202640ustar00rootroot00000000000000int foo(void) { label: return &&label == &&label; } /* * check-name: cse-label * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/cse-not01.c000066400000000000000000000004051411531012200201420ustar00rootroot00000000000000int and(int a) { return (~a & a) == 0; } int ior(int a) { return (~a | a) == ~0; } int xor(int a) { return (~a ^ a) == ~0; } /* * check-name: cse-not01 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/cse-not02.c000066400000000000000000000004761411531012200201530ustar00rootroot00000000000000int and(int a, int b) { return ((a == b) & (a != b)) == 0; } int ior(int a, int b) { return ((a == b) | (a != b)) == 1; } int xor(int a, int b) { return ((a == b) ^ (a != b)) == 1; } /* * check-name: cse-not02 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/cse-reg01.c000066400000000000000000000003231411531012200201160ustar00rootroot00000000000000int foo(int a, int b) { int x = a + b, y = ~b; return (x < y) == (y > x); } /* * check-name: cse-reg01 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/cse-setfval.c000066400000000000000000000003341411531012200206460ustar00rootroot00000000000000int ftest(double a, double b) { return a == 0.125 || b == 0.125; } /* * check-name: CSE OP_SETFVAL * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-pattern(1): setfval\\. */ sparse-0.6.4/validation/optim/cse-size.c000066400000000000000000000004051411531012200201530ustar00rootroot00000000000000static void foo(void) { unsigned short p = 0; int x = 1; for (;;) if (p) p = x; } /* * check-name: cse-size * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-excludes: phi\\. * check-output-excludes: cbr */ sparse-0.6.4/validation/optim/double-unop.c000066400000000000000000000005371411531012200206700ustar00rootroot00000000000000typedef 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.6.4/validation/optim/dup-cond0.c000066400000000000000000000004041411531012200202210ustar00rootroot00000000000000struct s { int f; }; static int foo(struct s *s) { if (s->f) return 0; else if (!s->f) return 4; return -1; } /* * check-name: dup-cond0 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-contains: select */ sparse-0.6.4/validation/optim/eqne-select.c000066400000000000000000000006051411531012200206400ustar00rootroot00000000000000int sel_eq01(int a, int b) { return ((a == b) ? a : b) == b; } int sel_eq10(int a, int b) { return ((a == b) ? b : a) == a; } int sel_ne01(int a, int b) { return ((a != b) ? a : b) == a; } int sel_ne10(int a, int b) { return ((a != b) ? b : a) == b; } /* * check-name: eqne-select * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/ext-trunc-greater.c000066400000000000000000000003571411531012200220170ustar00rootroot00000000000000short sgt(char x) { return (int) x; } short ugt(unsigned char x) { return (int) x; } /* * check-name: ext-trunc-greater * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-excludes: trunc\\. */ sparse-0.6.4/validation/optim/ext-trunc-same.c000066400000000000000000000004621411531012200213100ustar00rootroot00000000000000short seq(short x) { return (int) x; } short ueq(unsigned short x) { return (int) x; } /* * check-name: ext-trunc-same * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-excludes: trunc\\. * check-output-excludes: sext\\. * check-output-excludes: zext\\. */ sparse-0.6.4/validation/optim/ext-trunc-smaller.c000066400000000000000000000004221411531012200220160ustar00rootroot00000000000000char slt(short x) { return (int) x; } char ult(unsigned short x) { return (int) x; } /* * check-name: ext-trunc-smaller * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-excludes: sext\\. * check-output-excludes: zext\\. */ sparse-0.6.4/validation/optim/fact-add-mul.c000066400000000000000000000026351411531012200206760ustar00rootroot00000000000000int fr_abx(int a, int b, int x) { return ((a * x) + (b * x)) == ((a + b) * x); } int fl_abx(int a, int b, int x) { return ((x * a) + (x * b)) == ((a + b) * x); } int fm_abx(int a, int b, int x) { return ((a * x) + (x * b)) == ((a + b) * x); } int fn_abx(int a, int b, int x) { return ((x * a) + (b * x)) == ((a + b) * x); } int fr_bax(int b, int a, int x) { return ((a * x) + (b * x)) == ((b + a) * x); } int fl_bax(int b, int a, int x) { return ((x * a) + (x * b)) == ((b + a) * x); } int fm_bax(int b, int a, int x) { return ((a * x) + (x * b)) == ((b + a) * x); } int fn_bax(int b, int a, int x) { return ((x * a) + (b * x)) == ((b + a) * x); } int fr_axb(int a, int x, int b) { return ((a * x) + (b * x)) == ((a + b) * x); } int fl_axb(int a, int x, int b) { return ((x * a) + (x * b)) == ((a + b) * x); } int fm_axb(int a, int x, int b) { return ((a * x) + (x * b)) == ((a + b) * x); } int fn_axb(int a, int x, int b) { return ((x * a) + (b * x)) == ((a + b) * x); } int fr_bxa(int b, int x, int a) { return ((b * x) + (a * x)) == ((b + a) * x); } int fl_bxa(int b, int x, int a) { return ((x * b) + (x * a)) == ((b + a) * x); } int fm_bxa(int b, int x, int a) { return ((b * x) + (x * a)) == ((b + a) * x); } int fn_bxa(int b, int x, int a) { return ((x * b) + (a * x)) == ((b + a) * x); } /* * check-name: fact-add-mul * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/fact-and-ior.c000066400000000000000000000026351411531012200207040ustar00rootroot00000000000000int fr_abx(int a, int b, int x) { return ((a | x) & (b | x)) == ((a & b) | x); } int fl_abx(int a, int b, int x) { return ((x | a) & (x | b)) == ((a & b) | x); } int fm_abx(int a, int b, int x) { return ((a | x) & (x | b)) == ((a & b) | x); } int fn_abx(int a, int b, int x) { return ((x | a) & (b | x)) == ((a & b) | x); } int fr_bax(int b, int a, int x) { return ((a | x) & (b | x)) == ((b & a) | x); } int fl_bax(int b, int a, int x) { return ((x | a) & (x | b)) == ((b & a) | x); } int fm_bax(int b, int a, int x) { return ((a | x) & (x | b)) == ((b & a) | x); } int fn_bax(int b, int a, int x) { return ((x | a) & (b | x)) == ((b & a) | x); } int fr_axb(int a, int x, int b) { return ((a | x) & (b | x)) == ((a & b) | x); } int fl_axb(int a, int x, int b) { return ((x | a) & (x | b)) == ((a & b) | x); } int fm_axb(int a, int x, int b) { return ((a | x) & (x | b)) == ((a & b) | x); } int fn_axb(int a, int x, int b) { return ((x | a) & (b | x)) == ((a & b) | x); } int fr_bxa(int b, int x, int a) { return ((b | x) & (a | x)) == ((b & a) | x); } int fl_bxa(int b, int x, int a) { return ((x | b) & (x | a)) == ((b & a) | x); } int fm_bxa(int b, int x, int a) { return ((b | x) & (x | a)) == ((b & a) | x); } int fn_bxa(int b, int x, int a) { return ((x | b) & (a | x)) == ((b & a) | x); } /* * check-name: fact-and-ior * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/fact-and-shift.c000066400000000000000000000007431411531012200212260ustar00rootroot00000000000000typedef unsigned int uint; typedef signed int sint; uint fact_and_shl(uint a, uint b, uint s) { return ((a << s) & (b << s)) == ((a & b) << s); } uint fact_and_lsr(uint a, uint b, uint s) { return ((a >> s) & (b >> s)) == ((a & b) >> s); } sint fact_and_asr(sint a, sint b, sint s) { return ((a >> s) & (b >> s)) == ((a & b) >> s); } /* * check-name: fact-and-shift * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/fact-ior-and.c000066400000000000000000000026351411531012200207040ustar00rootroot00000000000000int fr_abx(int a, int b, int x) { return ((a & x) | (b & x)) == ((a | b) & x); } int fl_abx(int a, int b, int x) { return ((x & a) | (x & b)) == ((a | b) & x); } int fm_abx(int a, int b, int x) { return ((a & x) | (x & b)) == ((a | b) & x); } int fn_abx(int a, int b, int x) { return ((x & a) | (b & x)) == ((a | b) & x); } int fr_bax(int b, int a, int x) { return ((a & x) | (b & x)) == ((b | a) & x); } int fl_bax(int b, int a, int x) { return ((x & a) | (x & b)) == ((b | a) & x); } int fm_bax(int b, int a, int x) { return ((a & x) | (x & b)) == ((b | a) & x); } int fn_bax(int b, int a, int x) { return ((x & a) | (b & x)) == ((b | a) & x); } int fr_axb(int a, int x, int b) { return ((a & x) | (b & x)) == ((a | b) & x); } int fl_axb(int a, int x, int b) { return ((x & a) | (x & b)) == ((a | b) & x); } int fm_axb(int a, int x, int b) { return ((a & x) | (x & b)) == ((a | b) & x); } int fn_axb(int a, int x, int b) { return ((x & a) | (b & x)) == ((a | b) & x); } int fr_bxa(int b, int x, int a) { return ((b & x) | (a & x)) == ((b | a) & x); } int fl_bxa(int b, int x, int a) { return ((x & b) | (x & a)) == ((b | a) & x); } int fm_bxa(int b, int x, int a) { return ((b & x) | (x & a)) == ((b | a) & x); } int fn_bxa(int b, int x, int a) { return ((x & b) | (a & x)) == ((b | a) & x); } /* * check-name: fact-ior-and * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/fact-ior-shift.c000066400000000000000000000007431411531012200212550ustar00rootroot00000000000000typedef unsigned int uint; typedef signed int sint; uint fact_ior_shl(uint a, uint b, uint s) { return ((a << s) | (b << s)) == ((a | b) << s); } uint fact_ior_lsr(uint a, uint b, uint s) { return ((a >> s) | (b >> s)) == ((a | b) >> s); } sint fact_ior_asr(sint a, sint b, sint s) { return ((a >> s) | (b >> s)) == ((a | b) >> s); } /* * check-name: fact-ior-shift * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/fact-select01.c000066400000000000000000000025521411531012200207710ustar00rootroot00000000000000int add_yx_y(int p, int x, int y) { return (p ? (y+x) : y) == ((p ? x : 0) + y); } int add_xy_y(int p, int y, int x) { return (p ? (x+y) : y) == ((p ? x : 0) + y); } int add_xy_x(int p, int x, int y) { return (p ? (x+y) : x) == ((p ? y : 0) + x); } int add_yx_x(int p, int y, int x) { return (p ? (y+x) : x) == ((p ? y : 0) + x); } int add_y_yx(int p, int x, int y) { return (p ? y : (y+x)) == ((p ? 0 : x) + y); } int ior_yx_y(int p, int x, int y) { return (p ? (y|x) : y) == ((p ? x : 0) | y); } int ior_xy_y(int p, int y, int x) { return (p ? (x|y) : y) == ((p ? x : 0) | y); } int ior_xy_x(int p, int x, int y) { return (p ? (x|y) : x) == ((p ? y : 0) | x); } int ior_yx_x(int p, int y, int x) { return (p ? (y|x) : x) == ((p ? y : 0) | x); } int ior_y_yx(int p, int x, int y) { return (p ? y : (y|x)) == ((p ? 0 : x) | y); } int xor_yx_y(int p, int x, int y) { return (p ? (y^x) : y) == ((p ? x : 0) ^ y); } int xor_xy_y(int p, int y, int x) { return (p ? (x^y) : y) == ((p ? x : 0) ^ y); } int xor_xy_x(int p, int x, int y) { return (p ? (x^y) : x) == ((p ? y : 0) ^ x); } int xor_yx_x(int p, int y, int x) { return (p ? (y^x) : x) == ((p ? y : 0) ^ x); } int xor_y_yx(int p, int x, int y) { return (p ? y : (y^x)) == ((p ? 0 : x) ^ y); } /* * check-name: fact-select01 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/fact-xor-and.c000066400000000000000000000026351411531012200207230ustar00rootroot00000000000000int fr_abx(int a, int b, int x) { return ((a & x) ^ (b & x)) == ((a ^ b) & x); } int fl_abx(int a, int b, int x) { return ((x & a) ^ (x & b)) == ((a ^ b) & x); } int fm_abx(int a, int b, int x) { return ((a & x) ^ (x & b)) == ((a ^ b) & x); } int fn_abx(int a, int b, int x) { return ((x & a) ^ (b & x)) == ((a ^ b) & x); } int fr_bax(int b, int a, int x) { return ((a & x) ^ (b & x)) == ((b ^ a) & x); } int fl_bax(int b, int a, int x) { return ((x & a) ^ (x & b)) == ((b ^ a) & x); } int fm_bax(int b, int a, int x) { return ((a & x) ^ (x & b)) == ((b ^ a) & x); } int fn_bax(int b, int a, int x) { return ((x & a) ^ (b & x)) == ((b ^ a) & x); } int fr_axb(int a, int x, int b) { return ((a & x) ^ (b & x)) == ((a ^ b) & x); } int fl_axb(int a, int x, int b) { return ((x & a) ^ (x & b)) == ((a ^ b) & x); } int fm_axb(int a, int x, int b) { return ((a & x) ^ (x & b)) == ((a ^ b) & x); } int fn_axb(int a, int x, int b) { return ((x & a) ^ (b & x)) == ((a ^ b) & x); } int fr_bxa(int b, int x, int a) { return ((b & x) ^ (a & x)) == ((b ^ a) & x); } int fl_bxa(int b, int x, int a) { return ((x & b) ^ (x & a)) == ((b ^ a) & x); } int fm_bxa(int b, int x, int a) { return ((b & x) ^ (x & a)) == ((b ^ a) & x); } int fn_bxa(int b, int x, int a) { return ((x & b) ^ (a & x)) == ((b ^ a) & x); } /* * check-name: fact-xor-and * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/fact-xor-shift.c000066400000000000000000000007431411531012200212740ustar00rootroot00000000000000typedef unsigned int uint; typedef signed int sint; uint fact_xor_shl(uint a, uint b, uint s) { return ((a << s) ^ (b << s)) == ((a ^ b) << s); } uint fact_xor_lsr(uint a, uint b, uint s) { return ((a >> s) ^ (b >> s)) == ((a ^ b) >> s); } sint fact_xor_asr(sint a, sint b, sint s) { return ((a >> s) ^ (b >> s)) == ((a ^ b) >> s); } /* * check-name: fact-xor-shift * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/fpcast-constant.c000066400000000000000000000003651411531012200215450ustar00rootroot00000000000000static double foo(double a, int p) { return a * ((p & 0) + 2); } /* * check-name: fpcast-constant * check-command: test-linearize $file * * check-output-ignore * check-output-contains: scvtf\\. * check-output-excludes: fmul\\..*\\$2 */ sparse-0.6.4/validation/optim/fpcast-nop.c000066400000000000000000000006361411531012200205110ustar00rootroot00000000000000static 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.6.4/validation/optim/inline-return.c000066400000000000000000000004741411531012200212320ustar00rootroot00000000000000static inline int def(void) { return 1; } int foo(void) { return def(); } int bar(void) { return def(); return 0; } /* * check-name: inline-return.c * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-pattern(2): ret\\..*\\$1 * check-output-excludes: ret\\..*\\$0 */ sparse-0.6.4/validation/optim/kill-casts.c000066400000000000000000000007541411531012200205060ustar00rootroot00000000000000extern 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\\. * check-output-excludes: fcvt[us]\\. * check-output-excludes: utptr\\. * check-output-excludes: ptrtu\\. * check-output-excludes: [sz]ext\\. * check-output-excludes: trunc\\. */ sparse-0.6.4/validation/optim/kill-stores0.c000066400000000000000000000006061411531012200207640ustar00rootroot00000000000000struct p { int x, y; }; struct q { int w; }; static int foo(void) { int x = 1; int y = x; return &x == &y; } static int bar(struct p p) { if (p.x != 0) ; } static int baz(struct p p, struct q q) { if (p.x != 0 || p.y != 1 || q.w == 0) ; } /* * check-name: kill-stores0 * check-command: test-linearize $file * * check-output-ignore * check-output-excludes: store\\. */ sparse-0.6.4/validation/optim/kill-stores1.c000066400000000000000000000012661411531012200207700ustar00rootroot00000000000000struct s { int c[1]; }; static struct s x, y; static int p; static void foo0(void) { (x = y).c; // x = y; } static void foo1(void) { int *t = (x = y).c; // x = y; } static void foo2(void) { (x = y).c + 1; // x = y; } static void foo3(void) { (x = y).c[0]; // x = y; } static void foo4(void) { (p ? x : y).c[0]; // ; } static void foo5(void) { (p, y).c[0]; // ; } /* * check-name: kill-stores1 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-pattern(4): load\\. * check-output-pattern(4): load\\..*0\\[y\\] * check-output-pattern(4): store\\. * check-output-pattern(4): store\\..*0\\[x\\] * check-output-excludes: select\\. */ sparse-0.6.4/validation/optim/kill-stores2.c000066400000000000000000000003361411531012200207660ustar00rootroot00000000000000extern void def(int *); static void foo(void) { int c; def(&c); if (c) c = c; } /* * check-name: kill-stores2 * check-command: test-linearize $file * * check-output-ignore * check-output-excludes: store\\. */ sparse-0.6.4/validation/optim/killed-insn.c000066400000000000000000000003021411531012200206360ustar00rootroot00000000000000static void foo(int v) { int a[2] = { }; a; a[1] = v; } /* * check-name: killed-insn * check-command: test-linearize $file * * check-output-ignore * check-output-excludes: store\\. */ sparse-0.6.4/validation/optim/live-stores0.c000066400000000000000000000006041411531012200207660ustar00rootroot00000000000000void init(int *x); static int foo(void) { int a[2] = { 0, 123, }; if (a[1] != 123) return 1; init(a); if (a[1] == 123) return 2; return 0; } #if 0 void init(int *x) { x[0] = x[1] = 0; } #endif /* * check-name: live-stores * check-command: test-linearize $file * * check-output-ignore * check-output-contains: store.32 *\\$123 * check-output-pattern(2,3): store\\. */ sparse-0.6.4/validation/optim/load-converted.c000066400000000000000000000003431411531012200213400ustar00rootroot00000000000000static int foo(int *p, int i) { int a = p[i]; int b = p[i]; return (a - b); } /* * check-name: load-converted * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-excludes: add\\. */ sparse-0.6.4/validation/optim/load-dead.c000066400000000000000000000003041411531012200202410ustar00rootroot00000000000000void foo(int *p) { *p; } int *p; void bar(void) { *p; } /* * check-name: load-dead * check-command: test-linearize -Wno-decl $file * check-output-ignore * check-output-excludes: load\\. */ sparse-0.6.4/validation/optim/load-semi-volatile.c000066400000000000000000000005441411531012200221240ustar00rootroot00000000000000struct s { volatile int a; }; struct s s; void foo(void) { s; s.a; } /* * check-name: load-semi-volatile * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-pattern(1): load * * check-description: * The load at line 9 must be removed. * The load at line 10 is volatile and thus * must not be removed. */ sparse-0.6.4/validation/optim/lsr-and0.c000066400000000000000000000003541411531012200200540ustar00rootroot00000000000000unsigned lsr_and0(unsigned x) { unsigned t = (x & 0x00000fff); return (t >> 12) & t; } /* * check-name: lsr-and0 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-contains: ret\\..*\\$0$ */ sparse-0.6.4/validation/optim/lsr-and1.c000066400000000000000000000007371411531012200200620ustar00rootroot00000000000000// If (t >> S) is simplified into (x >> S) // then the whole expression will be 0. // The test is only interesting if the sub-expression // (x & M) is referenced more than once // (because otherwise other simplifications apply). unsigned lsr_and1(unsigned x) { unsigned t = (x & 0xfffff000); return ((t >> 12) ^ (x >> 12)) & t; } /* * check-name: lsr-and1 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-contains: ret\\..*\\$0$ */ sparse-0.6.4/validation/optim/lsr-asr.c000066400000000000000000000010171411531012200200140ustar00rootroot00000000000000int lsrasr0(unsigned int x) { return ((int) (x >> 15)) >> 15; } int lsrasr1(unsigned int x) { return ((int) (x >> 16)) >> 15; } int lsrasr2(unsigned int x) { return ((int) (x >> 16)) >> 16; } /* * check-name: lsr-asr * check-command: test-linearize -Wno-decl $file * * check-output-start lsrasr0: .L0: lsr.32 %r3 <- %arg1, $30 ret.32 %r3 lsrasr1: .L2: lsr.32 %r7 <- %arg1, $31 ret.32 %r7 lsrasr2: .L4: ret.32 $0 * check-output-end */ sparse-0.6.4/validation/optim/lsr-shl0.c000066400000000000000000000004161411531012200200770ustar00rootroot00000000000000unsigned mask(unsigned x) { return (x << 15) >> 15; } /* * check-name: lsr-shl0 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-contains: and\\..*0x1ffff * check-output-excludes: lsr\\. * check-output-excludes: shl\\. */ sparse-0.6.4/validation/optim/lsr-to-asr.c000066400000000000000000000005661411531012200204440ustar00rootroot00000000000000int lsr_to_asr24(int x) { return ((signed char)(((unsigned)x) >> 24)) == (x >> 24); } struct s { int :30; signed int f:2; }; int lsr_to_asr30(int a) { union { int i; struct s s; } u = { .i = a }; return u.s.f == (a >> 30); } /* * check-name: lsr-to-asr * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/mask-lsr.c000066400000000000000000000004251411531012200201640ustar00rootroot00000000000000// ((x & M) | y) >> S to (y >> S) when (M >> S) == 0 unsigned int foo(unsigned int x, unsigned int y) { return ((x & 0xff) | y) >> 8; } /* * check-name: mask-lsr * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-excludes: %arg1 */ sparse-0.6.4/validation/optim/mask-out.c000066400000000000000000000003421411531012200201710ustar00rootroot00000000000000unsigned mask(unsigned a, unsigned b) { return ((a & 0xffff0000) | b) & 0x0000ffff; } /* * check-name: mask-out * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-excludes: %arg1 */ sparse-0.6.4/validation/optim/mask1-setne0.c000066400000000000000000000004751411531012200206500ustar00rootroot00000000000000struct s { unsigned i:1; }; int foo(struct s x) { unsigned int i = x.i; if (i == 0) return 1; else if (i == 1) return 1; return 0; } /* * check-name: mask1-setne0 * check-command: test-linearize -Wno-decl $file * * check-output-start foo: .L0: ret.32 $1 * check-output-end */ sparse-0.6.4/validation/optim/memops-missed01.c000066400000000000000000000005321411531012200213550ustar00rootroot00000000000000void bar(int); void foo(void) { char buf[1] = { 42 }; const char *p = buf; const char **q = &p; int ch = 0; switch (**q) { case 4: ch = 2; } bar(ch); } /* * check-name: memops-missed01 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-excludes: store\\. * check-output-excludes: load\\. */ sparse-0.6.4/validation/optim/memops-missed02.c000066400000000000000000000003421411531012200213550ustar00rootroot00000000000000void foo(int a[]) { int i, val; for (;; i++) val = a[i] ? a[i] : val; } /* * check-name: memops-missed02 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-pattern(1): load\\. */ sparse-0.6.4/validation/optim/merge_bbe-adjust_phi.c000066400000000000000000000005451411531012200224750ustar00rootroot00000000000000extern int array[2]; static inline int stupid_select(int idx) { if (idx) idx = 0; return array[idx]; } int select(void) { int d = stupid_select(-1); return d; } /* * check-name: merge_bbe-adjust_phi * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-excludes: phisrc\\. * check-output-excludes: phi\\. */ sparse-0.6.4/validation/optim/missing-select.c000066400000000000000000000004431411531012200213610ustar00rootroot00000000000000static int foo(int **g) { int i = 1; int *a[2]; int **p; a[1] = &i; if (g) p = g; else p = &a[0]; if (!g) **p = 0; return i; } /* * check-name: missing-select * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-contains: select\\. */ sparse-0.6.4/validation/optim/muldiv-by-one.c000066400000000000000000000007361411531012200211270ustar00rootroot00000000000000typedef 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.6.4/validation/optim/muldiv-by-zero.c000066400000000000000000000004151411531012200213170ustar00rootroot00000000000000typedef 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.6.4/validation/optim/muldiv-minus-one.c000066400000000000000000000007331411531012200216450ustar00rootroot00000000000000typedef 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): neg\\. */ sparse-0.6.4/validation/optim/multi-phisrc.c000066400000000000000000000004271411531012200210550ustar00rootroot00000000000000void fun(void); void foo(int p, int a) { if (p == p) { switch (p) { case 0: break; case 1: a = 0; } } if (a) fun(); } /* * check-name: multi-phisrc * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-excludes: phi */ sparse-0.6.4/validation/optim/null-phi.c000066400000000000000000000001151411531012200201570ustar00rootroot00000000000000static int foo(void) { if (0) return 0; } /* * check-name: null-phi */ sparse-0.6.4/validation/optim/or-and-constant1.c000066400000000000000000000006661411531012200215320ustar00rootroot00000000000000unsigned int and_or_equ(unsigned int a) { return (a | 3) & 3; } int and_or_eqs(int a) { return (a | 3) & 3; } unsigned int or_and_equ(unsigned int a) { return (a & 3) | 3; } int or_and_eqs(int a) { return (a & 3) | 3; } /* * check-name: or-and-constant1 * check-command: test-linearize -Wno-decl $file * check-known-to-fail * * check-output-ignore * check-output-pattern(4): ret\\..*\\$3 * check-output-excludes: or\\. */ sparse-0.6.4/validation/optim/phi-count00.c000066400000000000000000000005351411531012200205030ustar00rootroot00000000000000inline int inl(int d, int e, int f) { switch (d) { case 0: return e; case 1: return f; default: return 0; } } void foo(int a, int b, int c) { while (1) { if (inl(a, b, c)) break; } } /* * check-name: phi-count00 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-pattern(0,2): phisrc */ sparse-0.6.4/validation/optim/phi-ret.c000066400000000000000000000004151411531012200200020ustar00rootroot00000000000000int foo(int p, int q, int v) { if (q) { if (p) { v = p; p = 0; } } else p = 0; if (p) return v + 1; return q; } /* * check-name: phi-ret * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-excludes: phi\\. */ sparse-0.6.4/validation/optim/range-check1.c000066400000000000000000000004541411531012200206650ustar00rootroot00000000000000#define N 1024 _Bool check_ok(long i) { return i >= 0 && i < N; } /* * check-name: range-check1 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-contains: setbe\\..*0x3ff * check-output-excludes: set[lga][te]\\. * check-output-excludes: set[ab]\\. */ sparse-0.6.4/validation/optim/range-check2.c000066400000000000000000000003521411531012200206630ustar00rootroot00000000000000#define N 1024 _Bool check_ok(int i) { return (i >= 0 && i < N) == (((unsigned int)i) < N); } /* * check-name: range-check2 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/reassoc-op-op1.c000066400000000000000000000003341411531012200212020ustar00rootroot00000000000000int foo(int x, int *ptr) { int t = x + 1; *ptr = t; return t + -1; } /* * check-name: reassoc-op-op1 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-pattern(1): add\\. */ sparse-0.6.4/validation/optim/restrict.c000066400000000000000000000016521411531012200202750ustar00rootroot00000000000000extern int g, h; void f00u(int *s) { g = *s; h = *s; } void f00r(int *restrict s) { g = *s; h = *s; } void f01u(int *a, int *b, int *s) { *a = *s; *b = *s; } void f01r(int *restrict a, int *restrict b, int *restrict s) { *a = *s; *b = *s; } /* * check-name: optim/restrict * check-command: test-linearize -Wno-decl $file * check-known-to-fail * * check-output-start f00u: .L0: load.32 %r2 <- 0[%arg1] store.32 %r2 -> 0[g] load.32 %r4 <- 0[%arg1] store.32 %r4 -> 0[h] ret f00r: .L2: load.32 %r6 <- 0[%arg1] store.32 %r6 -> 0[g] store.32 %r6 -> 0[h] ret f01u: .L4: load.32 %r10 <- 0[%arg3] store.32 %r10 -> 0[%arg1] load.32 %r13 <- 0[%arg3] store.32 %r13 -> 0[%arg2] ret f01r: .L6: load.32 %r16 <- 0[%arg3] store.32 %r16 -> 0[%arg1] store.32 %r16 -> 0[%arg2] ret * check-output-end */ sparse-0.6.4/validation/optim/select-and-shift.c000066400000000000000000000005741411531012200215720ustar00rootroot00000000000000#define S1 2 #define S2 5 #define S (S2 - S1) #define A (1 << S1) #define B (1 << S2) int foo(int p) { return ((p & A) ? B : 0) == ((((unsigned)p) & A) << S); } int bar(int p) { return ((p & B) ? A : 0) == ((((unsigned)p) & B) >> S); } /* * check-name: select-and-shift * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/select-constant-cond.c000066400000000000000000000004201411531012200224550ustar00rootroot00000000000000int t(int p, int a, int b) { return ((p == p) ? a : b) == a; } int f(int p, int a, int b) { return ((p != p) ? a : b) == b; } /* * check-name: select-constant-cond * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/select-same-args.c000066400000000000000000000003011411531012200215600ustar00rootroot00000000000000int foo(int p, int a) { return (p ? a : a) == a; } /* * check-name: select-same-args * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/select-select-true-false0.c000066400000000000000000000004651411531012200233200ustar00rootroot00000000000000int fw(int p, int a, int b) { return ((p ? 42 : 0) ? a : b) == ( p ? a : b); } int bw(int p, int a, int b) { return ((p ? 0 : 42) ? a : b) == ( p ? b : a); } /* * check-name: select-select-true-false0 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/select-select-true-false1.c000066400000000000000000000003501411531012200233120ustar00rootroot00000000000000int foo(int p) { int t = (p ? 42 : 0); return (t ? 42 : 0) == ( p ? 42 : 0); } /* * check-name: select-select-true-false1 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/select-select-true-true.c000066400000000000000000000003341411531012200231200ustar00rootroot00000000000000int foo(int p, int a, int b) { return ((p ? 42 : 43) ? a : b) == a ; } /* * check-name: select-select-true-true * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/select-self-zero.c000066400000000000000000000003641411531012200216200ustar00rootroot00000000000000int sel_self0x(int x) { return (x ? 0 : x) == 0; } int sel_selfx0(int x) { return (x ? x : 0) == x; } /* * check-name: select-self-zero * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/select-zero.c000066400000000000000000000003601411531012200206650ustar00rootroot00000000000000static int sel0(int a) { if (a) return 0; else return a; } /* * check-name: select-zero * check-command: test-linearize $file * * check-output-ignore * check-output-contains: ret.32 *\\$0 * check-output-excludes: select\\. */ sparse-0.6.4/validation/optim/set-uimm0.c000066400000000000000000000010501411531012200202460ustar00rootroot00000000000000static _Bool setlt0(unsigned int a) { return (a < 0u) == 0; } static _Bool setge0(unsigned int a) { return (a >= 0u) == 1; } static _Bool setle0(unsigned int a) { return (a <= 0u) == (a == 0); } static _Bool setgt0(unsigned int a) { return (a > 0u) == (a != 0); } static _Bool setlt1(unsigned int a) { return (a < 1u) == (a == 0); } static _Bool setge1(unsigned int a) { return (a >= 1u) == (a != 0); } /* * check-name: set-uimm0 * check-command: test-linearize $file * * check-output-ignore * check-output-pattern(6): ret\\.1 *\\$1 */ sparse-0.6.4/validation/optim/set-uimm1.c000066400000000000000000000004031411531012200202500ustar00rootroot00000000000000static _Bool setle_umax(unsigned int a) { return (a <= ~0) == 1; } static _Bool setgt_umax(unsigned int a) { return (a > ~0) == 0; } /* * check-name: set-uimm1 * check-command: test-linearize $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/set-uimm2.c000066400000000000000000000006511411531012200202560ustar00rootroot00000000000000static _Bool setlt_umax(unsigned int a) { return (a < ~0) == (a != ~0); } static _Bool setle_umax(unsigned int a) { return (a <= ~1) == (a != ~0); } static _Bool setge_umax(unsigned int a) { return (a >= ~0) == (a == ~0); } static _Bool setgt_umax(unsigned int a) { return (a > ~1) == (a == ~0); } /* * check-name: set-uimm2 * check-command: test-linearize $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/set-uimm3.c000066400000000000000000000003711411531012200202560ustar00rootroot00000000000000int le(int x) { return (x <= 0x7fffffffU) == (x >= 0); } int gt(int x) { return (x > 0x7fffffffU) == (x < 0); } /* * check-name: set-uimm3 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/setcc-mask.c000066400000000000000000000003431411531012200204640ustar00rootroot00000000000000int foo (int a) { return ((a == 0) & 1) == (a == 0); } /* * check-name: setcc-mask * check-command: test-linearize -Wno-decl $file * * check-output-start foo: .L0: ret.32 $1 * check-output-end */ sparse-0.6.4/validation/optim/setcc-setcc.c000066400000000000000000000012551411531012200206350ustar00rootroot00000000000000static _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.6.4/validation/optim/setcc-seteq.c000066400000000000000000000005411411531012200206520ustar00rootroot00000000000000static _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.6.4/validation/optim/setcc-setne.c000066400000000000000000000005441411531012200206520ustar00rootroot00000000000000static _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.6.4/validation/optim/setne0-sext.c000066400000000000000000000002701411531012200206100ustar00rootroot00000000000000long foo(int a) { return a != 0; } /* * check-name: setne0-sext * check-command: test-linearize -m64 -Wno-decl $file * * check-output-ignore * check-output-excludes: sext\\. */ sparse-0.6.4/validation/optim/setne0-trunc.c000066400000000000000000000002721411531012200207620ustar00rootroot00000000000000char foo(int a) { return a != 0; } /* * check-name: setne0-trunc * check-command: test-linearize -m64 -Wno-decl $file * * check-output-ignore * check-output-excludes: trunc\\. */ sparse-0.6.4/validation/optim/setne0-zext.c000066400000000000000000000003221411531012200206150ustar00rootroot00000000000000unsigned long foo(int a) { return (unsigned int) (a != 0); } /* * check-name: setne0-zext * check-command: test-linearize -m64 -Wno-decl $file * * check-output-ignore * check-output-excludes: zext\\. */ sparse-0.6.4/validation/optim/sext-sext.c000066400000000000000000000003151411531012200203750ustar00rootroot00000000000000int foo(signed char offset) { return (int)(short) offset; } /* * check-name: sext-sext * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-pattern(1): sext\\. */ sparse-0.6.4/validation/optim/sext.c000066400000000000000000000003721411531012200174170ustar00rootroot00000000000000int sext(int x) { return (x << 5) >> 5; } /* * check-name: sext * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-excludes: sext\\. * check-output-contains: asr\\.32 * check-output-contains: shl\\.32 */ sparse-0.6.4/validation/optim/sh-or-and0.c000066400000000000000000000006471411531012200203110ustar00rootroot00000000000000unsigned lsr_or_and0(unsigned x, unsigned b) { return (((x & 0x00000fff) | b) >> 12); } unsigned shl_or_and0(unsigned x, unsigned b) { return (((x & 0xfff00000) | b) << 12); } /* * check-name: sh-or-and0 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-pattern(1): lsr\\. * check-output-pattern(1): shl\\. * check-output-excludes: or\\. * check-output-excludes: and\\. */ sparse-0.6.4/validation/optim/sh-or-and1.c000066400000000000000000000006511411531012200203050ustar00rootroot00000000000000unsigned lsr_or_and1(unsigned x, unsigned b) { return (((x & 0xfffff000) | b) >> 12); } unsigned shl_or_and1(unsigned x, unsigned b) { return (((x & 0x000fffff) | b) << 12); } /* * check-name: sh-or-and1 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-pattern(1): lsr\\. * check-output-pattern(1): shl\\. * check-output-pattern(2): or\\. * check-output-excludes: and\\. */ sparse-0.6.4/validation/optim/sh-or-and2.c000066400000000000000000000007511411531012200203070ustar00rootroot00000000000000unsigned lsr_or_and2(unsigned x, unsigned b) { return (((x & 0xf0ffffff) | b) >> 12); } unsigned shl_or_and2(unsigned x, unsigned b) { return (((x & 0xffffff0f) | b) << 12); } /* * check-name: sh-or-and2 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-pattern(1): lsr\\. * check-output-pattern(1): shl\\. * check-output-pattern(2): or\\. * check-output-pattern(1): and\\..*\\$0xf0fff000 * check-output-pattern(1): and\\..*\\$0xfff0f */ sparse-0.6.4/validation/optim/shift-big.c000066400000000000000000000023531411531012200203110ustar00rootroot00000000000000typedef unsigned int u32; typedef int s32; s32 asr31(s32 a) { return a >> 31; } s32 asr32(s32 a) { return a >> 32; } s32 asr33(s32 a) { return a >> 33; } u32 lsr31(u32 a) { return a >> 31; } u32 lsr32(u32 a) { return a >> 32; } u32 lsr33(u32 a) { return a >> 33; } u32 shl31(u32 a) { return a << 31; } u32 shl32(u32 a) { return a << 32; } u32 shl33(u32 a) { return a << 33; } /* * check-name: optim/shift-big.c * check-command: test-linearize -Wno-decl -m64 $file * * check-error-ignore * check-output-start asr31: .L0: asr.32 %r2 <- %arg1, $31 ret.32 %r2 asr32: .L2: asr.32 %r5 <- %arg1, $32 ret.32 %r5 asr33: .L4: asr.32 %r8 <- %arg1, $33 ret.32 %r8 lsr31: .L6: lsr.32 %r11 <- %arg1, $31 ret.32 %r11 lsr32: .L8: lsr.32 %r14 <- %arg1, $32 ret.32 %r14 lsr33: .L10: lsr.32 %r17 <- %arg1, $33 ret.32 %r17 shl31: .L12: shl.32 %r20 <- %arg1, $31 ret.32 %r20 shl32: .L14: shl.32 %r23 <- %arg1, $32 ret.32 %r23 shl33: .L16: shl.32 %r26 <- %arg1, $33 ret.32 %r26 * check-output-end */ sparse-0.6.4/validation/optim/shift-shift.c000066400000000000000000000031271411531012200206650ustar00rootroot00000000000000unsigned int shl0(unsigned int x) { return x << 15 << 15; } unsigned int shl1(unsigned int x) { return x << 16 << 15; } unsigned int shl2(unsigned int x) { return x << 16 << 16; } unsigned int shl3(unsigned int x) { return x << 12 << 10 << 10; } unsigned int lsr0(unsigned int x) { return x >> 15 >> 15; } unsigned int lsr1(unsigned int x) { return x >> 16 >> 15; } unsigned int lsr2(unsigned int x) { return x >> 16 >> 16; } unsigned int lsr3(unsigned int x) { return x >> 12 >> 10 >> 10; } int asr0(int x) { return x >> 15 >> 15; } int asr1(int x) { return x >> 16 >> 15; } int asr2(int x) { return x >> 16 >> 16; } int asr3(int x) { return x >> 12 >> 10 >> 10; } /* * check-name: shift-shift * check-command: test-linearize -Wno-decl $file * * check-output-start shl0: .L0: shl.32 %r3 <- %arg1, $30 ret.32 %r3 shl1: .L2: shl.32 %r7 <- %arg1, $31 ret.32 %r7 shl2: .L4: ret.32 $0 shl3: .L6: ret.32 $0 lsr0: .L8: lsr.32 %r20 <- %arg1, $30 ret.32 %r20 lsr1: .L10: lsr.32 %r24 <- %arg1, $31 ret.32 %r24 lsr2: .L12: ret.32 $0 lsr3: .L14: ret.32 $0 asr0: .L16: asr.32 %r37 <- %arg1, $30 ret.32 %r37 asr1: .L18: asr.32 %r41 <- %arg1, $31 ret.32 %r41 asr2: .L20: asr.32 %r45 <- %arg1, $31 ret.32 %r45 asr3: .L22: asr.32 %r50 <- %arg1, $31 ret.32 %r50 * check-output-end */ sparse-0.6.4/validation/optim/shift-zext.c000066400000000000000000000003351411531012200205400ustar00rootroot00000000000000unsigned int foo(unsigned int x) { return (x << 20) >> 20; } /* * check-name: shift-zext * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-contains: and\\..*%arg1, \\$0xfff */ sparse-0.6.4/validation/optim/shl-and0.c000066400000000000000000000003541411531012200200420ustar00rootroot00000000000000unsigned shl_and0(unsigned x) { unsigned t = (x & 0xfff00000); return (t << 12) & t; } /* * check-name: shl-and0 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-contains: ret\\..*\\$0$ */ sparse-0.6.4/validation/optim/shl-and1.c000066400000000000000000000007371411531012200200500ustar00rootroot00000000000000// If (t << S) is simplified into (x << S) // then the whole expression will be 0. // The test is only interesting if the sub-expression // (x & M) is referenced more than once // (because otherwise other simplifications apply). unsigned shl_and1(unsigned x) { unsigned t = (x & 0x000fffff); return ((t << 12) ^ (x << 12)) & t; } /* * check-name: shl-and1 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-contains: ret\\..*\\$0$ */ sparse-0.6.4/validation/optim/shl-lsr0.c000066400000000000000000000004211411531012200200730ustar00rootroot00000000000000unsigned mask(unsigned x) { return (x >> 15) << 15; } /* * check-name: shl-lsr0 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-contains: and\\..*0xffff8000 * check-output-excludes: lsr\\. * check-output-excludes: shl\\. */ sparse-0.6.4/validation/optim/simplify-add-neg.c000066400000000000000000000003171411531012200215640ustar00rootroot00000000000000int add_neg(int x, int y) { return x + -y; } /* * check-name: simplify-add-neg * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-contains: sub\\..*%arg1, %arg2 */ sparse-0.6.4/validation/optim/simplify-cte-sub-addl.c000066400000000000000000000003261411531012200225310ustar00rootroot00000000000000int cte_sub_addl(int x) { return (1 - x) + 1; } /* * check-name: simplify-cte-sub-addl * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-contains: sub\\..*\\$2, %arg1 */ sparse-0.6.4/validation/optim/simplify-cte-sub-addr.c000066400000000000000000000003261411531012200225370ustar00rootroot00000000000000int cte_sub_addr(int x) { return 2 - (x + 1); } /* * check-name: simplify-cte-sub-addr * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-contains: sub\\..*\\$1, %arg1 */ sparse-0.6.4/validation/optim/simplify-cte-sub-subr.c000066400000000000000000000003211411531012200225730ustar00rootroot00000000000000int cte_sub_subr(int x) { return 1 - (1 - x); } /* * check-name: simplify-cte-sub-subr * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-contains: ret\\..* %arg1 */ sparse-0.6.4/validation/optim/simplify-neg-add-cte.c000066400000000000000000000003331411531012200223330ustar00rootroot00000000000000#define C 3 int foo(int x) { return -(x + C) == (-3 - x); } /* * check-name: simplify-neg-add-cte * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-contains: ret\\..*\\$1 */ sparse-0.6.4/validation/optim/simplify-neg-add.c000066400000000000000000000003171411531012200215640ustar00rootroot00000000000000int neg_add(int x, int y) { return -x + y; } /* * check-name: simplify-neg-add * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-contains: sub\\..*%arg2, %arg1 */ sparse-0.6.4/validation/optim/simplify-neg-not.c000066400000000000000000000003041411531012200216300ustar00rootroot00000000000000int foo(int x) { return -(~x) == x + 1; } /* * check-name: simplify-neg-not * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-contains: ret\\..*\\$1 */ sparse-0.6.4/validation/optim/simplify-neg-sub.c000066400000000000000000000003201411531012200216170ustar00rootroot00000000000000int foo(int x, int y) { return -(x - y) == (y - x); } /* * check-name: simplify-neg-sub * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-contains: ret\\..*\\$1 */ sparse-0.6.4/validation/optim/simplify-not-add-cte.c000066400000000000000000000003331411531012200223620ustar00rootroot00000000000000#define C 3 int foo(int x) { return ~(x + C) == (~C - x); } /* * check-name: simplify-not-add-cte * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-contains: ret\\..*\\$1 */ sparse-0.6.4/validation/optim/simplify-not-neg.c000066400000000000000000000003061411531012200216320ustar00rootroot00000000000000int foo(int x) { return ~(-x) == (x - 1); } /* * check-name: simplify-not-neg * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-contains: ret\\..*\\$1 */ sparse-0.6.4/validation/optim/simplify-not-sub-cte.c000066400000000000000000000003331411531012200224230ustar00rootroot00000000000000#define C 3 int foo(int x) { return ~(C - x) == (x + ~C); } /* * check-name: simplify-not-sub-cte * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-contains: ret\\..*\\$1 */ sparse-0.6.4/validation/optim/simplify-not-xor-cte.c000066400000000000000000000003331411531012200224420ustar00rootroot00000000000000#define C 3 int foo(int x) { return ~(x ^ C) == (x ^ ~C); } /* * check-name: simplify-not-xor-cte * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-contains: ret\\..*\\$1 */ sparse-0.6.4/validation/optim/simplify-same-add-subl.c000066400000000000000000000003621411531012200227030ustar00rootroot00000000000000int add_subl(int x, int y) { return (x + y) - x; } /* * check-name: simplify-same-add-subl * check-command: test-linearize -Wno-decl $file * * check-output-start add_subl: .L0: ret.32 %arg2 * check-output-end */ sparse-0.6.4/validation/optim/simplify-same-add-subr.c000066400000000000000000000003621411531012200227110ustar00rootroot00000000000000int add_subr(int x, int y) { return (x + y) - y; } /* * check-name: simplify-same-add-subr * check-command: test-linearize -Wno-decl $file * * check-output-start add_subr: .L0: ret.32 %arg1 * check-output-end */ sparse-0.6.4/validation/optim/simplify-same-addl-sub.c000066400000000000000000000003171411531012200227030ustar00rootroot00000000000000int foo(int x, int y) { return x + (y - x); } /* * check-name: simplify-same-addl-sub * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-contains: ret\\..*%arg2 */ sparse-0.6.4/validation/optim/simplify-same-sub-addl.c000066400000000000000000000003171411531012200227030ustar00rootroot00000000000000int foo(int x, int y) { return (x - y) + y; } /* * check-name: simplify-same-sub-addl * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-contains: ret\\..*%arg1 */ sparse-0.6.4/validation/optim/simplify-same-subl-add.c000066400000000000000000000004271411531012200227050ustar00rootroot00000000000000int subl_add(int x, int y) { return x - (x + y); } /* * check-name: simplify-same-subl-add * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-contains: neg\\..* %arg2 * check-output-excludes: add\\. * check-output-excludes: sub\\. */ sparse-0.6.4/validation/optim/simplify-same-subr-add.c000066400000000000000000000004271411531012200227130ustar00rootroot00000000000000int subr_add(int x, int y) { return x - (y + x); } /* * check-name: simplify-same-subr-add * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-contains: neg\\..* %arg2 * check-output-excludes: add\\. * check-output-excludes: sub\\. */ sparse-0.6.4/validation/optim/simplify-sub-neg.c000066400000000000000000000003171411531012200216250ustar00rootroot00000000000000int sub_neg(int x, int y) { return x - -y; } /* * check-name: simplify-sub-neg * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-contains: add\\..*%arg., %arg. */ sparse-0.6.4/validation/optim/simplify-zero-sub.c000066400000000000000000000003031411531012200220260ustar00rootroot00000000000000int zero_sub(int x) { return 0 - x; } /* * check-name: simplify-zero-sub * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-contains: neg\\..* %arg1 */ sparse-0.6.4/validation/optim/store-dominated.c000066400000000000000000000003231411531012200215260ustar00rootroot00000000000000static int a[]; static void foo(void) { int *c = &a[1]; *c = *c = 0; } /* * check-name: store-dominated * check-command: test-linearize $file * * check-output-ignore * check-output-excludes: add\\. */ sparse-0.6.4/validation/optim/testsuite.c000066400000000000000000000003051411531012200204610ustar00rootroot00000000000000int foo(void) { return 0; } /* * check-name: testsuite * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-match(ret): \\$0 * check-output-returns: 0 */ sparse-0.6.4/validation/optim/trivial-phi01.c000066400000000000000000000003741411531012200210270ustar00rootroot00000000000000void foo(int a) { if (a) { while (a) { switch (0) { default: a = 0; case 0:; } } } } /* * check-name: trivial-phi01 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-excludes: phi\\. */ sparse-0.6.4/validation/optim/trivial-phis.c000066400000000000000000000003351411531012200210460ustar00rootroot00000000000000void foo(int a) { while (1) a ^= 0; } /* * check-name: trivial phis * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-excludes: phi\\. * check-output-excludes: phisrc\\. */ sparse-0.6.4/validation/optim/trunc-mask-zext.c000066400000000000000000000004171411531012200215100ustar00rootroot00000000000000unsigned long long foo(unsigned long long x) { return (((unsigned int) x) & 0x7ffU); } /* * check-name: trunc-mask-zext * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-excludes: trunc\\. * check-output-excludes: zext\\. */ sparse-0.6.4/validation/optim/trunc-not0.c000066400000000000000000000010301411531012200204350ustar00rootroot00000000000000typedef __INT32_TYPE__ int32; typedef __INT64_TYPE__ int64; static _Bool sfoo(int64 a) { return ((int32) ~a) == (~ (int32)a); } static _Bool sbar(int64 a) { return (~(int32) ~a) == (int32)a; } typedef __UINT32_TYPE__ uint32; typedef __UINT64_TYPE__ uint64; static _Bool ufoo(uint64 a) { return ((uint32) ~a) == (~ (uint32)a); } static _Bool ubar(uint64 a) { return (~(uint32) ~a) == (uint32)a; } /* * check-name: trunc-not0 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/trunc-or-shl.c000066400000000000000000000005601411531012200207700ustar00rootroot00000000000000// because of the cast to char, the fist arg should be eliminated // and the whole reduced to TRUNC(%arg2, 8) char foo(int a, int b) { return (a << 8) | b; } /* * check-name: trunc-or-shl * check-command: test-linearize -Wno-decl $file * check-known-to-fail * * check-output-ignore * check-output-excludes: %arg1 * check-output-contains: trunc\\..*%arg2 */ sparse-0.6.4/validation/optim/trunc-seteq0.c000066400000000000000000000006001411531012200207600ustar00rootroot00000000000000struct S { int :1; signed int s:2; unsigned int u:3; }; int os(int i, struct S *b) { return i || b->s; } int ou(int i, struct S *b) { return i || b->u; } /* * check-name: trunc-seteq0 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-excludes: trunc\\. * check-output-excludes: sext\\. * check-output-excludes: zext\\. */ sparse-0.6.4/validation/optim/trunc-setne0.c000066400000000000000000000004361411531012200207640ustar00rootroot00000000000000struct s { unsigned int u:1; }; unsigned int foo(struct s x) { if (x.u) return 1; else return 0; } /* * check-name: trunc-setne0 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-contains: and\\. * check-output-excludes: trunc\\. */ sparse-0.6.4/validation/optim/trunc-trunc.c000066400000000000000000000003101411531012200207100ustar00rootroot00000000000000char foo(int a) { return ((((short) a) + 1) - 1); } /* * check-name: trunc-trunc * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-pattern(1): trunc\\. */ sparse-0.6.4/validation/optim/void-if-convert.c000066400000000000000000000005071411531012200214470ustar00rootroot00000000000000int 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.6.4/validation/optim/volatile-bitfield.c000066400000000000000000000003311411531012200220260ustar00rootroot00000000000000struct s { int f:3; }; void foo(volatile struct s *p) { p->f; } /* * check-name: volatile-bitfield * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-contains: load\\. */ sparse-0.6.4/validation/optim/volatile-side-effect.c000066400000000000000000000003311411531012200224220ustar00rootroot00000000000000void foo(int p, volatile int *ptr) { p ? : *ptr; p ? : *ptr; } /* * check-name: volatile-side-effect * check-command: test-linearize -Wno-decl $file * check-output-ignore * * check-output-pattern(2): load */ sparse-0.6.4/validation/optim/volatile-store00.c000066400000000000000000000005131411531012200215420ustar00rootroot00000000000000void foo(volatile int *p) { *p = 0; *p = 0; } void bar(void) { extern volatile int i; i = 0; i = 0; } void baz(void) { volatile int i; i = 0; i = 0; } /* * check-name: keep volatile stores * check-command: test-linearize -Wno-decl -fdump-ir=final $file * check-output-ignore * check-output-pattern(6): store\\. */ sparse-0.6.4/validation/optim/zext-and.c000066400000000000000000000003251411531012200201640ustar00rootroot00000000000000unsigned int foo(unsigned char x) { return (unsigned int)x & 0xffffU; } /* * check-name: zext-and * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-excludes: and\\. */ sparse-0.6.4/validation/optim/zext-and1.c000066400000000000000000000003341411531012200202450ustar00rootroot00000000000000unsigned int bar(unsigned char x) { return (unsigned int)x & 0xff01U; } /* * check-name: zext-and1 * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-contains: and\\..*\\$1 */ sparse-0.6.4/validation/optim/zext-asr.c000066400000000000000000000003551411531012200202120ustar00rootroot00000000000000unsigned short foo(unsigned short a) { return a >> 16; } /* * check-name: zext-asr * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-contains: ret\\..*\\$0 * check-output-excludes: asr\\. */ sparse-0.6.4/validation/optim/zext-cmpu.c000066400000000000000000000014131411531012200203650ustar00rootroot00000000000000int ltg(unsigned x) { return (((long long)x) < 0x100000000ULL) == 1; } int ltl(unsigned x) { return (((long long)x) < 0x0ffffffffULL) == (x < 0xffffffffU); } int leg(unsigned x) { return (((long long)x) <= 0x0ffffffffULL) == 1; } int lel(unsigned x) { return (((long long)x) <= 0x0fffffffeULL) == (x <= 0xfffffffeU); } int geg(unsigned x) { return (((long long)x) >= 0x100000000ULL) == 0; } int gel(unsigned x) { return (((long long)x) >= 0x0ffffffffULL) == (x >= 0xffffffffU); } int gtg(unsigned x) { return (((long long)x) > 0x0ffffffffULL) == 0; } int gtl(unsigned x) { return (((long long)x) > 0x0fffffffeULL) == (x > 0xfffffffeU); } /* * check-name: zext-cmpu * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-returns: 1 */ sparse-0.6.4/validation/optim/zext-sext.c000066400000000000000000000003611411531012200204050ustar00rootroot00000000000000int foo(unsigned char offset) { return (int)(short) offset; } /* * check-name: zext-sext * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-excludes: sext\\. * check-output-pattern(1): zext\\. */ sparse-0.6.4/validation/optim/zext-zext.c000066400000000000000000000003721411531012200204160ustar00rootroot00000000000000int foo(unsigned char offset) { return (int)(unsigned short) offset; } /* * check-name: zext-zext * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-excludes: sext\\. * check-output-pattern(1): zext\\. */ sparse-0.6.4/validation/option-parsing-00.c000066400000000000000000000001271411531012200204700ustar00rootroot00000000000000 /* * check-name: option parsing 00 * check-command: sparse -foptimize-xyz $file */ sparse-0.6.4/validation/option-parsing-01.c000066400000000000000000000001321411531012200204650ustar00rootroot00000000000000 /* * check-name: option parsing 01 * check-command: sparse -fno-optimize-xyz $file */ sparse-0.6.4/validation/outer-scope.c000066400000000000000000000005161411531012200175510ustar00rootroot00000000000000#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.6.4/validation/overflow.c000066400000000000000000000005311411531012200171440ustar00rootroot00000000000000extern int a; int a = __INT_MAX__ * 2; int foo(void) { return __INT_MAX__ * 2; } /* * check-name: overflow * check-command: sparse -Wno-decl $file * * check-known-to-fail * check-error-start bug-overflow.c:3:21: warning: integer overflow in expression bug-overflow.c:7:28: warning: integer overflow in expression * check-error-end */ sparse-0.6.4/validation/packed-bitfield0.c000066400000000000000000000015201411531012200203670ustar00rootroot00000000000000#define alignof(X) __alignof__(X) #define __packed __attribute__((packed)) struct sa { int a:7; int c:10; int b:2; } __packed; _Static_assert(alignof(struct sa) == 1, "alignof(struct sa)"); _Static_assert( sizeof(struct sa) == 3, "sizeof(struct sa)"); static int get_size(void) { return sizeof(struct sa); } static void chk_align(struct sa sa, struct sa *p) { _Static_assert(alignof(sa) == 1, "alignof(sa)"); _Static_assert(alignof(*p) == 1, "alignof(*p)"); } static int fp0(struct sa *sa) { return sa->c; } static int fpx(struct sa *sa, int idx) { return sa[idx].c; } static int fglobal(void) { extern struct sa g; return g.c; } static struct sa l; static int flocal(void) { return l.c; } int main(void) { extern void fun(struct sa *); struct sa sa = { 0 }; fun(&sa); return 0; } /* * check-name: packed-bitfield0 */ sparse-0.6.4/validation/packed-bitfield1.c000066400000000000000000000005511411531012200203730ustar00rootroot00000000000000#define __packed __attribute__((packed)) struct s { unsigned int f0:1; unsigned int f1:1; unsigned int pad:6; } __packed; _Static_assert(sizeof(struct s) == 1, "sizeof(struct s)"); extern struct s g; static int foo(struct s *ptr) { int f = 0; f += g.f0; f += g.f1; f += ptr->f0; f += ptr->f1; return f; } /* * check-name: packed-bitfield1 */ sparse-0.6.4/validation/packed-bitfield2.c000066400000000000000000000003531411531012200203740ustar00rootroot00000000000000struct bf2 { unsigned p1:2; unsigned i1:32; unsigned p2:2; unsigned s9:9; unsigned s9:9; unsigned s9:9; unsigned b1:1; } __attribute__((packed)); _Static_assert(sizeof(struct bf2) == 8); /* * check-name: packed-bitfield2 */ sparse-0.6.4/validation/packed-bitfield3.c000066400000000000000000000007511411531012200203770ustar00rootroot00000000000000#define __packed __attribute__((packed)) typedef unsigned char u8; typedef __UINT16_TYPE__ u16; typedef __UINT32_TYPE__ u32; typedef __UINT64_TYPE__ u64; struct b { u32 a:1; u32 b:2; u32 c:4; u32 d:8; u32 e:16; } __packed; _Static_assert(__alignof(struct b) == 1); _Static_assert( sizeof(struct b) == 4); struct c { u8 a; u8 b; u64 c:48; } __packed; _Static_assert(__alignof(struct c) == 1); _Static_assert( sizeof(struct c) == 8); /* * check-name: packed-bitfield3 */ sparse-0.6.4/validation/packed-bitfield4.c000066400000000000000000000004571411531012200204030ustar00rootroot00000000000000#define __packed __attribute__((packed)) typedef __UINT32_TYPE__ u32; struct s { u32 f:24; } __packed; _Static_assert(sizeof(struct s) == 3); static int ld(struct s *s) { return s->f; } /* * check-name: packed-bitfield4 * check-description: Is check_access() OK with short packed bitfields? */ sparse-0.6.4/validation/packed-bitfield5.c000066400000000000000000000005131411531012200203750ustar00rootroot00000000000000#define __packed __attribute__((packed)) typedef __UINT32_TYPE__ u32; struct s { u32 a:5; u32 f:30; u32 z:5; } __packed; _Static_assert(sizeof(struct s) == 5); static int ld(struct s *s) { return s->f; } /* * check-name: packed-bitfield5 * check-description: is check_access() OK with 'overlapping' packed bitfields? */ sparse-0.6.4/validation/packed-deref0.c000066400000000000000000000005131411531012200176730ustar00rootroot00000000000000#define __packed __attribute__((packed)) typedef struct { __INT8_TYPE__ a; __INT16_TYPE__ b; __INT32_TYPE__ c; } __packed obj_t; _Static_assert(sizeof(obj_t) == 7, "sizeof packed struct"); static void foo(obj_t *ptr, int val) { ptr->c = val; } static void bar(obj_t o) { foo(&o, 0); } /* * check-name: packed-deref0 */ sparse-0.6.4/validation/packed-struct.c000066400000000000000000000012321411531012200200510ustar00rootroot00000000000000#define __packed __attribute__((packed)) typedef unsigned char u8; typedef __UINT16_TYPE__ u16; typedef __UINT32_TYPE__ u32; typedef __UINT64_TYPE__ u64; struct a { u8 a; u8 b; u16 c; } __packed; _Static_assert(__alignof(struct a) == 1, "align struct"); _Static_assert( sizeof(struct a) == 4, " size struct"); struct b { u32 a; u32 b; } __packed; _Static_assert(__alignof(struct b) == 1, "align struct"); _Static_assert( sizeof(struct b) == 8, "size struct"); struct c { u16 a; u32 b; } __packed; _Static_assert(__alignof(struct c) == 1, "align struct"); _Static_assert( sizeof(struct c) == 6, "size struct"); /* * check-name: packed-struct */ sparse-0.6.4/validation/parsing/000077500000000000000000000000001411531012200166015ustar00rootroot00000000000000sparse-0.6.4/validation/parsing/enum-attr.c000066400000000000000000000012071411531012200206610ustar00rootroot00000000000000#define __attr __attribute__((deprecated)) enum { old __attr, cur __attr = 42, new, }; enum odd { odd = __attr 33, }; enum bad { bad = 43 __attr, }; /* * check-name: enum-attr * * check-error-start parsing/enum-attr.c:10:15: error: typename in expression parsing/enum-attr.c:10:15: error: undefined identifier '__attribute__' parsing/enum-attr.c:10:15: error: bad constant expression type parsing/enum-attr.c:10:22: error: Expected } at end of specifier parsing/enum-attr.c:10:22: error: got 33 parsing/enum-attr.c:14:18: error: Expected } at end of specifier parsing/enum-attr.c:14:18: error: got __attribute__ * check-error-end */ sparse-0.6.4/validation/plain-char-compatibility.c000066400000000000000000000005731411531012200221740ustar00rootroot00000000000000static int sfoo(void) { return __builtin_types_compatible_p(char, signed char); } static int ufoo(void) { return __builtin_types_compatible_p(char, unsigned char); } /* * check-name: plain-char-compatibility * check-command: test-linearize $file * check-known-to-fail * * check-output-ignore * check-output-pattern(2): ret.*\\$0 * check-output-excludes: ret.*\\$1 */ sparse-0.6.4/validation/pragma-once.c000066400000000000000000000001111411531012200174640ustar00rootroot00000000000000#pragma once #include "pragma-once.c" /* * check-name: #pragma once */ sparse-0.6.4/validation/preprocessor/000077500000000000000000000000001411531012200176645ustar00rootroot00000000000000sparse-0.6.4/validation/preprocessor/bad-cmdline-include.c000066400000000000000000000004201411531012200236040ustar00rootroot00000000000000#error some random error /* * check-name: bad-cmdline-include * check-command: sparse -include $file * * check-error-start command-line: note: in included file (through builtin): preprocessor/bad-cmdline-include.c:1:2: error: some random error * check-error-end */ sparse-0.6.4/validation/preprocessor/base-file.c000066400000000000000000000004141411531012200216560ustar00rootroot00000000000000__FILE__ __BASE_FILE__ #include "base-file.h" /* * check-name: base file * check-command: sparse -E $file * * check-output-start "preprocessor/base-file.c" "preprocessor/base-file.c" "preprocessor/base-file.h" "preprocessor/base-file.c" * check-output-end */ sparse-0.6.4/validation/preprocessor/base-file.h000066400000000000000000000000271411531012200216630ustar00rootroot00000000000000__FILE__ __BASE_FILE__ sparse-0.6.4/validation/preprocessor/builtin.c000066400000000000000000000003711411531012200214770ustar00rootroot00000000000000__CHECKER__ F(__CHECKER__,__CHECKER__) S(#__CHECKER__) const char str[] = "__CHECKER__"; /* * check-name: builtin * check-command: sparse -E $file * * check-output-start 1 F(1,1) S(#1) const char str[] = "__CHECKER__"; * check-output-end */ sparse-0.6.4/validation/preprocessor/cli-D-arg.c000066400000000000000000000002141411531012200215240ustar00rootroot00000000000000A B /* * check-name: cli: -D MACRO * check-command: sparse -E -D A -D B=abc $file * * check-output-start 1 abc * check-output-end */ sparse-0.6.4/validation/preprocessor/cli-D-space.c000066400000000000000000000002311411531012200220450ustar00rootroot00000000000000M(0,1) /* * check-name: cli: allow spaces in macros * check-command: sparse -E '-DM(X, Y)=a' $file * * check-output-start a * check-output-end */ sparse-0.6.4/validation/preprocessor/counter1.c000066400000000000000000000002211411531012200215630ustar00rootroot00000000000000__COUNTER__ __COUNTER__ /* * check-name: __COUNTER__ #1 * check-command: sparse -E $file * * check-output-start 0 1 * check-output-end */ sparse-0.6.4/validation/preprocessor/counter2.c000066400000000000000000000004301411531012200215660ustar00rootroot00000000000000__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.6.4/validation/preprocessor/counter2.h000066400000000000000000000000251411531012200215730ustar00rootroot00000000000000__FILE__ __COUNTER__ sparse-0.6.4/validation/preprocessor/counter3.c000066400000000000000000000004121411531012200215670ustar00rootroot00000000000000/* * 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.6.4/validation/preprocessor/directive-within-macro.c000066400000000000000000000007101411531012200244030ustar00rootroot00000000000000#define f(x) x f(1 #if 1 // OK a #elif 2 // OK b #else // OK c #endif // OK #ifdef f // OK d #endif // OK #ifndef f // OK e #endif // OK 3) f(1 #define x y // KO 3) /* * check-name: directive-within-macro * check-command: sparse -E $file * * check-output-start 1 a d 3 1 3 * check-output-end * * check-error-start preprocessor/directive-within-macro.c:20:1: warning: directive in macro's argument list * check-error-end */ sparse-0.6.4/validation/preprocessor/dump-macros-empty.c000066400000000000000000000002461411531012200234150ustar00rootroot00000000000000/* * check-name: dump-macros with empty file * check-command: sparse -E -dD empty-file * * check-output-ignore check-output-pattern(1): #define __CHECKER__ 1 */ sparse-0.6.4/validation/preprocessor/dump-macros-multi.c000066400000000000000000000002601411531012200234050ustar00rootroot00000000000000/* * check-name: dump-macros with multiple files * check-command: sparse -E -dD empty-file $file * * check-output-ignore check-output-pattern(2): #define __CHECKER__ 1 */ sparse-0.6.4/validation/preprocessor/dump-macros-only.c000066400000000000000000000016131411531012200232370ustar00rootroot00000000000000 #define ABC abc #undef ABC #define DEF def #undef DEF #define DEF xyz #define NYDEF ydef #define STRING(x) #x #define CONCAT(x,y) x ## y #define unlocks(...) annotate(unlock_func(__VA_ARGS__)) #define apply(x,...) x(__VA_ARGS__) int main(int argc, char *argv[]) { return 0; } /* * check-name: dump-macros only -dM * check-command: sparse -E -dM -DIJK=ijk -UNDEF -UNYDEF $file * * check-output-ignore check-output-pattern(1): #define __CHECKER__ 1 check-output-contains: #define IJK ijk check-output-contains: #define DEF xyz check-output-contains: #define NYDEF ydef check-output-contains: #define STRING(x) #x check-output-contains: #define CONCAT(x,y) x ## y check-output-contains: #define unlocks(...) annotate(unlock_func(__VA_ARGS__)) check-output-contains: #define apply(x,...) x(__VA_ARGS__) check-output-excludes: int main(int argc, char \\*argv\\[\\]) check-output-excludes: ^\\[^#] */ sparse-0.6.4/validation/preprocessor/dump-macros.c000066400000000000000000000015421411531012200222610ustar00rootroot00000000000000#define ABC abc #undef ABC #define DEF def #undef DEF #define DEF xyz #define NYDEF ydef #define STRING(x) #x #define CONCAT(x,y) x ## y #define unlocks(...) annotate(unlock_func(__VA_ARGS__)) #define apply(x,...) x(__VA_ARGS__) int main(int argc, char *argv[]) { return 0; } /* * check-name: dump-macros * check-command: sparse -E -dD -DIJK=ijk -UNDEF -UNYDEF $file * * check-output-ignore check-output-pattern(1): #define __CHECKER__ 1 check-output-contains: #define IJK ijk check-output-contains: #define DEF xyz check-output-contains: #define NYDEF ydef check-output-contains: #define STRING(x) #x check-output-contains: #define CONCAT(x,y) x ## y check-output-contains: #define unlocks(...) annotate(unlock_func(__VA_ARGS__)) check-output-contains: #define apply(x,...) x(__VA_ARGS__) check-output-contains: int main(int argc, char \\*argv\\[\\]) */ sparse-0.6.4/validation/preprocessor/dynamic.c000066400000000000000000000007361411531012200214620ustar00rootroot00000000000000#if defined(__LINE__) __LINE__ #endif #if defined(__FILE__) __FILE__ #endif #if defined(__BASE_FILE__) __BASE_FILE__ #endif #if defined(__DATE__) date #endif #if defined(__TIME__) time #endif #if defined(__COUNTER__) counter #endif #if defined(__INCLUDE_LEVEL__) __INCLUDE_LEVEL__ #endif /* * check-name: dynamic-macros * check-command: sparse -E $file * * check-output-start 2 "preprocessor/dynamic.c" "preprocessor/dynamic.c" date time counter 0 * check-output-end */ sparse-0.6.4/validation/preprocessor/early-escape.c000066400000000000000000000007271411531012200224100ustar00rootroot00000000000000#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.6.4/validation/preprocessor/empty-char-constant.c000066400000000000000000000002151411531012200237260ustar00rootroot00000000000000#if 0 '' #endif /* * check-name: empty-char-constant * check-command: sparse -E $file * * check-output-start * check-output-end */ sparse-0.6.4/validation/preprocessor/expand-and-nl.c000066400000000000000000000002251411531012200224550ustar00rootroot00000000000000#define M(X) X-X M(a b) /* * check-name: expand-and-nl * check-command: sparse -E $file * * check-output-start a b-a b * check-output-end */ sparse-0.6.4/validation/preprocessor/expand-redef.c000066400000000000000000000005611411531012200223740ustar00rootroot00000000000000#define f(x) x x f(1 #undef f #define f 2 f) /* * check-name: expand-redef * check-command: sparse -E $file * * check-output-start 1 2 1 2 * check-output-end * * check-error-start preprocessor/expand-redef.c:3:1: warning: directive in macro's argument list preprocessor/expand-redef.c:4:1: warning: directive in macro's argument list * check-error-end */ sparse-0.6.4/validation/preprocessor/extra-token.c000066400000000000000000000004511411531012200222710ustar00rootroot00000000000000#define THIS 0 #ifdef THIS == 1 #endif /* * check-name: preprocessor/extra-token.c * check-command: sparse -E $file * check-known-to-fail * * check-error-start preprocessor/extra-token.c:2:13: warning: extra tokens at end of #ifdef directive * check-error-end * * check-output-ignore */ sparse-0.6.4/validation/preprocessor/freestanding.c000066400000000000000000000002251411531012200225000ustar00rootroot00000000000000__STDC_HOSTED__ /* * check-name: freestanding * check-command: sparse -E -ffreestanding $file * * check-output-start 0 * check-output-end */ sparse-0.6.4/validation/preprocessor/has-attribute.c000066400000000000000000000020041411531012200226000ustar00rootroot00000000000000#ifndef __has_attribute __has_attribute()??? Quesako? #define __has_attribute(x) 0 #else "has __has_attribute(), yeah!" #endif 123 __has_attribute(nothinx) def #if __has_attribute(nothinx) #error "not a attribute!" #endif #if 1 \ && __has_attribute(packed) \ && __has_attribute(aligned) \ && __has_attribute(const) \ && __has_attribute(pure) \ && __has_attribute(noreturn) \ && __has_attribute(designated_init) \ && __has_attribute(transparent_union) \ "ok gcc" #endif #if 1 \ && __has_attribute(fastcall) \ "ok gcc ignore" #endif #if 1 \ && __has_attribute(nocast) \ && __has_attribute(noderef) \ && __has_attribute(safe) \ && __has_attribute(force) \ && __has_attribute(bitwise) \ && __has_attribute(address_space) \ && __has_attribute(context) \ "ok sparse specific" #endif /* * check-name: has-attribute * check-command: sparse -E $file * * check-output-start "has __has_attribute(), yeah!" 123 0 def "ok gcc" "ok gcc ignore" "ok sparse specific" * check-output-end */ sparse-0.6.4/validation/preprocessor/has-builtin.c000066400000000000000000000013721411531012200222520ustar00rootroot00000000000000#ifndef __has_builtin __has_builtin()??? Quesako? #define __has_builtin(x) 0 #else "has __has_builtin(), yeah!" #endif #if __has_builtin(nothing) #error "not a builtin!" #endif #if __has_builtin(__builtin_offsetof) \ || __has_builtin(__builtin_types_compatible_p) #error "builtin ops are not builtin functions!" #endif #if __has_builtin(__builtin_va_list) \ || __has_builtin(__builtin_ms_va_list) #error "builtin types are not builtin functions!" #endif #if __has_builtin(__builtin_abs) abs #endif #if __has_builtin(__builtin_constant_p) constant_p #endif 123 __has_builtin(abc) def /* * check-name: has-builtin * check-command: sparse -E $file * * check-output-start "has __has_builtin(), yeah!" abs constant_p 123 0 def * check-output-end */ sparse-0.6.4/validation/preprocessor/has-feature.c000066400000000000000000000005101411531012200222300ustar00rootroot00000000000000#ifndef __has_feature __has_feature()??? Quesako? #define __has_feature(x) 0 #else "has __has_feature(), yeah!" #endif #if __has_feature(not_a_feature) #error "not a feature!" #endif /* * check-name: has-feature * check-command: sparse -E $file * * check-output-start "has __has_feature(), yeah!" * check-output-end */ sparse-0.6.4/validation/preprocessor/hosted.c000066400000000000000000000002111411531012200213100ustar00rootroot00000000000000__STDC_HOSTED__ /* * check-name: hosted * check-command: sparse -E -fhosted $file * * check-output-start 1 * check-output-end */ sparse-0.6.4/validation/preprocessor/ident-pragma.c000066400000000000000000000003131411531012200223750ustar00rootroot00000000000000#pragma ident "foo" /* * check-description: check that '#pragma ident ... " is ignored. * check-name: ident-pragma * check-command: sparse -E $file * * check-output-start * check-output-end */ sparse-0.6.4/validation/preprocessor/ident.c000066400000000000000000000003061411531012200211320ustar00rootroot00000000000000#ident "foo" /* * check-description: check that the non-standard "#ident ..." is ignored. * check-name: ident * check-command: sparse -E $file * * check-output-start * check-output-end */ sparse-0.6.4/validation/preprocessor/include-level.c000066400000000000000000000003611411531012200225600ustar00rootroot00000000000000__FILE__: __INCLUDE_LEVEL__ #include "include-level.h" /* * check-name: include-level * check-command: sparse -E $file * * check-output-start "preprocessor/include-level.c": 0 "preprocessor/include-level.h": 1 * check-output-end */ sparse-0.6.4/validation/preprocessor/include-level.h000066400000000000000000000000341411531012200225620ustar00rootroot00000000000000__FILE__: __INCLUDE_LEVEL__ sparse-0.6.4/validation/preprocessor/missing-delim.c000066400000000000000000000005751411531012200226000ustar00rootroot00000000000000static int c = 'a; static char s[] = "abc; static char t[] = "xyz"; extern void foo(void); /* * check-name: missing-delim * check-command: sparse -E $file * check-output-ignore * * check-error-start preprocessor/missing-delim.c:2:0: warning: missing terminating ' character preprocessor/missing-delim.c:4:0: warning: missing terminating " character * check-error-end */ sparse-0.6.4/validation/preprocessor/phase2-backslash.c000066400000000000000000000030301411531012200231370ustar00rootroot00000000000000/* * '\\' 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. */ /* * check-name: phase2-backslash * check-command: sparse -E $file * * check-output-start "\a" 1 D '\a' * check-output-end * * check-error-start preprocessor/phase2-backslash.c:68:0: warning: backslash-newline at end of file * check-error-end */ #define A(x) #x #define B(x) A(x) /* This should result in "\a" */ 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 */ D #define E '\\ a' /* This should give '\a' - with no warnings issued */ E /* This should give nothing */ // junk \ more junk /* This should also give nothing */ /\ * comment *\ / /* And this should complain since final newline should not be eaten by '\\' */ \ sparse-0.6.4/validation/preprocessor/phase3-comments.c000066400000000000000000000003671411531012200230440ustar00rootroot00000000000000/* * Each comment should be treated as if it had been a single space. */ /* This should give nothing */ #define X /* */ Y /* * check-name: phase3-comments * check-command: sparse -E $file * * check-output-start * check-output-end */ sparse-0.6.4/validation/preprocessor/predef-llp64.c000066400000000000000000000003061411531012200222330ustar00rootroot00000000000000#include "predef.c" /* * check-name: predefined macros for LLP64 * check-command: test-linearize -Wno-decl -msize-llp64 $file * check-output-ignore * * check-output-contains: ret\\..*\\$0 */ sparse-0.6.4/validation/preprocessor/predef-lp32.c000066400000000000000000000002751411531012200220570ustar00rootroot00000000000000#include "predef.c" /* * check-name: predefined macros for LP32 * check-command: test-linearize -Wno-decl -m32 $file * check-output-ignore * * check-output-contains: ret\\..*\\$0 */ sparse-0.6.4/validation/preprocessor/predef-lp64.c000066400000000000000000000002751411531012200220640ustar00rootroot00000000000000#include "predef.c" /* * check-name: predefined macros for LP64 * check-command: test-linearize -Wno-decl -m64 $file * check-output-ignore * * check-output-contains: ret\\..*\\$0 */ sparse-0.6.4/validation/preprocessor/predef-token.c000066400000000000000000000000761411531012200224160ustar00rootroot00000000000000static __UINT8_TYPE__ u8; /* * check-name: predef-token */ sparse-0.6.4/validation/preprocessor/predef-unsigned.c000066400000000000000000000003231411531012200231050ustar00rootroot00000000000000#include "predef.c" /* * check-name: predefined macros for -funsigned-char * check-command: test-linearize -Wno-decl -funsigned-char $file * check-output-ignore * * check-output-contains: ret\\..*\\$0 */ sparse-0.6.4/validation/preprocessor/predef.c000066400000000000000000000032421411531012200212760ustar00rootroot00000000000000#define BITS(T) (sizeof(T) * 8) #define SIGN_BIT(T) (1ULL << (BITS(T) - 1)) #define SMASK(T) (SIGN_BIT(T) - 1) #define UMASK(T) (SIGN_BIT(T) | SMASK(T)) int test(void); int test(void) { #define TEST_BIT(X, T) if (__ ## X ## _BIT__ != BITS(T)) return 1 TEST_BIT(CHAR, char); #define TEST_MAX(X, M) if (__ ## X ## _MAX__ != M) return 1 #define TEST_SMAX(X, T) TEST_MAX(X, SMASK(T)) #define TEST_UMAX(X, T) TEST_MAX(X, UMASK(T)) TEST_SMAX(SCHAR, signed char); TEST_SMAX(SHRT, short); TEST_SMAX(INT, int); TEST_SMAX(LONG, long); TEST_SMAX(LONG_LONG, long long); TEST_MAX( INT8, 0x7f); TEST_MAX(UINT8, 0xffU); TEST_MAX( INT16, 0x7fff); TEST_MAX(UINT16, 0xffffU); TEST_MAX( INT32, 0x7fffffff); TEST_MAX(UINT32, 0xffffffffU); TEST_MAX( INT64, 0x7fffffffffffffffLL); TEST_MAX(UINT64, 0xffffffffffffffffULL); TEST_SMAX(INTMAX, __INTMAX_TYPE__); TEST_UMAX(UINTMAX, __UINTMAX_TYPE__); TEST_SMAX(INTPTR, __INTPTR_TYPE__); TEST_UMAX(UINTPTR, __UINTPTR_TYPE__); TEST_SMAX(PTRDIFF, __PTRDIFF_TYPE__); TEST_UMAX(SIZE, __SIZE_TYPE__); #define TEST_SIZEOF(X, T) if (__SIZEOF_ ## X ## __ != sizeof(T)) return 1 TEST_SIZEOF(SHORT, short); TEST_SIZEOF(INT, int); TEST_SIZEOF(LONG, long); TEST_SIZEOF(LONG_LONG, long long); #ifdef __SIZEOF_INT128__ TEST_SIZEOF(INT128, __int128); #endif TEST_SIZEOF(PTRDIFF_T, __PTRDIFF_TYPE__); TEST_SIZEOF(SIZE_T, __SIZE_TYPE__); TEST_SIZEOF(POINTER, void*); TEST_SIZEOF(FLOAT, float); TEST_SIZEOF(DOUBLE, double); TEST_SIZEOF(LONG_DOUBLE, long double); return 0; } /* * check-name: predefined macros: __SIZEOF___, ... * check-command: test-linearize -Wno-decl $file * check-output-ignore * * check-output-contains: ret\\..*\\$0 */ sparse-0.6.4/validation/preprocessor/preprocessor1.c000066400000000000000000000003601411531012200226360ustar00rootroot00000000000000#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.6.4/validation/preprocessor/preprocessor10.c000066400000000000000000000005111411531012200227140ustar00rootroot00000000000000/* 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.6.4/validation/preprocessor/preprocessor11.c000066400000000000000000000020451411531012200227210ustar00rootroot00000000000000#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.6.4/validation/preprocessor/preprocessor12.c000066400000000000000000000003161411531012200227210ustar00rootroot00000000000000/* * 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.6.4/validation/preprocessor/preprocessor13.c000066400000000000000000000006741411531012200227310ustar00rootroot00000000000000/* * 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.6.4/validation/preprocessor/preprocessor14.c000066400000000000000000000003701411531012200227230ustar00rootroot00000000000000/* * 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.6.4/validation/preprocessor/preprocessor15.c000066400000000000000000000003341411531012200227240ustar00rootroot00000000000000#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.6.4/validation/preprocessor/preprocessor16.c000066400000000000000000000014711411531012200227300ustar00rootroot00000000000000#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.6.4/validation/preprocessor/preprocessor17.c000066400000000000000000000002771411531012200227340ustar00rootroot00000000000000#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.6.4/validation/preprocessor/preprocessor18.c000066400000000000000000000005571411531012200227360ustar00rootroot00000000000000/* 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.6.4/validation/preprocessor/preprocessor19.c000066400000000000000000000006711411531012200227340ustar00rootroot00000000000000/* 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.6.4/validation/preprocessor/preprocessor2.c000066400000000000000000000003201411531012200226330ustar00rootroot00000000000000#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.6.4/validation/preprocessor/preprocessor20.c000066400000000000000000000003071411531012200227200ustar00rootroot00000000000000#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.6.4/validation/preprocessor/preprocessor20.h000066400000000000000000000000451411531012200227240ustar00rootroot00000000000000#ifdef X B #endif #ifndef Y A #endif sparse-0.6.4/validation/preprocessor/preprocessor21.c000066400000000000000000000004551411531012200227250ustar00rootroot00000000000000#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.6.4/validation/preprocessor/preprocessor22.c000066400000000000000000000015261411531012200227260ustar00rootroot00000000000000#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 -pedantic -E $file * * check-error-start preprocessor/preprocessor22.c:6:1: warning: directive in macro's argument list preprocessor/preprocessor22.c:8:1: warning: directive in macro's argument list preprocessor/preprocessor22.c:10:1: warning: directive in macro's argument list preprocessor/preprocessor22.c:12:1: warning: directive in macro's argument list * check-error-end * * check-output-start struct { int b; } a;; * check-output-end */ sparse-0.6.4/validation/preprocessor/preprocessor23.c000066400000000000000000000025531411531012200227300ustar00rootroot00000000000000#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) #define J(...) ,##__VA_ARGS__ J() J(x) /* * check-name: Preprocessor #23 * check-command: sparse -E $file * * check-output-start , ,x ,, ,x, ,x,x ,xx,x ,x , ,x ,x ,xx ,x * 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.6.4/validation/preprocessor/preprocessor3.c000066400000000000000000000011461411531012200226430ustar00rootroot00000000000000/* * 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.6.4/validation/preprocessor/preprocessor4.c000066400000000000000000000003641411531012200226450ustar00rootroot00000000000000#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.6.4/validation/preprocessor/preprocessor5.c000066400000000000000000000003241411531012200226420ustar00rootroot00000000000000#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.6.4/validation/preprocessor/preprocessor6.c000066400000000000000000000016311411531012200226450ustar00rootroot00000000000000/* 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.6.4/validation/preprocessor/preprocessor7.c000066400000000000000000000003141411531012200226430ustar00rootroot00000000000000#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.6.4/validation/preprocessor/preprocessor8.c000066400000000000000000000016451411531012200226540ustar00rootroot00000000000000#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.6.4/validation/preprocessor/preprocessor9.c000066400000000000000000000004331411531012200226470ustar00rootroot00000000000000/* 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.6.4/validation/preprocessor/stringify.c000066400000000000000000000004471411531012200220530ustar00rootroot00000000000000#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.6.4/validation/preprocessor/wide.c000066400000000000000000000003431411531012200207600ustar00rootroot00000000000000#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.6.4/validation/prototype.c000066400000000000000000000001771411531012200173540ustar00rootroot00000000000000static int prototype(void); /* * check-name: Compile skip function prototype * check-command: sparsec -c $file -o tmp.o */ sparse-0.6.4/validation/ptr-inherit.c000066400000000000000000000037021411531012200175510ustar00rootroot00000000000000#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.6.4/validation/ptr-sub-blows.c000066400000000000000000000006371411531012200200300ustar00rootroot00000000000000static int ok(int *a, int *b) { return a - b; } struct s { int a, b, c; }; static int ko(struct s *a, struct s *b) { return a - b; } /* * check-name: ptr-sub-blows * check-command: sparse -Wptr-subtraction-blows $file * * check-error-start ptr-sub-blows.c:12:18: warning: potentially expensive pointer subtraction ptr-sub-blows.c:12:18: 'struct s' has a non-power-of-2 size: 12 * check-error-end */ sparse-0.6.4/validation/pure-function.c000066400000000000000000000005171411531012200201030ustar00rootroot00000000000000 static __attribute__((__pure__)) int pure_int(int v) { int i = v; return i; } static __attribute__((__pure__)) void *pure_ptr(void *p) { void *i = p; return i; } static void foo(int v, void *p) { int val = pure_int(v); void *ptr = pure_ptr(p); (void)val; (void)ptr; } /* * check-name: Pure function attribute */ sparse-0.6.4/validation/range-syntax.c000066400000000000000000000006751411531012200177320ustar00rootroot00000000000000 static void ok(int a, int b, int c) { __range__(a, 0, 8); __range__(a, b, c); } static void ko(int a, int b, int c) { __range__ a, 0, 8; __range__ a, b, c; } /* * check-name: range syntax * * check-error-start range-syntax.c:10:19: error: Expected ( after __range__ statement range-syntax.c:10:19: error: got a range-syntax.c:11:19: error: Expected ( after __range__ statement range-syntax.c:11:19: error: got a * check-error-end */ sparse-0.6.4/validation/repeat.h000066400000000000000000000027751411531012200166020ustar00rootroot00000000000000#define R0(P, S) P(S) #define R1(P, S) R0(P,S##0) R0(P,S##1) #define R2(P, S) R0(P,S##0) R0(P,S##1) R0(P,S##2) R0(P,S##3) #define R3(P, S) R0(P,S##0) R0(P,S##1) R0(P,S##2) R0(P,S##3) R0(P,S##4) R0(P,S##5) R0(P,S##6) R0(P,S##7) #define R4(P, S) R3(P,S##0) R3(P,S##1) #define R5(P, S) R3(P,S##0) R3(P,S##1) R3(P,S##2) R3(P,S##3) #define R6(P, S) R3(P,S##0) R3(P,S##1) R3(P,S##2) R3(P,S##3) R3(P,S##4) R3(P,S##5) R3(P,S##6) R3(P,S##7) #define R7(P, S) R6(P,S##0) R6(P,S##1) #define R8(P, S) R6(P,S##0) R6(P,S##1) R6(P,S##2) R6(P,S##3) #define R9(P, S) R6(P,S##0) R6(P,S##1) R6(P,S##2) R6(P,S##3) R6(P,S##4) R6(P,S##5) R6(P,S##6) R6(P,S##7) #define R10(P, S) R9(P,S##0) R9(P,S##1) #define R11(P, S) R9(P,S##0) R9(P,S##1) R9(P,S##2) R9(P,S##3) #define R12(P, S) R9(P,S##0) R9(P,S##1) R9(P,S##2) R9(P,S##3) R9(P,S##4) R9(P,S##5) R9(P,S##6) R9(P,S##7) #define R13(P, S) R12(P,S##0) R12(P,S##1) #define R14(P, S) R12(P,S##0) R12(P,S##1) R12(P,S##2) R12(P,S##3) #define R15(P, S) R12(P,S##0) R12(P,S##1) R12(P,S##2) R12(P,S##3) R12(P,S##4) R12(P,S##5) R12(P,S##6) R12(P,S##7) #define R16(P, S) R15(P,S##0) R15(P,S##1) #define R17(P, S) R15(P,S##0) R15(P,S##1) R15(P,S##2) R15(P,S##3) #define R18(P, S) R15(P,S##0) R15(P,S##1) R15(P,S##2) R15(P,S##3) R15(P,S##4) R15(P,S##5) R15(P,S##6) R15(P,S##7) #define R19(P, S) R18(P,S##0) R18(P,S##1) #define R20(P, S) R18(P,S##0) R18(P,S##1) R18(P,S##2) R18(P,S##3) #define REPEAT_(RN, P) RN(P,) #define REPEAT2(N, P) REPEAT_(R##N,P) sparse-0.6.4/validation/reserved.c000066400000000000000000000164231411531012200171270ustar00rootroot00000000000000static 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.6.4/validation/restrict-array.c000066400000000000000000000017231411531012200202600ustar00rootroot00000000000000#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.6.4/validation/restrict.c000066400000000000000000000061061411531012200171440ustar00rootroot00000000000000void f00(void *restrict dst); void f01(void *restrict *dst); void f02(void *restrict *dst); void f03(void *restrict *dst); void *restrict rp; void * up; void f00(void *dst) { } /* check-should-pass */ void f01(typeof(&rp) dst) { } /* check-should-pass */ void f02(void **dst) { } /* check-should-fail */ void f03(typeof(&up) dst) { } /* check-should-fail */ void foo(void) { rp = up; /* check-should-pass */ up = rp; /* check-should-pass */ } void ref(void) { void *const qp; void * up; extern void *const *pqp; extern void **pup; pqp = &qp; /* check-should-pass */ pqp = &up; /* check-should-pass */ pqp = pup; pup = &up; /* check-should-pass */ pup = &qp; /* check-should-fail */ pup = pqp; /* check-should-fail */ } void bar(void) { extern void *restrict *prp; extern void **pup; prp = &rp; /* check-should-pass */ prp = &up; /* check-should-pass */ prp = pup; pup = &up; /* check-should-pass */ pup = &rp; /* check-should-fail */ pup = prp; /* check-should-fail */ } void baz(void) { extern typeof(&rp) prp; extern typeof(&up) pup; prp = &rp; /* check-should-pass */ prp = &up; /* check-should-pass */ prp = pup; pup = &up; /* check-should-pass */ pup = &rp; /* check-should-fail */ pup = prp; /* check-should-fail */ } /* * check-name: restrict qualifier * check-command: sparse -Wno-decl $file * * check-error-start restrict.c:11:6: error: symbol 'f02' redeclared with different type (incompatible argument 1 (different modifiers)): restrict.c:11:6: void extern [addressable] [toplevel] f02( ... ) restrict.c:3:6: note: previously declared as: restrict.c:3:6: void extern [addressable] [toplevel] f02( ... ) restrict.c:12:6: error: symbol 'f03' redeclared with different type (incompatible argument 1 (different modifiers)): restrict.c:12:6: void extern [addressable] [toplevel] f03( ... ) restrict.c:4:6: note: previously declared as: restrict.c:4:6: void extern [addressable] [toplevel] f03( ... ) restrict.c:33:13: warning: incorrect type in assignment (different modifiers) restrict.c:33:13: expected void **extern [assigned] pup restrict.c:33:13: got void *const * restrict.c:34:13: warning: incorrect type in assignment (different modifiers) restrict.c:34:13: expected void **extern [assigned] pup restrict.c:34:13: got void *const *extern [assigned] pqp restrict.c:48:13: warning: incorrect type in assignment (different modifiers) restrict.c:48:13: expected void **extern [assigned] pup restrict.c:48:13: got void *restrict * restrict.c:49:13: warning: incorrect type in assignment (different modifiers) restrict.c:49:13: expected void **extern [assigned] pup restrict.c:49:13: got void *restrict *extern [assigned] prp restrict.c:63:13: warning: incorrect type in assignment (different modifiers) restrict.c:63:13: expected void **extern [assigned] pup restrict.c:63:13: got void *restrict * restrict.c:64:13: warning: incorrect type in assignment (different modifiers) restrict.c:64:13: expected void **extern [assigned] pup restrict.c:64:13: got void *restrict *extern [assigned] prp * check-error-end */ sparse-0.6.4/validation/restricted-typeof.c000066400000000000000000000002741411531012200207610ustar00rootroot00000000000000typedef 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.6.4/validation/scheck/000077500000000000000000000000001411531012200163765ustar00rootroot00000000000000sparse-0.6.4/validation/scheck/ko.c000066400000000000000000000002161411531012200171520ustar00rootroot00000000000000static void ko(int x) { __assert((~x) == (~0 + x)); } /* * check-name: scheck-ko * check-command: scheck $file * check-known-to-fail */ sparse-0.6.4/validation/scheck/ok.c000066400000000000000000000006051411531012200171540ustar00rootroot00000000000000static void ok(int x) { __assert((~x) == (~0 - x)); // true but not simplified yet __assert_eq(~x, ~0 - x); __assert_const(x & 0, 0); } static void always(int x) { __assert((x - x) == 0); // true and simplified } static void assumed(int x, int a, int b) { __assume((a & ~b) == 0); __assert_eq((x ^ a) | b, x | b); } /* * check-name: scheck-ok * check-command: scheck $file */ sparse-0.6.4/validation/self-quote-args.c000066400000000000000000000003451411531012200203220ustar00rootroot00000000000000/* * check-name: self-quote-args * check-description: This is testing that the test-suite * respect the quoting of the command's arguments. * check-command: sparse '-foption with-spaces' empty-file * check-output-ignore */ sparse-0.6.4/validation/shift-negative.c000066400000000000000000000011331411531012200202150ustar00rootroot00000000000000unsigned int fn1(unsigned int a) { return a >> -1; } unsigned int fn2(unsigned int a) { return a >> ~0; } unsigned int fo1(unsigned int a) { return a >> ((a & 0) | -1); } unsigned int fo2(unsigned int a) { return a >> ((a & 0) ^ ~0); } /* * check-name: shift-negative * check-command: sparse -Wno-decl $file * * check-error-start shift-negative.c:1:48: warning: shift count is negative (-1) shift-negative.c:2:48: warning: shift count is negative (-1) shift-negative.c:4:59: warning: shift count is negative (-1) shift-negative.c:5:59: warning: shift count is negative (-1) * check-error-end */ sparse-0.6.4/validation/shift-undef-long.c000066400000000000000000000010751411531012200204560ustar00rootroot00000000000000static unsigned very_big_shift(unsigned int a) { unsigned r = 0; r |= a << (0ULL ^ ~0U); r |= a << ((( signed long long) ~0U) + 1); r |= a << (((unsigned long long) ~0U) + 1); r |= a << (~((unsigned long long) ~0U)); return r; } /* * check-name: shift-undef-long * check-command: sparse -m64 $file * * check-error-start shift-undef-long.c:4:25: warning: shift count is negative (-1) shift-undef-long.c:5:47: warning: shift too big (4294967296) for type unsigned int shift-undef-long.c:7:20: warning: shift count is negative (-4294967296) * check-error-end */ sparse-0.6.4/validation/shift-undef.c000066400000000000000000000102311411531012200175130ustar00rootroot00000000000000int simple(int s, unsigned int u, int p) { s = s >> 100; u = u >> 101; u = u << 102; s = s >> -1; u = u >> -2; u = u << -3; if (0) return s >> 103; if (0) return u >> 104; if (0) return u << 105; if (0) return s >> -4; if (0) return u >> -5; if (0) return u << -6; if (p && 0) return s >> 106; if (p && 0) return u >> 107; if (p && 0) return u << 108; if (p && 0) return s >> -7; if (p && 0) return u >> -8; if (p && 0) return u << -9; s = s >> ((p & 0) + 109); u ^= p; // reloaded because now == 0 u = u >> ((p & 0) + 110); u ^= p; // reloaded because now == 0 u = u << ((p & 0) + 111); u ^= p; // reloaded because now == 0 s = s >> ((p & 0) + -10); u = u >> ((p & 0) + -11); u ^= p; // reloaded because now == 0 u = u << ((p & 0) + -12); u ^= p; // reloaded because now == 0 return s + u; } int compound(int s, unsigned int u, int p) { s >>= 100; u >>= 101; u <<= 102; s >>= -1; u >>= -2; u <<= -3; if (0) return s >>= 103; if (0) return u >>= 104; if (0) return u <<= 105; if (0) return s >>= -4; if (0) return u >>= -5; if (0) return u <<= -6; if (p && 0) return s >>= 106; if (p && 0) return u >>= 107; if (p && 0) return u <<= 108; if (p && 0) return s >>= -7; if (p && 0) return u >>= -8; if (p && 0) return u <<= -9; s >>= ((p & 0) + 109); u ^= p; // reloaded because now == 0 u >>= ((p & 0) + 110); u ^= p; // reloaded because now == 0 u <<= ((p & 0) + 111); u ^= p; // reloaded because now == 0 s >>= ((p & 0) + -10); u >>= ((p & 0) + -11); u ^= p; // reloaded because now == 0 u <<= ((p & 0) + -12); u ^= p; // reloaded because now == 0 return s + u; } int ok(int s, unsigned int u, int p) { // GCC doesn't warn on these if (0 && (s >> 100)) return 0; if (0 && (u >> 101)) return 0; if (0 && (u << 102)) return 0; if (0 && (s >> -1)) return 0; if (0 && (u >> -2)) return 0; if (0 && (u << -3)) return 0; if (0 && (s >>= 103)) return 0; if (0 && (u >>= 104)) return 0; if (0 && (u <<= 105)) return 0; if (0 && (s >>= -4)) return 0; if (0 && (u >>= -5)) return 0; if (0 && (u <<= -6)) return 0; return 1; } struct bf { unsigned int u:8; int s:8; }; int bf(struct bf *p) { unsigned int r = 0; r += p->s << 8; r += p->s >> 8; r += p->u >> 8; return r; } /* * The following is used in the kernel at several places * It shouldn't emit any warnings. */ typedef unsigned long long u64; typedef unsigned int u32; extern void hw_w32x2(u32 hi, u32 lo); inline void hw_w64(u64 val) { hw_w32x2(val >> 32, (u32) val); } void hw_write(u32 val) { hw_w64(val); } /* * check-name: shift too big or negative * check-command: sparse -Wno-decl $file * * check-error-start shift-undef.c:3:18: warning: shift too big (100) for type int shift-undef.c:4:18: warning: shift too big (101) for type unsigned int shift-undef.c:5:18: warning: shift too big (102) for type unsigned int shift-undef.c:6:19: warning: shift count is negative (-1) shift-undef.c:7:19: warning: shift count is negative (-2) shift-undef.c:8:19: warning: shift count is negative (-3) shift-undef.c:21:29: warning: shift too big (109) for type int shift-undef.c:22:29: warning: shift too big (110) for type unsigned int shift-undef.c:23:29: warning: shift too big (111) for type unsigned int shift-undef.c:24:29: warning: shift count is negative (-10) shift-undef.c:25:29: warning: shift count is negative (-11) shift-undef.c:26:29: warning: shift count is negative (-12) shift-undef.c:32:15: warning: shift too big (100) for type int shift-undef.c:33:15: warning: shift too big (101) for type unsigned int shift-undef.c:34:15: warning: shift too big (102) for type unsigned int shift-undef.c:35:16: warning: shift count is negative (-1) shift-undef.c:36:16: warning: shift count is negative (-2) shift-undef.c:37:16: warning: shift count is negative (-3) shift-undef.c:50:26: warning: shift too big (109) for type int shift-undef.c:51:26: warning: shift too big (110) for type unsigned int shift-undef.c:52:26: warning: shift too big (111) for type unsigned int shift-undef.c:53:26: warning: shift count is negative (-10) shift-undef.c:54:26: warning: shift count is negative (-11) shift-undef.c:55:26: warning: shift count is negative (-12) * check-error-end */ sparse-0.6.4/validation/sizeof-bool.c000066400000000000000000000005141411531012200175320ustar00rootroot00000000000000static 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.6.4/validation/sizeof-builtin.c000066400000000000000000000004441411531012200202470ustar00rootroot00000000000000int test(void); int test(void) { return sizeof &__builtin_trap; } /* * check-name: sizeof-builtin * check-command: sparse -Wno-decl $file * check-known-to-fail * * check-error-start sizeof-function.c:4:16: error: expression using addressof on a builtin function * check-error-end */ sparse-0.6.4/validation/sizeof-compound-postfix.c000066400000000000000000000002261411531012200221150ustar00rootroot00000000000000struct 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.6.4/validation/sizeof-function.c000066400000000000000000000020451411531012200204250ustar00rootroot00000000000000extern int fun(void); extern int (*ptr)(void); static inline int inl(int *a) { return *a + 1; } int test(void); int test(void) { unsigned int s = 0; // OK s += sizeof &fun; s += sizeof ptr; s += sizeof &ptr; s += sizeof &inl; // KO s += sizeof fun; s += sizeof *fun; s += sizeof *ptr; s += sizeof inl; s += sizeof *inl; s += sizeof __builtin_trap; s += sizeof *__builtin_trap; return s; } /* * check-name: sizeof-function * check-command: sparse -Wpointer-arith -Wno-decl $file * * check-error-start sizeof-function.c:22:14: warning: expression using sizeof on a function sizeof-function.c:23:14: warning: expression using sizeof on a function sizeof-function.c:25:14: warning: expression using sizeof on a function sizeof-function.c:27:14: warning: expression using sizeof on a function sizeof-function.c:28:14: warning: expression using sizeof on a function sizeof-function.c:30:14: warning: expression using sizeof on a function sizeof-function.c:31:14: warning: expression using sizeof on a function * check-error-end */ sparse-0.6.4/validation/sizeof-incomplete-type.c000066400000000000000000000014551411531012200217220ustar00rootroot00000000000000struct s { char a; char b[sizeof(struct s)]; char c; char d[sizeof(struct s)]; int j:sizeof(struct s); }; static int array[] = { [0] = 0, [sizeof(array)] = 1, [2] = 0, [sizeof(array)] = 2, }; /* * check-name: sizeof incomplete type * * check-known-to-fail * check-error-start sizeof-incomplete-type.c:3:16: error: invalid application of 'sizeof' to incomplete type 'struct s' sizeof-incomplete-type.c:5:16: error: invalid application of 'sizeof' to incomplete type 'struct s' sizeof-incomplete-type.c:6:16: error: invalid application of 'sizeof' to incomplete type 'struct s' sizeof-incomplete-type.c:11:17: error: invalid application of 'sizeof' to incomplete type 'int[]' sizeof-incomplete-type.c:13:17: error: invalid application of 'sizeof' to incomplete type 'int[]' * check-error-end */ sparse-0.6.4/validation/sizeof-void.c000066400000000000000000000022571411531012200175460ustar00rootroot00000000000000#define is_constexpr(x) \ (sizeof(int) == sizeof(*(8 ? ((void *)((long)(x) * 0l)) : (int *)8))) static int test(void) { unsigned int s = 0, i = 0; void *ptr = &i; // OK s += sizeof i; s += sizeof &i; s += sizeof ptr; s += sizeof &ptr; // KO s += sizeof(void); s += sizeof *ptr; s += is_constexpr(ptr++); s += is_constexpr((i++, 1)); s += is_constexpr(sizeof *ptr); s += is_constexpr(ptr + 1); s += is_constexpr(&ptr + 1); s += is_constexpr(*(((char *)&ptr) + 1)); return s; } /* * check-name: sizeof(void) is valid * check-description: sizeof(void) was rejected because void is an incomplete * type * check-command: sparse -Wpointer-arith $file * * check-error-start sizeof-void.c:16:14: warning: expression using sizeof(void) sizeof-void.c:17:14: warning: expression using sizeof(void) sizeof-void.c:18:14: warning: expression using sizeof(void) sizeof-void.c:19:14: warning: expression using sizeof(void) sizeof-void.c:20:14: warning: expression using sizeof(void) sizeof-void.c:21:14: warning: expression using sizeof(void) sizeof-void.c:22:14: warning: expression using sizeof(void) sizeof-void.c:23:14: warning: expression using sizeof(void) * check-error-end */ sparse-0.6.4/validation/specifiers1.c000066400000000000000000000032131411531012200175160ustar00rootroot00000000000000static 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.6.4/validation/specifiers2.c000066400000000000000000000151561411531012200175300ustar00rootroot00000000000000typedef 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.6.4/validation/static-forward-decl.c000066400000000000000000000002641411531012200211420ustar00rootroot00000000000000int fref(void); int fref(void) { return 0; } static int floc(void); int floc(void) { return 0; } static int oloc; int oloc = 0; /* * check-name: static forward declaration */ sparse-0.6.4/validation/static_assert.c000066400000000000000000000035041411531012200201540ustar00rootroot00000000000000_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(,); // C2x's version: without message _Static_assert(1); _Static_assert(0); /* * 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: string literal expected for _Static_assert() static_assert.c:37:18: error: bad constant expression static_assert.c:52:19: error: string literal expected for _Static_assert() static_assert.c:53:16: error: Expected constant expression static_assert.c:54:16: error: Expected constant expression static_assert.c:54:17: error: string literal expected for _Static_assert() static_assert.c:58:16: error: static assertion failed * check-error-end */ sparse-0.6.4/validation/storage-struct-member.c000066400000000000000000000006011411531012200215320ustar00rootroot00000000000000int foo(a) register int a; { return a; } struct s { register int a; }; /* * check-name: storage in struct member * check-command: sparse -Wno-decl $file * * check-known-to-fail * check-error-start storage-struct-member.c:2:9: warning: non-ANSI definition of function 'foo' storage-struct-member.c:8:9: error: storage specifier in structure definition' * check-error-end */ sparse-0.6.4/validation/strict-prototypes0.c000066400000000000000000000002421411531012200211160ustar00rootroot00000000000000extern void func1(); extern void myfunction(), myfunc2(); /* * check-name: strict-prototypes disabled * check-command: sparse -Wno-strict-prototypes $file */ sparse-0.6.4/validation/strict-prototypes1.c000066400000000000000000000007331411531012200211240ustar00rootroot00000000000000extern void func0(); extern void func1(), func2(); /* * check-name: strict-prototypes enabled * check-command: sparse -Wstrict-prototypes $file * check-known-to-fail * * check-error-start strict-prototypes1.c:1:18: warning: non-ANSI function declaration of function 'func0' strict-prototypes1.c:2:18: warning: non-ANSI function declaration of function 'func1' strict-prototypes1.c:2:27: warning: non-ANSI function declaration of function 'func2' * check-error-end */ sparse-0.6.4/validation/struct-as.c000066400000000000000000000004711411531012200172310ustar00rootroot00000000000000/* * 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) { return test(&sp->a); } /* * check-name: Address space of a struct member */ sparse-0.6.4/validation/struct-attribute-placement.c000066400000000000000000000001551411531012200225760ustar00rootroot00000000000000struct __attribute__((__aligned__(16))) foo { int a; }; /* * check-name: struct attribute placement */ sparse-0.6.4/validation/struct-ns1.c000066400000000000000000000007171411531012200173320ustar00rootroot00000000000000// 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.6.4/validation/struct-ns2.c000066400000000000000000000007311411531012200173270ustar00rootroot00000000000000static 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.6.4/validation/struct-size1.c000066400000000000000000000004551411531012200176630ustar00rootroot00000000000000struct 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.6.4/validation/switch-long.c000066400000000000000000000011221411531012200175340ustar00rootroot00000000000000void def(void); void r0(void); void r1(void); void sw_long(long long a) { switch (a) { case 0: return r0(); case 1LL << 00: return r1(); case 1LL << 32: return r1(); } return def(); } /* * check-name: switch-long * check-command: test-linearize -Wno-decl $file * * check-output-start sw_long: .L0: switch.64 %arg1, 0 -> .L2, 1 -> .L3, 4294967296 -> .L4, default -> .L1 .L2: call r0 br .L5 .L3: call r1 br .L5 .L4: call r1 br .L5 .L1: call def br .L5 .L5: ret * check-output-end */ sparse-0.6.4/validation/tautological-compare.c000066400000000000000000000030071411531012200214150ustar00rootroot00000000000000typedef 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.6.4/validation/test-suite000077500000000000000000000356541411531012200172070ustar00rootroot00000000000000#!/bin/sh #set -x cd $(dirname "$0") default_path=".." default_cmd="sparse \$file" default_args="$SPARSE_TEST_ARGS" tests_list="" prog_name=`basename $0` if [ ! -x "$default_path/sparse-llvm" ]; then disabled_cmds="sparsec sparsei sparse-llvm sparse-llvm-dis" fi if [ ! -x "$default_path/scheck" ]; then disabled_cmds="$disabled_cmds scheck" 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 vquiet="" quiet=0 abort=0 ## # verbose(string) - prints string if we are in verbose mode verbose() { [ "$V" -eq "1" ] && echo " $1" return 0 } ## # warning(string) - prints a warning warning() { [ "$quiet" -ne 1 ] && echo "warning: $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 } ## # 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 check_output_match=0 check_output_returns=0 check_arch_ignore="" check_arch_only="" check_assert="" check_cpp_if="" 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 ;; check-output-match) check_output_match=1 ;; check-output-returns:) check_output_returns=1 ;; check-arch-ignore:) arch=$(uname -m) check_arch_ignore="$val" ;; check-arch-only:) arch=$(uname -m) check_arch_only="$val" ;; check-assert:) check_assert="$val" ;; check-cpp-if:) check_cpp_if="$val" ;; check-description:) ;; # ignore check-note:) ;; # ignore check-warning:) ;; # ignore check-error-start) ;; # ignore check-error-end) ;; # ignore check-output-start) ;; # ignore check-output-end) ;; # ignore check-should-pass) ;; # ignore, unused annotation check-should-fail) ;; # ignore, unused annotation check-should-warn) ;; # ignore, unused annotation check-*) error "$1: unknown tag '$tag'" 1 ;; esac done << EOT $lines EOT } ## # helper for has_(each|none)_patterns() has_patterns() { ifile="$1" patt="$2" ofile="$3" cmp="$4" msg="$5" grep "$patt:" "$ifile" | \ sed -e "s/^.*$patt: *\(.*\)$/\1/" | \ while read val; do grep -s -q "$val" "$ofile" if [ "$?" $cmp 0 ]; then error " Pattern '$val' unexpectedly $msg" 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" "$4" -ne "$3" } ## # 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" "$4" -eq "$3" } ## # minmax_patterns(ifile tag ofile) - does ofile contains the # the patterns given by ifile's tags # the right number of time? minmax_patterns() { ifile="$1" patt="$2" ofile="$3" grep "$patt([0-9-]*\(, *\)*[0-9-]*):" "$ifile" | \ sed -e "s/^.*$patt(\([0-9]*\)): *\(.*\)/\1 eq \2/" \ -e "s/^.*$patt(\([0-9-]*\), *\([0-9-]*\)): *\(.*\)/\1 \2 \3/" | \ while read min max pat; do n=$(grep -s "$pat" "$ofile" | wc -l) if [ "$max" = "eq" ]; then if [ "$n" -ne "$min" ]; then error " Pattern '$pat' expected $min times but got $n times" return 1 fi continue fi if [ "$min" != '-' ]; then if [ "$n" -lt "$min" ]; then error " Pattern '$pat' expected min $min times but got $n times" return 1 fi fi if [ "$max" != '-' ]; then if [ "$n" -gt "$max" ]; then error " Pattern '$pat' expected max $max times but got $n times" return 1 fi fi done return $? } ## match_patterns() { ifile="$1" patt="$2" ofile="$3" grep "$patt" "$ifile" | sed -e "s/^.*$patt(\(.*\)): *\(.*\)$/\1 \2/" | \ while read ins pat; do grep -s "^ $ins\\.*[0-9]* " "$ofile" | grep -v -s -q "$pat" if [ "$?" -ne 1 ]; then error " IR doesn't match '$pat'" return 1 fi done return $? } ## return_patterns() { ifile="$1" patt="$2" ofile="$3" grep "$patt:" "$ifile" | sed -e "s/^.*$patt: *\(.*\)$/\1/" | \ while read ret; do grep -s "^ ret\\.[0-9]" "$ofile" | grep -v -s -q "[ \$]${ret}\$" if [ "$?" -ne 1 ]; then error " Return doesn't match '$ret'" return 1 fi done return $? } ## # 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 } ## do_usage() { echo "$prog_name - a tiny automatic testing script" echo "Usage: $prog_name [option(s)] [command] [arguments]" echo echo "options:" echo " -a|--abort Abort the tests as soon as one fails." echo " -q|--quiet Be extra quiet while running the tests." echo " --args='...' Add these options to the test command." echo echo "commands:" echo " [file ...] Runs the test suite on the given file(s)." echo " If a directory is given, run only those files." echo " If no file is given, run the whole testsuite." echo " single file Run the test in 'file'." echo " format file [name [cmd]] Help writing a new test case using cmd." echo echo " [command] help Print usage." } disable() { disabled_tests=$(($disabled_tests + 1)) if [ -z "$vquiet" ]; then echo " SKIP $1 ($2)" fi } ## # 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" quiet=0 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 warning "$file: test 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 disable "$test_name" "$file" return 3 fi done if [ "$check_arch_ignore" != "" ]; then if echo $arch | egrep -q -w "$check_arch_ignore"; then disable "$test_name" "$file" return 3 fi fi if [ "$check_arch_only" != "" ]; then if ! (echo $arch | egrep -q -w "$check_arch_only"); then disable "$test_name" "$file" return 3 fi fi if [ "$check_assert" != "" ]; then res=$(../sparse - 2>&1 >/dev/null <<- EOF _Static_assert($check_assert, "$check_assert"); EOF ) if [ "$res" != "" ]; then disable "$test_name" "$file" return 3 fi fi if [ "$check_cpp_if" != "" ]; then res=$(../sparse -E - 2>/dev/null <<- EOF #if !($check_cpp_if) fail #endif EOF ) if [ "$res" != "" ]; then disable "$test_name" "$file" return 3 fi fi if [ -z "$vquiet" ]; then echo " TEST $test_name ($file)" fi verbose "Using command : $(echo "$@")" # grab the expected exit value expected_exit_value=$check_exit_value verbose "Expecting exit value: $expected_exit_value" # do we want a timeout? pre_cmd="" if [ $check_timeout -ne 0 ]; then pre_cmd="timeout $check_timeout" fi shift # launch the test command and # grab the actual output & exit value eval $pre_cmd $default_path/$base_cmd $default_args "$@" \ 1> $file.output.got 2> $file.error.got actual_exit_value=$? must_fail=$check_known_to_fail [ $must_fail -eq 1 ] && [ $V -eq 0 ] && quiet=1 known_ko_tests=$(($known_ko_tests + $must_fail)) for stream in error output; 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' absent $file.output.got if [ "$?" -ne "0" ]; then test_failed=1 fi fi if [ $check_output_excludes -eq 1 ]; then has_none_patterns "$file" 'check-output-excludes' present $file.output.got if [ "$?" -ne "0" ]; then test_failed=1 fi fi if [ $check_output_pattern -eq 1 ]; then # verify the 'check-output-pattern(...)' tags minmax_patterns "$file" 'check-output-pattern' $file.output.got if [ "$?" -ne "0" ]; then test_failed=1 fi fi if [ $check_output_match -eq 1 ]; then # verify the 'check-output-match($insn): $patt' tags match_patterns "$file" 'check-output-match' $file.output.got if [ "$?" -ne "0" ]; then test_failed=1 fi fi if [ $check_output_returns -eq 1 ]; then # verify the 'check-output-return: $value' tags return_patterns "$file" 'check-output-returns' $file.output.got if [ "$?" -ne "0" ]; then test_failed=1 fi fi if [ "$must_fail" -eq "1" ]; then if [ "$test_failed" -eq "1" ]; then [ -z "$vquiet" ] && \ echo "info: XFAIL: test '$file' is known to fail" else echo "error: XPASS: test '$file' is known to fail but succeed!" fi else if [ "$test_failed" -eq "1" ]; then echo "error: FAIL: test '$file' failed" else [ "$V" -ne "0" ] && \ echo "info: PASS: test '$file' passed" fi fi if [ "$test_failed" -ne "$must_fail" ]; then [ $abort -eq 1 ] && exit 1 test_failed=1 failed=1 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 OK=OK [ $failed -eq 0 ] || OK=KO # prints some numbers tests_nr=$(($ok_tests + $ko_tests)) echo "$OK: out of $tests_nr tests, $ok_tests passed, $ko_tests failed" if [ "$known_ko_tests" -ne 0 ]; then echo " $known_ko_tests of them are known to fail" fi 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_help() { echo "Usage: $prog_name [option(s)] [--]format file [name [cmd]]" echo echo "options:" echo " -a append the created test to the input file" echo " -f write a test known to fail" echo " -l write a test for linearized code" echo " -r write a test for linearized code returning 1" echo " -p write a test for pre-processing" echo " -s write a test for symbolic checking" echo echo "argument(s):" echo " file file containing the test case(s)" echo " name name for the test case (defaults to file)" echo " cmd command to be used (defaults to 'sparse \$file')" } ## # do_format([options,] file[, name[, cmd]]) - helps a test writer to format test-suite tags do_format() { def_cmd="$default_cmd" append=0 linear=0 fail=0 ret='' while [ $# -gt 0 ] ; do case "$1" in -a) append=1 ;; -f) fail=1 ;; -l) def_cmd='test-linearize -Wno-decl $file' linear=1 ;; -r) def_cmd='test-linearize -Wno-decl $file' ret=1 ;; -p) def_cmd='sparse -E $file' ;; -s) def_cmd='scheck $file' ;; help|-*) do_format_help return 0 ;; *) break ;; esac shift continue done if [ $# -lt 1 -o $# -gt 3 ]; then do_format_help return 0 fi arg_file "$1" || return 1 file="$1" fname="$2" [ -z "$fname" ] && fname="$(basename "$1" .c)" fcmd="$3" [ -z "$fcmd" ] && fcmd="$def_cmd" cmd=`eval echo $default_path/$fcmd` $cmd 1> $file.output.got 2> $file.error.got fexit_value=$? [ $append != 0 ] && exec >> $file 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 if [ $fail != 0 ]; then echo " * check-known-to-fail" fi if [ "$ret" != '' ]; then echo ' *' echo ' * check-output-ignore' echo " * check-output-returns: $ret" rm -f "$file.output.got" fi if [ $linear != 0 ]; then echo ' *' echo ' * check-output-ignore' echo ' * check-output-contains: xyz\\\\.' echo ' * check-output-excludes: \\\\.' 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 } ## allow flags from environment set -- $SPARSE_TEST_FLAGS "$@" ## process the flags while [ "$#" -gt "0" ]; do case "$1" in -a|--abort) abort=1 ;; -q|--quiet) vquiet=1 ;; --args=*) default_args="${1#--args=}"; ;; single|--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 exit $failed ;; format|--format) shift do_format "$@" exit 0 ;; help) do_usage exit 1 ;; *.c|*.cdoc) tests_list="$tests_list $1" ;; *) if [ ! -d "$1" ]; then do_usage exit 1 fi tests_list="$tests_list $(find "$1" -name '*.c' | sort)" ;; esac shift done if [ -z "$tests_list" ]; then tests_list=`find . -name '*.c' | sed -e 's#^\./\(.*\)#\1#' | sort` fi do_test_suite exit $failed sparse-0.6.4/validation/transparent-union.c000066400000000000000000000005041411531012200207700ustar00rootroot00000000000000struct 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.6.4/validation/type-attribute-align.c000066400000000000000000000006721411531012200213610ustar00rootroot00000000000000#define __aligned(N) __attribute__((aligned(N))) #define alignof(X) __alignof__(X) struct s { short a, b, c; } __aligned(2*sizeof(short)); static int fs(void) { return sizeof(struct s); } static int fa(void) { return alignof(struct s); } void main(void) { _Static_assert( sizeof(struct s) == 4 * sizeof(short), "size"); _Static_assert(alignof(struct s) == 2 * sizeof(short), "alignment"); } /* * check-name: type-attribute-align */ sparse-0.6.4/validation/type-attribute-as.c000066400000000000000000000013001411531012200206570ustar00rootroot00000000000000#define __as __attribute__((address_space(__as))) struct s { int i; } __as; extern void use0(void *); extern void use1(void __as *); void main(void) { struct s s; int i; use0(&s); // KO use0(&i); // OK use1(&s); // OK use1(&i); // KO } /* * check-name: type-attribute-as * * check-error-start type-attribute-as.c:16:15: warning: incorrect type in argument 1 (different address spaces) type-attribute-as.c:16:15: expected void * type-attribute-as.c:16:15: got struct s __as * type-attribute-as.c:19:15: warning: incorrect type in argument 1 (different address spaces) type-attribute-as.c:19:15: expected void __as * type-attribute-as.c:19:15: got int * * check-error-end */ sparse-0.6.4/validation/type-attribute-mod.c000066400000000000000000000004251411531012200210420ustar00rootroot00000000000000#define __noderef __attribute__((noderef)) struct s { int i; } __noderef; void main(void) { struct s s; s.i = 0; } /* * check-name: type-attribute-mod * * check-error-start type-attribute-mod.c:12:9: warning: dereference of noderef expression * check-error-end */ sparse-0.6.4/validation/type-attribute-qual.c000066400000000000000000000004531411531012200212260ustar00rootroot00000000000000static const struct s { int x; } map[2]; static void foo(struct s *p, int v) { p->x += v; } /* * check-name: type-attribute-qual * check-description: When declaring a type and a variable in the same * declaration, ensure that type qualifiers apply to the variable * and not to the type. */ sparse-0.6.4/validation/type-compare.c000066400000000000000000000041521411531012200177110ustar00rootroot00000000000000#define __user __attribute__((address_space(1))) #define __safe __attribute__((safe)) #define __nocast __attribute__((nocast)) #define __bitwise __attribute__((bitwise)) #define __noderef __attribute__((noderef)) int test(void) { if ([int] != [int]) return 1; if (!([int] == [int])) return 1; if ([int] == [long]) return 1; if (!([int] != [long])) return 1; if ([int] == [unsigned int]) return 1; if (!([int] != [unsigned int])) return 1; if ([int] != [int]) return 1; if ([typeof(int)] != [int]) return 1; if ([int] != [typeof(int)]) return 1; if ([typeof(int)] != [typeof(int)]) return 1; if ([char] > [short]) return 1; if ([short] < [char]) return 1; if (!([char] <= [short])) return 1; if (!([short] >= [char])) return 1; if ([short] > [int]) return 1; if ([int] < [short]) return 1; if (!([short] <= [int])) return 1; if (!([int] >= [short])) return 1; if ([int] > [long]) return 1; if ([long] < [int]) return 1; if (!([int] <= [long])) return 1; if (!([long] >= [int])) return 1; if ([long] > [long long]) return 1; if ([long long] < [long]) return 1; if (!([long] <= [long long])) return 1; if (!([long long] >= [long])) return 1; if ([int *] != [int *]) return 1; if ([int *] == [void *]) return 1; // qualifiers are ignored if ([int] != [const int]) return 1; if ([int] != [volatile int]) return 1; // but others modifiers are significant if ([int] == [int __nocast]) return 1; if ([int] == [int __bitwise]) return 1; // if ([int *] == [const int *]) return 1; if ([int *] == [volatile int *]) return 1; if ([int *] == [int __user *]) return 1; if ([int *] == [int __safe *]) return 1; if ([int *] == [int __nocast *]) return 1; if ([int *] == [int __bitwise *]) return 1; if ([int *] == [int __noderef *]) return 1; return 0; } /* * check-name: type-as-first-class comparison * check-description: This test the sparse extension making * types first class citizens which can be compared * for equality (or size for <, >, <=, >=). * See expand.c:compare_types(). * check-command: test-linearize -Wno-decl $file * check-output-ignore * * check-output-contains: ret\\..*\\$0 */ sparse-0.6.4/validation/type1.c000066400000000000000000000007601411531012200163470ustar00rootroot00000000000000/* * 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.6.4/validation/typedef-redef-c89.c000066400000000000000000000004771411531012200204360ustar00rootroot00000000000000typedef int int_t; typedef int int_t; /* * check-name: typedef-redef-c89 * check-command: sparse -std=c89 --pedantic $file * check-known-to-fail * * check-error-start typedef-redef-c89.c:2:13: warning: redefinition of typedef 'int_t' typedef-redef-c89.c:1:13: info: originally defined here * check-error-end */ sparse-0.6.4/validation/typedef-redef.c000066400000000000000000000006161411531012200200300ustar00rootroot00000000000000typedef int ok_t; typedef int ok_t; typedef int ko_t; typedef long ko_t; /* * check-name: typedef-redef * * check-error-start typedef-redef.c:5:14: error: symbol 'ko_t' redeclared with different type (different type sizes): typedef-redef.c:5:14: long [usertype] ko_t typedef-redef.c:4:14: note: previously declared as: typedef-redef.c:4:14: int [usertype] ko_t * check-error-end */ sparse-0.6.4/validation/typedef_shadow.c000066400000000000000000000004361411531012200203120ustar00rootroot00000000000000typedef 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.6.4/validation/typediff-arraysize.c000066400000000000000000000006041411531012200211230ustar00rootroot00000000000000extern int ok0[]; int ok0[1]; // OK extern int ok1[1]; int ok1[]; // OK but size should be 1 extern int ko1[1]; int ko1[2]; // KO /* * check-name: typediff-arraysize * check-known-to-fail * * check-error-start typediff-arraysize.c:3:29: error: symbol 'ko1' redeclared with different type (originally declared at typediff-arraysize.c:3) - different array sizes * check-error-end */ sparse-0.6.4/validation/typediff-enum.c000066400000000000000000000023201411531012200200530ustar00rootroot00000000000000enum num { ZERO, ONE, MANY, }; typedef enum num num; extern int v; num v = 0; extern num w; int w = 0; int foo(void); num foo(void) { return ZERO; } num bar(void); int bar(void) { return ZERO; } void baz(int a); void baz(num a) { } void qux(num a); void qux(int a) { } /* * check-name: typediff-enum * check-known-to-fail * * check-error-start typediff-enum.c:5:5: error: symbol 'v' redeclared with different type (originally declared at typediff-enum.c:4) - different types typediff-enum.c:8:5: error: symbol 'w' redeclared with different type (originally declared at typediff-enum.c:7) - different types typediff-enum.c:11:5: error: symbol 'foo' redeclared with different type (originally declared at typediff-enum.c:10) - different types typediff-enum.c:14:5: error: symbol 'bar' redeclared with different type (originally declared at typediff-enum.c:13) - different types typediff-enum.c:17:6: error: symbol 'baz' redeclared with different type (originally declared at typediff-enum.c:16) - incompatible argument 1 (different types) typediff-enum.c:20:6: error: symbol 'qux' redeclared with different type (originally declared at typediff-enum.c:19) - incompatible argument 1 (different types) * check-error-end */ sparse-0.6.4/validation/typeof-addresspace.c000066400000000000000000000006111411531012200210620ustar00rootroot00000000000000#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.6.4/validation/typeof-attribute.c000066400000000000000000000005511411531012200206120ustar00rootroot00000000000000#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.6.4/validation/typeof-bad.c000066400000000000000000000005621411531012200173370ustar00rootroot00000000000000static typeof(undef) a; static int foo(void) { return a; } /* * check-name: typeof-bad * * check-error-start typeof-bad.c:1:15: error: undefined identifier 'undef' typeof-bad.c:5:16: warning: incorrect type in return expression (different base types) typeof-bad.c:5:16: expected int typeof-bad.c:5:16: got bad type static [toplevel] a * check-error-end */ sparse-0.6.4/validation/typeof-mods.c000066400000000000000000000045341411531012200175560ustar00rootroot00000000000000#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_restrict(void) { int *restrict obj, *restrict *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_atomic(void) { int _Atomic 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) { static __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.6.4/validation/typeof-noderef.c000066400000000000000000000004701411531012200202310ustar00rootroot00000000000000#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.6.4/validation/typeof-safe.c000066400000000000000000000014511411531012200175250ustar00rootroot00000000000000#define __safe __attribute__((safe)) static void test_safe(void) { int obj; int __safe *ptr; int __safe *ptr2 = ptr; typeof(ptr) ptr3 = ptr; typeof(*ptr) var2 = obj; int __safe var3 = obj; int *ptr4 = &obj; int *ptr5 = ptr; // KO typeof(*ptr) sobj; typeof(&sobj) ptr6 = &obj; typeof(&sobj) ptr7 = ptr; // KO obj = obj; ptr = ptr; obj = *ptr; ptr = (int __safe *) &obj; } /* * check-name: typeof-safe * * check-error-start typeof-safe.c:13:21: warning: incorrect type in initializer (different modifiers) typeof-safe.c:13:21: expected int *ptr5 typeof-safe.c:13:21: got int [safe] *ptr typeof-safe.c:17:30: warning: incorrect type in initializer (different modifiers) typeof-safe.c:17:30: expected int *ptr7 typeof-safe.c:17:30: got int [safe] *ptr * check-error-end */ sparse-0.6.4/validation/typesign.c000066400000000000000000000031401411531012200171420ustar00rootroot00000000000000static 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.6.4/validation/usual-conv-lp32.c000066400000000000000000000003361411531012200201560ustar00rootroot00000000000000extern long l; extern unsigned int u; #if __SIZEOF_LONG__ == __SIZEOF_INT__ _Static_assert([typeof(l + u)] == [unsigned long], "ulong"); #endif /* * check-name: usual-conversions * check-command: sparse -m32 $file */ sparse-0.6.4/validation/var-undef-partial.c000066400000000000000000000006011411531012200206200ustar00rootroot00000000000000int foo(int a, int b) { int var = 0; int r; if (a) var = 1; if (b) r = var; return r; // undef if !b } /* * check-name: variable partially undefined * check-description: trigger a bug in symbol/memop simplification * check-description: sparse-llvm is used here as semantic checker of sparse's IR * check-command: sparse-llvm -Wno-decl $file * check-output-ignore */ sparse-0.6.4/validation/varargs1.c000066400000000000000000000002451411531012200170310ustar00rootroot00000000000000extern 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.6.4/validation/vla-sizeof-ice.c000066400000000000000000000006461411531012200201250ustar00rootroot00000000000000// credit goes to Martin Uecker for the awesome ICE_P macro #define ICE_P(x) \ (__builtin_types_compatible_p(typeof(0?((void*)((long)(x)*0l)):(int*)1),int*)) #define T(x) __builtin_choose_expr(ICE_P(x), 1, 0) #define TEST(x, r) _Static_assert(T(x) == r, #x " => " #r) static void test(int n) { char foo[n++]; TEST(sizeof(foo), 0); } /* * check-name: vla-sizeof-ice * check-command: sparse -Wno-vla $file */ sparse-0.6.4/validation/vla-sizeof.c000066400000000000000000000006541411531012200173660ustar00rootroot00000000000000unsigned long vla_sizeof0(int size) { int a[size]; return sizeof(a); } unsigned long vla_sizeof1(int size) { struct s { int a[size]; }; return sizeof(struct s); } unsigned long vla_sizeof2(int size) { struct s { int a[size]; } *p; return sizeof(*p); } void* vla_inc(int size, void *base) { struct s { int a[size]; } *p = base; ++p; return p; } /* * check-name: vla-sizeof.c * * check-known-to-fail */ sparse-0.6.4/validation/vla-sizeof0.c000066400000000000000000000004671411531012200174500ustar00rootroot00000000000000#define N 2 #define T int static unsigned int foo(int x) { T a[(1,N)]; return sizeof(a) == (N * sizeof(T)); } /* * check-name: vla-sizeof cte,cte * check-command: test-linearize -Wvla $file * * check-output-ignore * check-output-contains: ret\\.32 *\\$1 * * check-error-start * check-error-end */ sparse-0.6.4/validation/vla-sizeof1.c000066400000000000000000000005631411531012200174460ustar00rootroot00000000000000#define N 2 #define T int static unsigned int foo(int x) { T a[(x,N)]; return sizeof(a) == (N * sizeof(T)); } /* * check-name: vla-sizeof var,cte * check-command: test-linearize -Wvla $file * * check-output-ignore * check-output-contains: ret\\.32 *\\$1 * * check-error-start vla-sizeof1.c:6:15: warning: Variable length array is used. * check-error-end */ sparse-0.6.4/validation/vla-sizeof2.c000066400000000000000000000005521411531012200174450ustar00rootroot00000000000000#define N 2 #define T int static unsigned long foo(int x) { T a[x]; return sizeof(a) == (x * sizeof(T)); } /* * check-name: vla-sizeof var * check-command: test-linearize -Wvla $file * * check-output-ignore * check-output-contains: ret\\..*\\$1 * * check-error-start vla-sizeof2.c:6:13: warning: Variable length array is used. * check-error-end */ sparse-0.6.4/validation/vla-sizeof3.c000066400000000000000000000005711411531012200174470ustar00rootroot00000000000000#define N 2UL #define T int static unsigned long foo(int x) { T a[x][N]; return sizeof(a) == (N * x * sizeof(T)); } /* * check-name: vla-sizeof var X cte * check-command: test-linearize -Wvla $file * * check-output-ignore * check-output-contains: ret\\..*\\$1 * * check-error-start vla-sizeof3.c:6:13: warning: Variable length array is used. * check-error-end */ sparse-0.6.4/validation/vla-sizeof4.c000066400000000000000000000006741411531012200174540ustar00rootroot00000000000000#define N 2 #define T int static unsigned long foo(int x, int y) { T a[x][y]; return sizeof(a) == (x * (y * sizeof(T))); } /* * check-name: vla-sizeof var X var * check-command: test-linearize -Wvla $file * * check-output-ignore * check-output-contains: ret\\..*\\$1 * * check-error-start vla-sizeof4.c:6:16: warning: Variable length array is used. vla-sizeof4.c:6:13: warning: Variable length array is used. * check-error-end */ sparse-0.6.4/validation/wide.c000066400000000000000000000002751411531012200162360ustar00rootroot00000000000000static 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 */ sparse-0.6.4/version.c000066400000000000000000000001241411531012200146320ustar00rootroot00000000000000#include "lib.h" #include "version.h" const char *sparse_version = SPARSE_VERSION;