pax_global_header00006660000000000000000000000064135507313320014514gustar00rootroot0000000000000052 comment=8ab81af8985237432393894e814bb0ae1d7a1799 sparse-0.6.1/000077500000000000000000000000001355073133200130155ustar00rootroot00000000000000sparse-0.6.1/.gitignore000066400000000000000000000005721355073133200150110ustar00rootroot00000000000000# generic *.o *.o.d *.a *.so .*.swp *.pyc *~ # generated version.h # programs test-inspect test-lexing test-parsing obfuscate sparse compile graph test-dissect test-linearize example test-unssa ctags c2xml sparse-llvm # tags tags TAGS # stgit generated dirs patches-* # quilt's files patches series # local makefile local.mk .*.mk # cscope and Qt files cscope.out *.pro* sparse-0.6.1/Documentation/000077500000000000000000000000001355073133200156265ustar00rootroot00000000000000sparse-0.6.1/Documentation/.gitignore000066400000000000000000000000241355073133200176120ustar00rootroot00000000000000build dev-options.1 sparse-0.6.1/Documentation/IR.rst000066400000000000000000000242671355073133200167050ustar00rootroot00000000000000.. default-domain:: ir Sparse's 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_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 .. 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_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 or a label-as-value. The value is given as an expression EXPR_STRING or EXPR_LABEL. * .val: (expression) input expression * .target: the resulting value * .type: type of .target, the value .. 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_users: list of phi instructions 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, .len: offet & size 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.1/Documentation/Makefile000066400000000000000000000011261355073133200172660ustar00rootroot00000000000000# 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.1/Documentation/TODO.md000066400000000000000000000061441355073133200167220ustar00rootroot00000000000000TODO ==== Essential --------- * SSA is broken by simplify_loads() & branches rewriting/simplification * attributes of struct, union & enums are ignored (and possibly in other cases too). * add support for bitwise enums Documentation ------------- * document the extensions * 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 * Previous declarations and the definition need to be merged. For example, in the code here below, the function definition is **not** static: ``` static void foo(void); void foo(void) { ... } ``` Testsuite -------- * there are more than 50 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 __builtin_unreachable() * add support for format(printf()) (WIP by Ben Dooks) * make use of UNDEFs (issues warnings, simplification, ... ?) * add a pass to inline small functions during simplification. Optimization ------------ * the current way of doing CSE uses a lot of time * add SSA based DCE * add SSA based PRE * Add SSA based SCCP * use better/more systematic use of internal verification framework IR -- * 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 ----------------- * add some basic register allocation * add a pass to transform 3-addresses code to 2-addresses * what can be done for x86? Longer term/to investigate -------------------------- * better architecture handling than current machine.h + target.c * 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 have elements be removed while being iterated but this is hard to insure it is not done. * 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.1/Documentation/api.rst000066400000000000000000000004151355073133200171310ustar00rootroot00000000000000Sparse API ========== .. contents:: :local: :depth: 2 Utilities ~~~~~~~~~ .. c:autodoc:: ptrlist.c .. c:autodoc:: utils.h Parsing ~~~~~~~ .. c:autodoc:: expression.h Typing ~~~~~~ .. c:autodoc:: evaluate.h Optimization ~~~~~~~~~~~~ .. c:autodoc:: simplify.c sparse-0.6.1/Documentation/conf.py000066400000000000000000000122331355073133200171260ustar00rootroot00000000000000# -*- 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.3' # 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. # 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'] # 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 = { '**': [ 'relations.html', # needs 'show_related': True theme option to display 'searchbox.html', ] } 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.1/Documentation/data-structures.txt000066400000000000000000000103431355073133200215220ustar00rootroot00000000000000- 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.1/Documentation/dev-options.rst000066400000000000000000000022301355073133200206240ustar00rootroot00000000000000sparse - extra 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.1/Documentation/doc-guide.rst000066400000000000000000000065011355073133200202220ustar00rootroot00000000000000How to write sparse documentation ================================= 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) :noindex: :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.1/Documentation/index.rst000066400000000000000000000010031355073133200174610ustar00rootroot00000000000000.. sparse documentation master file. Welcome to sparse's documentation ================================= .. toctree:: :maxdepth: 1 User documentation ------------------ .. toctree:: :maxdepth: 1 nocast-vs-bitwise Developer documentation ----------------------- .. toctree:: :maxdepth: 1 test-suite dev-options api IR doc-guide How to contribute ----------------- .. toctree:: :maxdepth: 1 submitting-patches TODO Indices and tables ================== * :ref:`genindex` sparse-0.6.1/Documentation/logo.svg000066400000000000000000000133731355073133200173160ustar00rootroot00000000000000 image/svg+xml Layer 1 sparse-0.6.1/Documentation/nocast-vs-bitwise.md000066400000000000000000000034511355073133200215340ustar00rootroot00000000000000# __nocast vs __bitwise `__nocast` warns about explicit or implicit casting to different types. HOWEVER, it doesn't consider two 32-bit integers to be different types, so a `__nocast int` type may be returned as a regular `int` type and then the `__nocast` is lost. So `__nocast` on integer types is usually not that powerful. It just gets lost too easily. It's more useful for things like pointers. It also doesn't warn about the mixing: you can add integers to `__nocast` integer types, and it's not really considered anything wrong. `__bitwise` ends up being a *stronger integer separation*. That one doesn't allow you to mix with non-bitwise integers, so now it's much harder to lose the type by mistake. So the basic rule is: - `__nocast` on its own tends to be more useful for *big* integers that still need to act like integers, but you want to make it much less likely that they get truncated by mistake. So a 64-bit integer that you don't want to mistakenly/silently be returned as `int`, for example. But they mix well with random integer types, so you can add to them etc without using anything special. However, that mixing also means that the `__nocast` really gets lost fairly easily. - `__bitwise` is for *unique types* that cannot be mixed with other types, and that you'd never want to just use as a random integer (the integer `0` is special, though, and gets silently accepted - it's kind of like `NULL` for pointers). So `gfp_t` or the `safe endianness` types would be `__bitwise`: you can only operate on them by doing specific operations that know about *that* particular type. Generally, you want `__bitwise` if you are looking for type safety. `__nocast` really is pretty weak. ## Reference: * Linus' e-mail about `__nocast` vs `__bitwise`: sparse-0.6.1/Documentation/sphinx/000077500000000000000000000000001355073133200171375ustar00rootroot00000000000000sparse-0.6.1/Documentation/sphinx/cdoc.py000077500000000000000000000170121355073133200204250ustar00rootroot00000000000000#!/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('.') l = '\t' + l + '.' lst.append((n, 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.ext.autodoc import AutodocReporter 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.reporter, memo.title_styles, memo.section_level memo.reporter = AutodocReporter(lst, memo.reporter) node = docutils.nodes.section() try: self.state.nested_parse(lst, 0, node, match_titles=1) finally: memo.reporter, 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.1/Documentation/sphinx/ir.py000077500000000000000000000033411355073133200201270ustar00rootroot00000000000000#!/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.1/Documentation/submitting-patches.md000066400000000000000000000014771355073133200217730ustar00rootroot00000000000000Submitting patches: the sparse version ====================================== Sparse uses a patch submit process similar to the Linux Kernel [Submitting Patches](https://www.kernel.org/doc/html/v4.12/process/submitting-patches.html) This document mostly focuses on the parts that might be different from the Linux Kernel submitting process. 1. Git clone a sparse repository: git clone git://git.kernel.org/pub/scm/devel/sparse/sparse.git 2. [Coding Style](https://www.kernel.org/doc/html/v4.12/process/coding-style.html) remains the same. 3. Sign off the patch. The usage of the Signed-off-by tag is the same as [Linux Kernel Sign your work](https://www.kernel.org/doc/html/v4.12/process/submitting-patches.html#sign-your-work-the-developer-s-certificate-of-origin). Notice that sparse uses the MIT License. sparse-0.6.1/Documentation/test-suite.rst000066400000000000000000000107461355073133200204760ustar00rootroot00000000000000Test 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. 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.1/FAQ000066400000000000000000000064771355073133200133650ustar00rootroot00000000000000 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.1/LICENSE000066400000000000000000000027261355073133200140310ustar00rootroot00000000000000The '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.1/Makefile000066400000000000000000000152011355073133200144540ustar00rootroot00000000000000VERSION=0.6.1 ######################################################################## # The following variables can be overwritten from the command line OS = linux CC = gcc 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 += parse.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 += sset.o LIB_OBJS += stats.o LIB_OBJS += storage.o LIB_OBJS += symbol.o LIB_OBJS += target.o LIB_OBJS += tokenize.o LIB_OBJS += unssa.o LIB_OBJS += utils.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-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 # 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) ifeq ($(shell expr "$(LLVM_VERSION)" : '[3-9]\.'),2) LLVM_PROGS := sparse-llvm $(LLVM_PROGS): LD := g++ LLVM_LDFLAGS := $(shell $(LLVM_CONFIG) --ldflags) LLVM_CFLAGS := $(shell $(LLVM_CONFIG) --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 ######################################################################## 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)') lib.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/%.t: $(PROGRAMS) @validation/test-suite single $*.c 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.1/README000066400000000000000000000056771355073133200137140ustar00rootroot00000000000000 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.1/allocate.c000066400000000000000000000112501355073133200147440ustar00rootroot00000000000000/* * 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.1/allocate.h000066400000000000000000000052571355073133200147630ustar00rootroot00000000000000#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.1/ast-inspect.c000066400000000000000000000154141355073133200154200ustar00rootroot00000000000000 #include "token.h" #include "parse.h" #include "symbol.h" #include "ast-inspect.h" #include "expression.h" static inline void inspect_ptr_list(AstNode *node, const char *name, void (*inspect)(AstNode *)) { struct ptr_list *ptrlist = node->ptr; void *ptr; int i = 0; node->text = g_strdup_printf("%s %s:", node->text, name); FOR_EACH_PTR(ptrlist, ptr) { char *index = g_strdup_printf("%d: ", i++); ast_append_child(node, index, ptr, inspect); } END_FOR_EACH_PTR(ptr); } static const char *statement_type_name(enum statement_type type) { static const char *statement_type_name[] = { [STMT_NONE] = "STMT_NONE", [STMT_DECLARATION] = "STMT_DECLARATION", [STMT_EXPRESSION] = "STMT_EXPRESSION", [STMT_COMPOUND] = "STMT_COMPOUND", [STMT_IF] = "STMT_IF", [STMT_RETURN] = "STMT_RETURN", [STMT_CASE] = "STMT_CASE", [STMT_SWITCH] = "STMT_SWITCH", [STMT_ITERATOR] = "STMT_ITERATOR", [STMT_LABEL] = "STMT_LABEL", [STMT_GOTO] = "STMT_GOTO", [STMT_ASM] = "STMT_ASM", [STMT_CONTEXT] = "STMT_CONTEXT", [STMT_RANGE] = "STMT_RANGE", }; return statement_type_name[type] ?: "UNKNOWN_STATEMENT_TYPE"; } void inspect_statement(AstNode *node) { struct statement *stmt = node->ptr; node->text = g_strdup_printf("%s %s:", node->text, statement_type_name(stmt->type)); switch (stmt->type) { case STMT_COMPOUND: ast_append_child(node, "stmts:", stmt->stmts, inspect_statement_list); break; case STMT_EXPRESSION: ast_append_child(node, "expression:", stmt->expression, inspect_expression); break; case STMT_IF: ast_append_child(node, "conditional:", stmt->if_conditional, inspect_expression); ast_append_child(node, "if_true:", stmt->if_true, inspect_statement); ast_append_child(node, "if_false:", stmt->if_false, inspect_statement); break; case STMT_ITERATOR: ast_append_child(node, "break:", stmt->iterator_break, inspect_symbol); ast_append_child(node, "continue:", stmt->iterator_continue, inspect_symbol); ast_append_child(node, "pre_statement:", stmt->iterator_pre_statement, inspect_statement); ast_append_child(node, "statement:", stmt->iterator_statement, inspect_statement); ast_append_child(node, "post_statement:", stmt->iterator_post_statement, inspect_statement); break; case STMT_SWITCH: ast_append_child(node, "switch_expression:", stmt->switch_expression, inspect_expression); ast_append_child(node, "switch_statement:", stmt->switch_statement, inspect_statement); ast_append_child(node, "switch_break:", stmt->switch_break, inspect_symbol); ast_append_child(node, "switch_case:", stmt->switch_case, inspect_symbol); break; case STMT_CASE: ast_append_child(node, "case_expression:", stmt->case_expression, inspect_expression); ast_append_child(node, "case_to:", stmt->case_to, inspect_expression); ast_append_child(node, "case_statement:", stmt->case_statement, inspect_statement); ast_append_child(node, "case_label:", stmt->case_label, inspect_symbol); break; case STMT_RETURN: ast_append_child(node, "ret_value:", stmt->ret_value, inspect_expression); ast_append_child(node, "ret_target:", stmt->ret_target, inspect_symbol); break; default: break; } } void inspect_statement_list(AstNode *node) { inspect_ptr_list(node, "statement_list", inspect_statement); } static const char *symbol_type_name(enum type type) { static const char *type_name[] = { [SYM_UNINITIALIZED] = "SYM_UNINITIALIZED", [SYM_PREPROCESSOR] = "SYM_PREPROCESSOR", [SYM_BASETYPE] = "SYM_BASETYPE", [SYM_NODE] = "SYM_NODE", [SYM_PTR] = "SYM_PTR", [SYM_FN] = "SYM_FN", [SYM_ARRAY] = "SYM_ARRAY", [SYM_STRUCT] = "SYM_STRUCT", [SYM_UNION] = "SYM_UNION", [SYM_ENUM] = "SYM_ENUM", [SYM_TYPEDEF] = "SYM_TYPEDEF", [SYM_TYPEOF] = "SYM_TYPEOF", [SYM_MEMBER] = "SYM_MEMBER", [SYM_BITFIELD] = "SYM_BITFIELD", [SYM_LABEL] = "SYM_LABEL", [SYM_RESTRICT] = "SYM_RESTRICT", [SYM_FOULED] = "SYM_FOULED", [SYM_KEYWORD] = "SYM_KEYWORD", [SYM_BAD] = "SYM_BAD", }; return type_name[type] ?: "UNKNOWN_TYPE"; } void inspect_symbol(AstNode *node) { struct symbol *sym = node->ptr; node->text = g_strdup_printf("%s %s: %s", node->text, symbol_type_name(sym->type), builtin_typename(sym) ?: show_ident(sym->ident)); ast_append_child(node, "ctype.base_type:", sym->ctype.base_type,inspect_symbol); switch (sym->namespace) { case NS_PREPROCESSOR: break; default: ast_append_child(node, "arguments:", sym->arguments, inspect_symbol_list); ast_append_child(node, "symbol_list:", sym->symbol_list, inspect_symbol_list); ast_append_child(node, "stmt:", sym->stmt, inspect_statement); break; } } void inspect_symbol_list(AstNode *node) { inspect_ptr_list(node, "symbol_list", inspect_symbol); } static const char *expression_type_name(enum expression_type type) { static const char *expression_type_name[] = { [EXPR_VALUE] = "EXPR_VALUE", [EXPR_STRING] = "EXPR_STRING", [EXPR_SYMBOL] = "EXPR_SYMBOL", [EXPR_TYPE] = "EXPR_TYPE", [EXPR_BINOP] = "EXPR_BINOP", [EXPR_ASSIGNMENT] = "EXPR_ASSIGNMENT", [EXPR_LOGICAL] = "EXPR_LOGICAL", [EXPR_DEREF] = "EXPR_DEREF", [EXPR_PREOP] = "EXPR_PREOP", [EXPR_POSTOP] = "EXPR_POSTOP", [EXPR_CAST] = "EXPR_CAST", [EXPR_FORCE_CAST] = "EXPR_FORCE_CAST", [EXPR_IMPLIED_CAST] = "EXPR_IMPLIED_CAST", [EXPR_SIZEOF] = "EXPR_SIZEOF", [EXPR_ALIGNOF] = "EXPR_ALIGNOF", [EXPR_PTRSIZEOF] = "EXPR_PTRSIZEOF", [EXPR_CONDITIONAL] = "EXPR_CONDITIONAL", [EXPR_SELECT] = "EXPR_SELECT", [EXPR_STATEMENT] = "EXPR_STATEMENT", [EXPR_CALL] = "EXPR_CALL", [EXPR_COMMA] = "EXPR_COMMA", [EXPR_COMPARE] = "EXPR_COMPARE", [EXPR_LABEL] = "EXPR_LABEL", [EXPR_INITIALIZER] = "EXPR_INITIALIZER", [EXPR_IDENTIFIER] = "EXPR_IDENTIFIER", [EXPR_INDEX] = "EXPR_INDEX", [EXPR_POS] = "EXPR_POS", [EXPR_FVALUE] = "EXPR_FVALUE", [EXPR_SLICE] = "EXPR_SLICE", [EXPR_OFFSETOF] = "EXPR_OFFSETOF", }; return expression_type_name[type] ?: "UNKNOWN_EXPRESSION_TYPE"; } void inspect_expression(AstNode *node) { struct expression *expr = node->ptr; node->text = g_strdup_printf("%s %s", node->text, expression_type_name(expr->type)); switch (expr->type) { case EXPR_STATEMENT: ast_append_child(node, "statement:", expr->statement, inspect_statement); break; case EXPR_BINOP: case EXPR_COMMA: case EXPR_COMPARE: case EXPR_LOGICAL: case EXPR_ASSIGNMENT: ast_append_child(node, "left:", expr->left, inspect_expression); ast_append_child(node, "right:", expr->right, inspect_expression); break; case EXPR_CAST: case EXPR_FORCE_CAST: case EXPR_IMPLIED_CAST: ast_append_child(node, "cast_type:", expr->cast_type, inspect_symbol); ast_append_child(node, "cast_expression:", expr->cast_expression, inspect_expression); break; case EXPR_PREOP: ast_append_child(node, "unop:", expr->unop, inspect_expression); break; default: break; } } sparse-0.6.1/ast-inspect.h000066400000000000000000000005121355073133200154160ustar00rootroot00000000000000 #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.1/ast-model.c000066400000000000000000000346021355073133200150530ustar00rootroot00000000000000/* * 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.1/ast-model.h000066400000000000000000000043061355073133200150560ustar00rootroot00000000000000 /* * 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.1/ast-view.c000066400000000000000000000022151355073133200147200ustar00rootroot00000000000000 #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.1/ast-view.h000066400000000000000000000001371355073133200147260ustar00rootroot00000000000000 #include #include "lib.h" extern void treeview_main(struct symbol_list *syms); sparse-0.6.1/bitmap.h000066400000000000000000000026341355073133200144470ustar00rootroot00000000000000#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.1/bits.h000066400000000000000000000023241355073133200141300ustar00rootroot00000000000000/* 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; } #endif sparse-0.6.1/builtin.c000066400000000000000000000647461355073133200146500ustar00rootroot00000000000000/* * builtin evaluation & expansion. * * Copyright (C) 2003 Transmeta Corp. * 2003-2004 Linus Torvalds * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include "expression.h" #include "evaluate.h" #include "expand.h" #include "symbol.h" #include "compat/bswap.h" #include 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_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 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, }; /* * Builtin functions */ static struct symbol builtin_fn_type = { .type = SYM_FN /* , .variadic =1 */ }; static struct sym_init { const char *name; struct symbol *base_type; unsigned int modifiers; struct symbol_op *op; } builtins_table[] = { { "__builtin_constant_p", &builtin_fn_type, MOD_TOPLEVEL, &constant_p_op }, { "__builtin_safe_p", &builtin_fn_type, MOD_TOPLEVEL, &safe_p_op }, { "__builtin_warning", &builtin_fn_type, MOD_TOPLEVEL, &warning_op }, { "__builtin_expect", &builtin_fn_type, MOD_TOPLEVEL, &expect_op }, { "__builtin_choose_expr", &builtin_fn_type, MOD_TOPLEVEL, &choose_op }, { "__builtin_bswap16", &builtin_fn_type, MOD_TOPLEVEL, &bswap_op }, { "__builtin_bswap32", &builtin_fn_type, MOD_TOPLEVEL, &bswap_op }, { "__builtin_bswap64", &builtin_fn_type, MOD_TOPLEVEL, &bswap_op }, { "__builtin_clrsb", &builtin_fn_type, MOD_TOPLEVEL, &clrsb_op }, { "__builtin_clrsbl", &builtin_fn_type, MOD_TOPLEVEL, &clrsb_op }, { "__builtin_clrsbll", &builtin_fn_type, MOD_TOPLEVEL, &clrsb_op }, { "__builtin_clz", &builtin_fn_type, MOD_TOPLEVEL, &clz_op }, { "__builtin_clzl", &builtin_fn_type, MOD_TOPLEVEL, &clz_op }, { "__builtin_clzll", &builtin_fn_type, MOD_TOPLEVEL, &clz_op }, { "__builtin_ctz", &builtin_fn_type, MOD_TOPLEVEL, &ctz_op }, { "__builtin_ctzl", &builtin_fn_type, MOD_TOPLEVEL, &ctz_op }, { "__builtin_ctzll", &builtin_fn_type, MOD_TOPLEVEL, &ctz_op }, { "__builtin_ffs", &builtin_fn_type, MOD_TOPLEVEL, &ffs_op }, { "__builtin_ffsl", &builtin_fn_type, MOD_TOPLEVEL, &ffs_op }, { "__builtin_ffsll", &builtin_fn_type, MOD_TOPLEVEL, &ffs_op }, { "__builtin_isfinite", &builtin_fn_type, MOD_TOPLEVEL, &fp_unop_op }, { "__builtin_isinf", &builtin_fn_type, MOD_TOPLEVEL, &fp_unop_op }, { "__builtin_isinf_sign", &builtin_fn_type, MOD_TOPLEVEL, &fp_unop_op }, { "__builtin_isnan", &builtin_fn_type, MOD_TOPLEVEL, &fp_unop_op }, { "__builtin_isnormal", &builtin_fn_type, MOD_TOPLEVEL, &fp_unop_op }, { "__builtin_parity", &builtin_fn_type, MOD_TOPLEVEL, &parity_op }, { "__builtin_parityl", &builtin_fn_type, MOD_TOPLEVEL, &parity_op }, { "__builtin_parityll", &builtin_fn_type, MOD_TOPLEVEL, &parity_op }, { "__builtin_popcount", &builtin_fn_type, MOD_TOPLEVEL, &popcount_op }, { "__builtin_popcountl", &builtin_fn_type, MOD_TOPLEVEL, &popcount_op }, { "__builtin_popcountll", &builtin_fn_type, MOD_TOPLEVEL, &popcount_op }, { "__builtin_signbit", &builtin_fn_type, MOD_TOPLEVEL, &fp_unop_op }, { "__builtin_add_overflow", &builtin_fn_type, MOD_TOPLEVEL, &overflow_op }, { "__builtin_sub_overflow", &builtin_fn_type, MOD_TOPLEVEL, &overflow_op }, { "__builtin_mul_overflow", &builtin_fn_type, MOD_TOPLEVEL, &overflow_op }, { "__builtin_add_overflow_p", &builtin_fn_type, MOD_TOPLEVEL, &overflow_p_op }, { "__builtin_sub_overflow_p", &builtin_fn_type, MOD_TOPLEVEL, &overflow_p_op }, { "__builtin_mul_overflow_p", &builtin_fn_type, MOD_TOPLEVEL, &overflow_p_op }, { NULL, NULL, 0 } }; void init_builtins(int stream) { struct sym_init *ptr; builtin_fn_type.variadic = 1; for (ptr = builtins_table; ptr->name; ptr++) { struct symbol *sym; sym = create_symbol(stream, ptr->name, SYM_NODE, NS_SYMBOL); sym->ctype.base_type = ptr->base_type; sym->ctype.modifiers = ptr->modifiers; sym->op = ptr->op; sym->builtin = 1; } } static void declare_builtin(const char *name, struct symbol *rtype, int variadic, ...) { int stream = 0; // FIXME struct symbol *sym = create_symbol(stream, name, SYM_NODE, NS_SYMBOL); struct symbol *fun = alloc_symbol(sym->pos, SYM_FN); struct symbol *arg; va_list args; sym->ctype.base_type = fun; sym->ctype.modifiers = MOD_TOPLEVEL; sym->builtin = 1; fun->ctype.base_type = rtype; fun->variadic = variadic; va_start(args, variadic); while ((arg = va_arg(args, struct symbol *))) { struct symbol *anode = alloc_symbol(sym->pos, SYM_NODE); anode->ctype.base_type = arg; add_symbol(&fun->arguments, anode); } va_end(args); } void declare_builtins(void) { struct symbol *va_list_ctype = &ptr_ctype; declare_builtin("__builtin_abort", &void_ctype, 0, NULL); declare_builtin("__builtin_abs", &int_ctype , 0, &int_ctype, NULL); declare_builtin("__builtin_alloca", &ptr_ctype, 0, size_t_ctype, NULL); declare_builtin("__builtin_alpha_cmpbge", &long_ctype, 0, &long_ctype, &long_ctype, NULL); declare_builtin("__builtin_alpha_extbl", &long_ctype, 0, &long_ctype, &long_ctype, NULL); declare_builtin("__builtin_alpha_extwl", &long_ctype, 0, &long_ctype, &long_ctype, NULL); declare_builtin("__builtin_alpha_insbl", &long_ctype, 0, &long_ctype, &long_ctype, NULL); declare_builtin("__builtin_alpha_inslh", &long_ctype, 0, &long_ctype, &long_ctype, NULL); declare_builtin("__builtin_alpha_insql", &long_ctype, 0, &long_ctype, &long_ctype, NULL); declare_builtin("__builtin_alpha_inswl", &long_ctype, 0, &long_ctype, &long_ctype, NULL); declare_builtin("__builtin_bcmp", &int_ctype , 0, &const_ptr_ctype, &const_ptr_ctype, size_t_ctype, NULL); declare_builtin("__builtin_bcopy", &void_ctype, 0, &const_ptr_ctype, &ptr_ctype, size_t_ctype, NULL); declare_builtin("__builtin_bswap16", &ushort_ctype, 0, &ushort_ctype, NULL); declare_builtin("__builtin_bswap32", &uint_ctype, 0, &uint_ctype, NULL); declare_builtin("__builtin_bswap64", &ullong_ctype, 0, &ullong_ctype, NULL); declare_builtin("__builtin_bzero", &void_ctype, 0, &ptr_ctype, size_t_ctype, NULL); declare_builtin("__builtin_calloc", &ptr_ctype, 0, size_t_ctype, size_t_ctype, NULL); declare_builtin("__builtin_clrsb", &int_ctype, 0, &int_ctype, NULL); declare_builtin("__builtin_clrsbl", &int_ctype, 0, &long_ctype, NULL); declare_builtin("__builtin_clrsbll", &int_ctype, 0, &llong_ctype, NULL); declare_builtin("__builtin_clz", &int_ctype, 0, &int_ctype, NULL); declare_builtin("__builtin_clzl", &int_ctype, 0, &long_ctype, NULL); declare_builtin("__builtin_clzll", &int_ctype, 0, &llong_ctype, NULL); declare_builtin("__builtin_ctz", &int_ctype, 0, &int_ctype, NULL); declare_builtin("__builtin_ctzl", &int_ctype, 0, &long_ctype, NULL); declare_builtin("__builtin_ctzll", &int_ctype, 0, &llong_ctype, NULL); declare_builtin("__builtin_exit", &void_ctype, 0, &int_ctype, NULL); declare_builtin("__builtin_expect", &long_ctype, 0, &long_ctype ,&long_ctype, NULL); declare_builtin("__builtin_extract_return_addr", &ptr_ctype, 0, &ptr_ctype, NULL); declare_builtin("__builtin_fabs", &double_ctype, 0, &double_ctype, NULL); declare_builtin("__builtin_ffs", &int_ctype, 0, &int_ctype, NULL); declare_builtin("__builtin_ffsl", &int_ctype, 0, &long_ctype, NULL); declare_builtin("__builtin_ffsll", &int_ctype, 0, &llong_ctype, NULL); declare_builtin("__builtin_frame_address", &ptr_ctype, 0, &uint_ctype, NULL); declare_builtin("__builtin_free", &void_ctype, 0, &ptr_ctype, NULL); declare_builtin("__builtin_huge_val", &double_ctype, 0, NULL); declare_builtin("__builtin_huge_valf", &float_ctype, 0, NULL); declare_builtin("__builtin_huge_vall", &ldouble_ctype, 0, NULL); declare_builtin("__builtin_index", &string_ctype, 0, &const_string_ctype, &int_ctype, NULL); declare_builtin("__builtin_inf", &double_ctype, 0, NULL); declare_builtin("__builtin_inff", &float_ctype, 0, NULL); declare_builtin("__builtin_infl", &ldouble_ctype, 0, NULL); declare_builtin("__builtin_isfinite", &int_ctype, 1, NULL); declare_builtin("__builtin_isgreater", &int_ctype, 0, &float_ctype, &float_ctype, NULL); declare_builtin("__builtin_isgreaterequal", &int_ctype, 0, &float_ctype, &float_ctype, NULL); declare_builtin("__builtin_isinf", &int_ctype, 1, NULL); declare_builtin("__builtin_isinf_sign", &int_ctype, 1, NULL); declare_builtin("__builtin_isless", &int_ctype, 0, &float_ctype, &float_ctype, NULL); declare_builtin("__builtin_islessequal", &int_ctype, 0, &float_ctype, &float_ctype, NULL); declare_builtin("__builtin_islessgreater", &int_ctype, 0, &float_ctype, &float_ctype, NULL); declare_builtin("__builtin_isnan", &int_ctype, 1, NULL); declare_builtin("__builtin_isnormal", &int_ctype, 1, NULL); declare_builtin("__builtin_isunordered", &int_ctype, 0, &float_ctype, &float_ctype, NULL); declare_builtin("__builtin_labs", &long_ctype, 0, &long_ctype, NULL); declare_builtin("__builtin_llabs", &llong_ctype, 0, &llong_ctype, NULL); declare_builtin("__builtin_malloc", &ptr_ctype, 0, size_t_ctype, NULL); declare_builtin("__builtin_memchr", &ptr_ctype, 0, &const_ptr_ctype, &int_ctype, size_t_ctype, NULL); declare_builtin("__builtin_memcmp", &int_ctype, 0, &const_ptr_ctype, &const_ptr_ctype, size_t_ctype, NULL); declare_builtin("__builtin_memcpy", &ptr_ctype, 0, &ptr_ctype, &const_ptr_ctype, size_t_ctype, NULL); declare_builtin("__builtin_memmove", &ptr_ctype, 0, &ptr_ctype, &const_ptr_ctype, size_t_ctype, NULL); declare_builtin("__builtin_mempcpy", &ptr_ctype, 0, &ptr_ctype, &const_ptr_ctype, size_t_ctype, NULL); declare_builtin("__builtin_memset", &ptr_ctype, 0, &ptr_ctype, &int_ctype, size_t_ctype, NULL); declare_builtin("__builtin_nan", &double_ctype, 0, &const_string_ctype, NULL); declare_builtin("__builtin_nanf", &float_ctype, 0, &const_string_ctype, NULL); declare_builtin("__builtin_nanl", &ldouble_ctype, 0, &const_string_ctype, NULL); declare_builtin("__builtin_object_size", size_t_ctype, 0, &const_ptr_ctype, &int_ctype, NULL); declare_builtin("__builtin_parity", &int_ctype, 0, &uint_ctype, NULL); declare_builtin("__builtin_parityl", &int_ctype, 0, &ulong_ctype, NULL); declare_builtin("__builtin_parityll", &int_ctype, 0, &ullong_ctype, NULL); declare_builtin("__builtin_popcount", &int_ctype, 0, &uint_ctype, NULL); declare_builtin("__builtin_popcountl", &int_ctype, 0, &ulong_ctype, NULL); declare_builtin("__builtin_popcountll", &int_ctype, 0, &ullong_ctype, NULL); declare_builtin("__builtin_prefetch", &void_ctype, 1, &const_ptr_ctype, NULL); declare_builtin("__builtin_printf", &int_ctype, 1, &const_string_ctype, NULL); declare_builtin("__builtin_puts", &int_ctype, 0, &const_string_ctype, NULL); declare_builtin("__builtin_realloc", &ptr_ctype, 0, &ptr_ctype, size_t_ctype, NULL); declare_builtin("__builtin_return_address", &ptr_ctype, 0, &uint_ctype, NULL); declare_builtin("__builtin_rindex", &string_ctype, 0, &const_string_ctype, &int_ctype, NULL); declare_builtin("__builtin_sadd_overflow", &bool_ctype, 0, &int_ctype, &int_ctype, &int_ptr_ctype, NULL); declare_builtin("__builtin_saddl_overflow", &bool_ctype, 0, &long_ctype, &long_ctype, &long_ptr_ctype, NULL); declare_builtin("__builtin_saddll_overflow", &bool_ctype, 0, &llong_ctype, &llong_ctype, &llong_ptr_ctype, NULL); declare_builtin("__builtin_signbit", &int_ctype, 1, NULL); declare_builtin("__builtin_smul_overflow", &bool_ctype, 0, &int_ctype, &int_ctype, &int_ptr_ctype, NULL); declare_builtin("__builtin_smull_overflow", &bool_ctype, 0, &long_ctype, &long_ctype, &long_ptr_ctype, NULL); declare_builtin("__builtin_smulll_overflow", &bool_ctype, 0, &llong_ctype, &llong_ctype, &llong_ptr_ctype, NULL); declare_builtin("__builtin_snprintf", &int_ctype, 1, &string_ctype, size_t_ctype, &const_string_ctype, NULL); declare_builtin("__builtin_sprintf", &int_ctype, 1, &string_ctype, &const_string_ctype, NULL); declare_builtin("__builtin_ssub_overflow", &bool_ctype, 0, &int_ctype, &int_ctype, &int_ptr_ctype, NULL); declare_builtin("__builtin_ssubl_overflow", &bool_ctype, 0, &long_ctype, &long_ctype, &long_ptr_ctype, NULL); declare_builtin("__builtin_ssubll_overflow", &bool_ctype, 0, &llong_ctype, &llong_ctype, &llong_ptr_ctype, NULL); declare_builtin("__builtin_stpcpy", &string_ctype, 0, &const_string_ctype, &const_string_ctype, NULL); declare_builtin("__builtin_stpncpy", &string_ctype, 0, &const_string_ctype, &const_string_ctype, size_t_ctype, NULL); declare_builtin("__builtin_strcasecmp", &int_ctype, 0, &const_string_ctype, &const_string_ctype, NULL); declare_builtin("__builtin_strcasestr", &string_ctype, 0, &const_string_ctype, &const_string_ctype, NULL); declare_builtin("__builtin_strcat", &string_ctype, 0, &string_ctype, &const_string_ctype, NULL); declare_builtin("__builtin_strchr", &string_ctype, 0, &const_string_ctype, &int_ctype, NULL); declare_builtin("__builtin_strcmp", &int_ctype, 0, &const_string_ctype, &const_string_ctype, NULL); declare_builtin("__builtin_strcpy", &string_ctype, 0, &string_ctype, &const_string_ctype, NULL); declare_builtin("__builtin_strcspn", size_t_ctype, 0, &const_string_ctype, &const_string_ctype, NULL); declare_builtin("__builtin_strdup", &string_ctype, 0, &const_string_ctype, NULL); declare_builtin("__builtin_strlen", size_t_ctype, 0, &const_string_ctype, NULL); declare_builtin("__builtin_strncasecmp", &int_ctype, 0, &const_string_ctype, &const_string_ctype, size_t_ctype, NULL); declare_builtin("__builtin_strncat", &string_ctype, 0, &string_ctype, &const_string_ctype, size_t_ctype, NULL); declare_builtin("__builtin_strncmp", &int_ctype, 0, &const_string_ctype, &const_string_ctype, size_t_ctype, NULL); declare_builtin("__builtin_strncpy", &string_ctype, 0, &string_ctype, &const_string_ctype, size_t_ctype, NULL); declare_builtin("__builtin_strndup", &string_ctype, 0, &const_string_ctype, size_t_ctype, NULL); declare_builtin("__builtin_strnstr", &string_ctype, 0, &const_string_ctype, &const_string_ctype, size_t_ctype, NULL); declare_builtin("__builtin_strpbrk", &string_ctype, 0, &const_string_ctype, &const_string_ctype, NULL); declare_builtin("__builtin_strrchr", &string_ctype, 0, &const_string_ctype, &int_ctype, NULL); declare_builtin("__builtin_strspn", size_t_ctype, 0, &const_string_ctype, &const_string_ctype, NULL); declare_builtin("__builtin_strstr", &string_ctype, 0, &const_string_ctype, &const_string_ctype, NULL); declare_builtin("__builtin_trap", &void_ctype, 0, NULL); declare_builtin("__builtin_uadd_overflow", &bool_ctype, 0, &uint_ctype, &uint_ctype, &uint_ptr_ctype, NULL); declare_builtin("__builtin_uaddl_overflow", &bool_ctype, 0, &ulong_ctype, &ulong_ctype, &ulong_ptr_ctype, NULL); declare_builtin("__builtin_uaddll_overflow", &bool_ctype, 0, &ullong_ctype, &ullong_ctype, &ullong_ptr_ctype, NULL); declare_builtin("__builtin_umul_overflow", &bool_ctype, 0, &uint_ctype, &uint_ctype, &uint_ptr_ctype, NULL); declare_builtin("__builtin_umull_overflow", &bool_ctype, 0, &ulong_ctype, &ulong_ctype, &ulong_ptr_ctype, NULL); declare_builtin("__builtin_umulll_overflow", &bool_ctype, 0, &ullong_ctype, &ullong_ctype, &ullong_ptr_ctype, NULL); declare_builtin("__builtin_unreachable", &void_ctype, 0, NULL); declare_builtin("__builtin_usub_overflow", &bool_ctype, 0, &uint_ctype, &uint_ctype, &uint_ptr_ctype, NULL); declare_builtin("__builtin_usubl_overflow", &bool_ctype, 0, &ulong_ctype, &ulong_ctype, &ulong_ptr_ctype, NULL); declare_builtin("__builtin_usubll_overflow", &bool_ctype, 0, &ullong_ctype, &ullong_ctype, &ullong_ptr_ctype, NULL); declare_builtin("__builtin_va_arg_pack_len", size_t_ctype, 0, NULL); declare_builtin("__builtin_vprintf", &int_ctype, 0, &const_string_ctype, va_list_ctype, NULL); declare_builtin("__builtin_vsnprintf", &int_ctype, 0, &string_ctype, size_t_ctype, &const_string_ctype, va_list_ctype, NULL); declare_builtin("__builtin_vsprintf", &int_ctype, 0, &string_ctype, &const_string_ctype, va_list_ctype, NULL); declare_builtin("__builtin___memcpy_chk", &ptr_ctype, 0, &ptr_ctype, &const_ptr_ctype, size_t_ctype, size_t_ctype, NULL); declare_builtin("__builtin___memmove_chk", &ptr_ctype, 0, &ptr_ctype, &const_ptr_ctype, size_t_ctype, size_t_ctype, NULL); declare_builtin("__builtin___mempcpy_chk", &ptr_ctype, 0, &ptr_ctype, &const_ptr_ctype, size_t_ctype, size_t_ctype, NULL); declare_builtin("__builtin___memset_chk", &ptr_ctype, 0, &ptr_ctype, &int_ctype, size_t_ctype, size_t_ctype, NULL); declare_builtin("__builtin___snprintf_chk", &int_ctype, 1, &string_ctype, size_t_ctype, &int_ctype , size_t_ctype, &const_string_ctype, NULL); declare_builtin("__builtin___sprintf_chk", &int_ctype, 1, &string_ctype, &int_ctype, size_t_ctype, &const_string_ctype, NULL); declare_builtin("__builtin___stpcpy_chk", &string_ctype, 0, &string_ctype, &const_string_ctype, size_t_ctype, NULL); declare_builtin("__builtin___strcat_chk", &string_ctype, 0, &string_ctype, &const_string_ctype, size_t_ctype, NULL); declare_builtin("__builtin___strcpy_chk", &string_ctype, 0, &string_ctype, &const_string_ctype, size_t_ctype, NULL); declare_builtin("__builtin___strncat_chk", &string_ctype, 0, &string_ctype, &const_string_ctype, size_t_ctype, size_t_ctype, NULL); declare_builtin("__builtin___strncpy_chk", &string_ctype, 0, &string_ctype, &const_string_ctype, size_t_ctype, size_t_ctype, NULL); declare_builtin("__builtin___vsnprintf_chk", &int_ctype, 0, &string_ctype, size_t_ctype, &int_ctype, size_t_ctype, &const_string_ctype, va_list_ctype, NULL); declare_builtin("__builtin___vsprintf_chk", &int_ctype, 0, &string_ctype, &int_ctype, size_t_ctype, &const_string_ctype, va_list_ctype, NULL); declare_builtin("__sync_add_and_fetch", &int_ctype, 1, &ptr_ctype, NULL); declare_builtin("__sync_and_and_fetch", &int_ctype, 1, &ptr_ctype, NULL); declare_builtin("__sync_bool_compare_and_swap", &int_ctype, 1, &ptr_ctype, NULL); declare_builtin("__sync_fetch_and_add", &int_ctype, 1, &ptr_ctype, NULL); declare_builtin("__sync_fetch_and_and", &int_ctype, 1, &ptr_ctype, NULL); declare_builtin("__sync_fetch_and_nand", &int_ctype, 1, &ptr_ctype, NULL); declare_builtin("__sync_fetch_and_or", &int_ctype, 1, &ptr_ctype, NULL); declare_builtin("__sync_fetch_and_sub", &int_ctype, 1, &ptr_ctype, NULL); declare_builtin("__sync_fetch_and_xor", &int_ctype, 1, &ptr_ctype, NULL); declare_builtin("__sync_lock_release", &void_ctype, 1, &ptr_ctype, NULL); declare_builtin("__sync_lock_test_and_set", &int_ctype, 1, &ptr_ctype, NULL); declare_builtin("__sync_nand_and_fetch", &int_ctype, 1, &ptr_ctype, NULL); declare_builtin("__sync_or_and_fetch", &int_ctype, 1, &ptr_ctype, NULL); declare_builtin("__sync_sub_and_fetch", &int_ctype, 1, &ptr_ctype, NULL); declare_builtin("__sync_synchronize", &void_ctype, 0, NULL); declare_builtin("__sync_val_compare_and_swap", &int_ctype, 1, &ptr_ctype, NULL); declare_builtin("__sync_xor_and_fetch", &int_ctype, 1, &ptr_ctype, NULL); // Blackfin-specific stuff declare_builtin("__builtin_bfin_csync", &void_ctype, 0, NULL); declare_builtin("__builtin_bfin_ssync", &void_ctype, 0, NULL); declare_builtin("__builtin_bfin_norm_fr1x32", &int_ctype, 0, &int_ctype, NULL); } sparse-0.6.1/c2xml.c000066400000000000000000000172421355073133200142140ustar00rootroot00000000000000/* * 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.1/cgcc000077500000000000000000000304001355073133200136370ustar00rootroot00000000000000#!/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|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 0; } # ----------------------------------------------------------------------------- # Simple arg-quoting function. Just adds backslashes when needed. sub quote_arg { my ($arg) = @_; return "''" if $arg eq ''; return join ('', map { m|^[-a-zA-Z0-9._/,=]+$| ? $_ : "\\" . $_; } (split (//, $arg))); } # ----------------------------------------------------------------------------- sub integer_types { my ($char,@dummy) = @_; my %pow2m1 = (8 => '127', 16 => '32767', 32 => '2147483647', 64 => '9223372036854775807', 128 => '170141183460469231731687303715884105727', ); my @types = (['SCHAR',''], ['SHRT',''], ['INT',''], ['LONG','L'], ['LONG_LONG','LL'], ['LONG_LONG_LONG','LLL']); my $result = " -D__CHAR_BIT__=$char"; while (@types && @_) { my $bits = shift @_; my ($name,$suffix) = @{ shift @types }; die "$0: weird number of bits." unless exists $pow2m1{$bits}; $result .= " -D__${name}_MAX__=" . $pow2m1{$bits} . $suffix; } return $result; } # ----------------------------------------------------------------------------- sub float_types { my ($has_inf,$has_qnan,$dec_dig,@bitsizes) = @_; my $result = " -D__FLT_RADIX__=2"; $result .= " -D__FINITE_MATH_ONLY__=" . ($has_inf || $has_qnan ? '0' : '1'); $result .= " -D__DECIMAL_DIG__=$dec_dig"; my %constants = (24 => { 'MIN' => '1.17549435e-38', 'MAX' => '3.40282347e+38', 'EPSILON' => '1.19209290e-7', 'DENORM_MIN' => '1.40129846e-45', }, 53 => { 'MIN' => '2.2250738585072014e-308', 'MAX' => '1.7976931348623157e+308', 'EPSILON' => '2.2204460492503131e-16', 'DENORM_MIN' => '4.9406564584124654e-324', }, 64 => { 'MIN' => '3.36210314311209350626e-4932', 'MAX' => '1.18973149535723176502e+4932', 'EPSILON' => '1.08420217248550443401e-19', 'DENORM_MIN' => '3.64519953188247460253e-4951', }, 113 => { 'MIN' => '3.36210314311209350626267781732175260e-4932', 'MAX' => '1.18973149535723176508575932662800702e+4932', 'EPSILON' => '1.92592994438723585305597794258492732e-34', 'DENORM_MIN' => '6.47517511943802511092443895822764655e-4966', }, ); my @types = (['FLT','F'], ['DBL',''], ['LDBL','L']); while (@types) { my ($mant_bits,$exp_bits) = @{ shift @bitsizes }; my ($name,$suffix) = @{ shift @types }; my $h = $constants{$mant_bits}; die "$0: weird number of mantissa bits." unless $h; my $mant_dig = int (($mant_bits - 1) * log (2) / log (10)); my $max_exp = 1 << ($exp_bits - 1); my $min_exp = 3 - $max_exp; my $max_10_exp = int ($max_exp * log (2) / log (10)); my $min_10_exp = -int (-$min_exp * log (2) / log (10)); $result .= " -D__${name}_MANT_DIG__=$mant_bits"; $result .= " -D__${name}_DIG__=$mant_dig"; $result .= " -D__${name}_MIN_EXP__='($min_exp)'"; $result .= " -D__${name}_MAX_EXP__=$max_exp"; $result .= " -D__${name}_MIN_10_EXP__='($min_10_exp)'"; $result .= " -D__${name}_MAX_10_EXP__=$max_10_exp"; $result .= " -D__${name}_HAS_INFINITY__=" . ($has_inf ? '1' : '0'); $result .= " -D__${name}_HAS_QUIET_NAN__=" . ($has_qnan ? '1' : '0');; foreach my $inf (sort keys %$h) { $result .= " -D__${name}_${inf}__=" . $h->{$inf} . $suffix; } } return $result; } # ----------------------------------------------------------------------------- sub define_size_t { my ($text) = @_; # We have to undef in order to override check's internal definition. return ' -U__SIZE_TYPE__ ' . "e_arg ("-D__SIZE_TYPE__=$text"); } # ----------------------------------------------------------------------------- sub add_specs { my ($spec) = @_; if ($spec eq 'sunos') { return &add_specs ('unix') . ' -D__sun__=1 -D__sun=1 -Dsun=1' . ' -D__svr4__=1 -DSVR4=1' . ' -D__STDC__=0' . ' -D_REENTRANT' . ' -D_SOLARIS_THREADS' . ' -DNULL="((void *)0)"'; } elsif ($spec eq 'linux') { return &add_specs ('unix') . ' -D__linux__=1 -D__linux=1 -Dlinux=linux'; } elsif ($spec eq 'gnu/kfreebsd') { return &add_specs ('unix') . ' -D__FreeBSD_kernel__=1'; } elsif ($spec eq 'openbsd') { return &add_specs ('unix') . ' -D__OpenBSD__=1'; } elsif ($spec eq 'freebsd') { return &add_specs ('unix') . ' -D__FreeBSD__=1'; } elsif ($spec eq 'netbsd') { return &add_specs ('unix') . ' -D__NetBSD__=1'; } elsif ($spec eq 'darwin') { return ' -D__APPLE__=1 -D__APPLE_CC__=1 -D__MACH__=1'; } 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 &add_specs ('unix') . ' -D__CYGWIN__=1 -D__CYGWIN32__=1' . " -D'_cdecl=__attribute__((__cdecl__))'" . " -D'__cdecl=__attribute__((__cdecl__))'" . " -D'_stdcall=__attribute__((__stdcall__))'" . " -D'__stdcall=__attribute__((__stdcall__))'" . " -D'_fastcall=__attribute__((__fastcall__))'" . " -D'__fastcall=__attribute__((__fastcall__))'" . " -D'__declspec(x)=__attribute__((x))'"; } elsif ($spec eq 'i386') { return ( &float_types (1, 1, 21, [24,8], [53,11], [64,15])); } elsif ($spec eq 'sparc') { return ( &integer_types (8, 16, 32, $m64 ? 64 : 32, 64) . &float_types (1, 1, 33, [24,8], [53,11], [113,15]) . &define_size_t ($m64 ? "long unsigned int" : "unsigned int") . ' -D__SIZEOF_POINTER__=' . ($m64 ? '8' : '4')); } elsif ($spec eq 'sparc64') { return ( &integer_types (8, 16, 32, 64, 64, 128) . &float_types (1, 1, 33, [24,8], [53,11], [113,15]) . &define_size_t ("long unsigned int") . ' -D__SIZEOF_POINTER__=8'); } elsif ($spec eq 'x86_64') { return &float_types (1, 1, 33, [24,8], [53,11], [113,15]); } elsif ($spec eq 'ppc') { return (' -D_BIG_ENDIAN -D_STRING_ARCH_unaligned=1' . &integer_types (8, 16, 32, $m64 ? 64 : 32, 64) . &float_types (1, 1, 21, [24,8], [53,11], [113,15]) . &define_size_t ($m64 ? "long unsigned int" : "unsigned int") . ' -D__SIZEOF_POINTER__=' . ($m64 ? '8' : '4')); } elsif ($spec eq 'ppc64') { return (' -D_STRING_ARCH_unaligned=1 -m64' . &float_types (1, 1, 21, [24,8], [53,11], [113,15])); } elsif ($spec eq 'ppc64+be') { return &add_specs ('ppc64') . ' -mbig-endian -D_CALL_ELF=1'; } elsif ($spec eq 'ppc64+le') { return &add_specs ('ppc64') . ' -mlittle-endian -D_CALL_ELF=2'; } elsif ($spec eq 's390x') { return (' -D_BIG_ENDIAN' . &integer_types (8, 16, 32, $m64 ? 64 : 32, 64) . &float_types (1, 1, 36, [24,8], [53,11], [113,15]) . &define_size_t ("long unsigned int") . ' -D__SIZEOF_POINTER__=' . ($m64 ? '8' : '4')); } elsif ($spec eq 'arm') { return (' -m32' . &float_types (1, 1, 36, [24,8], [53,11], [53, 11])); } elsif ($spec eq 'arm+hf') { return &add_specs ('arm') . ' -D__ARM_PCS_VFP=1'; } elsif ($spec eq 'aarch64') { return (' -m64' . &float_types (1, 1, 36, [24,8], [53,11], [113,15])); } elsif ($spec eq 'host_os_specs') { my $os = `uname -s`; chomp $os; return &add_specs (lc $os); } elsif ($spec eq 'host_arch_specs') { my $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 ('ppc64+le'); } 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 ('ppc64+be'); } elsif ($arch =~ /^(ppc64le)$/i) { return &add_specs ('ppc64+le'); } 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.1/cgcc.1000066400000000000000000000020461355073133200140000ustar00rootroot00000000000000.\" 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.1/char.c000066400000000000000000000063751355073133200141110ustar00rootroot00000000000000#include #include "target.h" #include "lib.h" #include "allocate.h" #include "token.h" #include "expression.h" #include "char.h" static const char *parse_escape(const char *p, unsigned *val, const char *end, int bits, struct position pos) { unsigned c = *p++; unsigned d; if (c != '\\') { *val = c; return p; } c = *p++; switch (c) { case 'a': c = '\a'; break; case 'b': c = '\b'; break; case 't': c = '\t'; break; case 'n': c = '\n'; break; case 'v': c = '\v'; break; case 'f': c = '\f'; break; case 'r': c = '\r'; break; case 'e': c = '\e'; break; case 'x': { unsigned mask = -(1U << (bits - 4)); for (c = 0; p < end; c = (c << 4) + d) { d = hexval(*p); if (d > 16) break; p++; if (c & mask) { warning(pos, "hex escape sequence out of range"); mask = 0; } } break; } case '0'...'7': { if (p + 2 < end) end = p + 2; c -= '0'; while (p < end && (d = *p - '0') < 8) { c = (c << 3) + d; p++; } if ((c & 0400) && bits < 9) warning(pos, "octal escape sequence out of range"); break; } default: /* everything else is left as is */ warning(pos, "unknown escape sequence: '\\%c'", c); break; case '\\': case '\'': case '"': case '?': break; /* those are legal, so no warnings */ } *val = c & ~((~0U << (bits - 1)) << 1); return p; } void get_char_constant(struct token *token, unsigned long long *val) { const char *p = token->embedded, *end; unsigned v; int type = token_type(token); switch (type) { case TOKEN_CHAR: case TOKEN_WIDE_CHAR: p = token->string->data; end = p + token->string->length - 1; break; case TOKEN_CHAR_EMBEDDED_0 ... TOKEN_CHAR_EMBEDDED_3: end = p + type - TOKEN_CHAR; break; default: end = p + type - TOKEN_WIDE_CHAR; } p = parse_escape(p, &v, end, type < TOKEN_WIDE_CHAR ? bits_in_char : 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.1/char.h000066400000000000000000000002241355073133200141010ustar00rootroot00000000000000extern void get_char_constant(struct token *, unsigned long long *); extern struct token *get_string_constant(struct token *, struct expression *); sparse-0.6.1/compat-bsd.c000066400000000000000000000024771355073133200152240ustar00rootroot00000000000000/* * BSD Compatibility functions * * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include "lib.h" #include "allocate.h" #include "token.h" #include "compat/mmap-blob.c" long double string_to_ld(const char *nptr, char **endptr) { return strtod(nptr, endptr); } sparse-0.6.1/compat-cygwin.c000066400000000000000000000033231355073133200157430ustar00rootroot00000000000000/* * 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.1/compat-linux.c000066400000000000000000000001671355073133200156050ustar00rootroot00000000000000#define _GNU_SOURCE #include "lib.h" #include "allocate.h" #include "compat/mmap-blob.c" #include "compat/strtold.c" sparse-0.6.1/compat-mingw.c000066400000000000000000000031021355073133200155570ustar00rootroot00000000000000/* * 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.1/compat-solaris.c000066400000000000000000000012551355073133200161210ustar00rootroot00000000000000#include "lib.h" #include "allocate.h" #include "compat/mmap-blob.c" #include #include #include long double string_to_ld(const char *str, char **endptr) { long double res; decimal_record dr; enum decimal_string_form form; decimal_mode dm; fp_exception_field_type excp; char *echar; string_to_decimal ((char **)&str, INT_MAX, 0, &dr, &form, &echar); if (endptr) *endptr = (char *)str; if (form == invalid_form) { errno = EINVAL; return 0.0; } dm.rd = fp_nearest; decimal_to_quadruple (&res, &dm, &dr, &excp); if (excp & ((1 << fp_overflow) | (1 << fp_underflow))) errno = ERANGE; return res; } sparse-0.6.1/compat.h000066400000000000000000000012551355073133200144540ustar00rootroot00000000000000#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.1/compat/000077500000000000000000000000001355073133200143005ustar00rootroot00000000000000sparse-0.6.1/compat/bswap.h000066400000000000000000000023021355073133200155620ustar00rootroot00000000000000#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.1/compat/mmap-blob.c000066400000000000000000000015251355073133200163150ustar00rootroot00000000000000#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.1/compat/strtold.c000066400000000000000000000001621355073133200161360ustar00rootroot00000000000000#include long double string_to_ld(const char *nptr, char **endptr) { return strtold(nptr, endptr); } sparse-0.6.1/compile-i386.c000066400000000000000000001554451355073133200153160ustar00rootroot00000000000000/* * 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" #include "version.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.1/compile.c000066400000000000000000000046171355073133200146210ustar00rootroot00000000000000/* * 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.1/compile.h000066400000000000000000000003071355073133200146160ustar00rootroot00000000000000#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.1/cse.c000066400000000000000000000203041355073133200137320ustar00rootroot00000000000000/* * 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_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_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 void add_instruction_to_end(struct instruction *insn, struct basic_block *bb) { struct instruction *br = delete_last_instruction(&bb->insns); insn->bb = bb; add_instruction(&bb->insns, insn); add_instruction(&bb->insns, br); } static struct instruction * try_to_cse(struct entrypoint *ep, struct instruction *i1, struct instruction *i2) { struct basic_block *b1, *b2, *common; /* * OK, i1 and i2 are the same instruction, modulo "target". * We should now see if we can combine them. */ b1 = i1->bb; b2 = i2->bb; /* * Currently we only handle the uninteresting degenerate case where * the CSE is inside one basic-block. */ if (b1 == b2) { struct instruction *insn; FOR_EACH_PTR(b1->insns, insn) { if (insn == i1) return cse_one_instruction(i2, i1); if (insn == i2) return cse_one_instruction(i1, i2); } END_FOR_EACH_PTR(insn); warning(b1->pos, "Whaa? unable to find CSE instructions"); return i1; } if (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); add_instruction_to_end(i1, common); } 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.1/cse.h000066400000000000000000000002601355073133200137360ustar00rootroot00000000000000#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.1/ctags.c000066400000000000000000000131411355073133200142620ustar00rootroot00000000000000/* * 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.1/dissect.c000066400000000000000000000342441355073133200146260ustar00rootroot00000000000000/* * sparse/dissect.c * * Started by Oleg Nesterov * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include "dissect.h" #define U_VOID 0x00 #define U_SELF ((1 << U_SHIFT) - 1) #define U_MASK (U_R_VAL | U_W_VAL | U_R_AOF) #define DO_LIST(l__, p__, expr__) \ do { \ typeof(l__->list[0]) p__; \ FOR_EACH_PTR(l__, p__) \ expr__; \ END_FOR_EACH_PTR(p__); \ } while (0) #define DO_2_LIST(l1__,l2__, p1__,p2__, expr__) \ do { \ typeof(l1__->list[0]) p1__; \ typeof(l2__->list[0]) p2__; \ PREPARE_PTR_LIST(l1__, p1__); \ FOR_EACH_PTR(l2__, p2__) \ expr__; \ NEXT_PTR_LIST(p1__); \ END_FOR_EACH_PTR(p2__); \ FINISH_PTR_LIST(p1__); \ } while (0) typedef unsigned usage_t; static struct reporter *reporter; static struct symbol *return_type; static void do_sym_list(struct symbol_list *list); static struct symbol *base_type(struct symbol *sym), *do_initializer(struct symbol *type, struct expression *expr), *do_expression(usage_t mode, struct expression *expr), *do_statement(usage_t mode, struct statement *stmt); static inline int is_ptr(struct symbol *type) { return type->type == SYM_PTR || type->type == SYM_ARRAY; } static inline usage_t u_rval(usage_t mode) { return mode & (U_R_VAL | (U_MASK << U_SHIFT)) ? U_R_VAL : 0; } static inline usage_t u_addr(usage_t mode) { return mode = mode & U_MASK ? U_R_AOF | (mode & U_W_AOF) : 0; } static usage_t u_lval(struct symbol *type) { int wptr = is_ptr(type) && !(type->ctype.modifiers & MOD_CONST); return wptr || type == &bad_ctype ? U_W_AOF | U_R_VAL : U_R_VAL; } static usage_t fix_mode(struct symbol *type, usage_t mode) { mode &= (U_SELF | (U_SELF << U_SHIFT)); switch (type->type) { case SYM_BASETYPE: if (!type->ctype.base_type) break; case SYM_ENUM: case SYM_BITFIELD: if (mode & U_MASK) mode &= U_SELF; default: break; case SYM_FN: if (mode & U_R_VAL) mode |= U_R_AOF; mode &= ~(U_R_VAL | U_W_AOF); break; case SYM_ARRAY: if (mode & (U_MASK << U_SHIFT)) mode >>= U_SHIFT; else if (mode != U_W_VAL) mode = u_addr(mode); } if (!(mode & U_R_AOF)) mode &= ~U_W_AOF; return mode; } static inline struct symbol *no_member(struct ident *name) { static struct symbol sym = { .type = SYM_BAD, }; sym.ctype.base_type = &bad_ctype; sym.ident = name; return &sym; } static struct symbol *report_member(usage_t mode, struct position *pos, struct symbol *type, struct symbol *mem) { struct symbol *ret = mem->ctype.base_type; if (reporter->r_member) reporter->r_member(fix_mode(ret, mode), pos, type, mem); return ret; } static void report_implicit(usage_t mode, struct position *pos, struct symbol *type) { if (type->type != SYM_STRUCT && type->type != SYM_UNION) return; if (!reporter->r_member) return; if (type->ident != NULL) reporter->r_member(mode, pos, type, NULL); DO_LIST(type->symbol_list, mem, report_implicit(mode, pos, base_type(mem))); } static inline struct symbol *expr_symbol(struct expression *expr) { struct symbol *sym = expr->symbol; if (!sym) { sym = lookup_symbol(expr->symbol_name, NS_SYMBOL); if (!sym) { sym = alloc_symbol(expr->pos, SYM_BAD); bind_symbol(sym, expr->symbol_name, NS_SYMBOL); sym->ctype.modifiers = MOD_EXTERN; } } if (!sym->ctype.base_type) sym->ctype.base_type = &bad_ctype; return sym; } static struct symbol *report_symbol(usage_t mode, struct expression *expr) { struct symbol *sym = expr_symbol(expr); struct symbol *ret = base_type(sym); if (0 && ret->type == SYM_ENUM) return report_member(mode, &expr->pos, ret, expr->symbol); if (reporter->r_symbol) reporter->r_symbol(fix_mode(ret, mode), &expr->pos, sym); return ret; } static inline struct ident *mk_name(struct ident *root, struct ident *node) { char name[256]; snprintf(name, sizeof(name), "%.*s:%.*s", root ? root->len : 0, root ? root->name : "", node ? node->len : 0, node ? node->name : ""); return built_in_ident(name); } static void examine_sym_node(struct symbol *node, struct ident *root) { struct symbol *base; struct ident *name; if (node->examined) return; node->examined = 1; name = node->ident; while ((base = node->ctype.base_type) != NULL) switch (base->type) { case SYM_TYPEOF: node->ctype.base_type = do_expression(U_VOID, base->initializer); break; case SYM_ARRAY: do_expression(U_R_VAL, base->array_size); case SYM_PTR: case SYM_FN: node = base; break; case SYM_STRUCT: case SYM_UNION: //case SYM_ENUM: if (base->evaluated) return; if (!base->symbol_list) return; base->evaluated = 1; if (!base->ident && name) base->ident = mk_name(root, name); if (base->ident && reporter->r_symdef) reporter->r_symdef(base); DO_LIST(base->symbol_list, mem, examine_sym_node(mem, base->ident ?: root)); default: return; } } static struct symbol *base_type(struct symbol *sym) { if (!sym) return &bad_ctype; if (sym->type == SYM_NODE) examine_sym_node(sym, NULL); return sym->ctype.base_type // builtin_fn_type ?: &bad_ctype; } static struct symbol *__lookup_member(struct symbol *type, struct ident *name, int *p_addr) { struct symbol *node; int addr = 0; FOR_EACH_PTR(type->symbol_list, node) if (!name) { if (addr == *p_addr) return node; } else if (node->ident == NULL) { node = __lookup_member(node->ctype.base_type, name, NULL); if (node) goto found; } else if (node->ident == name) { found: if (p_addr) *p_addr = addr; return node; } addr++; END_FOR_EACH_PTR(node); return NULL; } static struct symbol *lookup_member(struct symbol *type, struct ident *name, int *addr) { return __lookup_member(type, name, addr) ?: no_member(name); } static struct expression *peek_preop(struct expression *expr, int op) { do { if (expr->type != EXPR_PREOP) break; if (expr->op == op) return expr->unop; if (expr->op == '(') expr = expr->unop; else break; } while (expr); return NULL; } static struct symbol *do_expression(usage_t mode, struct expression *expr) { struct symbol *ret = &int_ctype; again: if (expr) switch (expr->type) { default: warning(expr->pos, "bad expr->type: %d", expr->type); case EXPR_TYPE: // [struct T]; Why ??? case EXPR_VALUE: case EXPR_FVALUE: break; case EXPR_LABEL: ret = &label_ctype; break; case EXPR_STRING: ret = &string_ctype; break; case EXPR_STATEMENT: ret = do_statement(mode, expr->statement); break; case EXPR_SIZEOF: case EXPR_ALIGNOF: case EXPR_PTRSIZEOF: do_expression(U_VOID, expr->cast_expression); break; case EXPR_COMMA: do_expression(U_VOID, expr->left); ret = do_expression(mode, expr->right); break; case EXPR_CAST: case EXPR_FORCE_CAST: //case EXPR_IMPLIED_CAST: ret = base_type(expr->cast_type); do_initializer(ret, expr->cast_expression); break; case EXPR_COMPARE: case EXPR_LOGICAL: mode = u_rval(mode); do_expression(mode, expr->left); do_expression(mode, expr->right); break; case EXPR_CONDITIONAL: //case EXPR_SELECT: do_expression(expr->cond_true ? U_R_VAL : U_R_VAL | mode, expr->conditional); ret = do_expression(mode, expr->cond_true); ret = do_expression(mode, expr->cond_false); break; case EXPR_CALL: ret = do_expression(U_R_PTR, expr->fn); if (is_ptr(ret)) ret = ret->ctype.base_type; DO_2_LIST(ret->arguments, expr->args, arg, val, do_expression(u_lval(base_type(arg)), val)); ret = ret->type == SYM_FN ? base_type(ret) : &bad_ctype; break; case EXPR_ASSIGNMENT: mode |= U_W_VAL | U_R_VAL; if (expr->op == '=') mode &= ~U_R_VAL; ret = do_expression(mode, expr->left); report_implicit(mode, &expr->pos, ret); mode = expr->op == '=' ? u_lval(ret) : U_R_VAL; do_expression(mode, expr->right); break; case EXPR_BINOP: { struct symbol *l, *r; mode |= u_rval(mode); l = do_expression(mode, expr->left); r = do_expression(mode, expr->right); if (expr->op != '+' && expr->op != '-') ; else if (!is_ptr_type(r)) ret = l; else if (!is_ptr_type(l)) ret = r; } break; case EXPR_PREOP: case EXPR_POSTOP: { struct expression *unop = expr->unop; switch (expr->op) { case SPECIAL_INCREMENT: case SPECIAL_DECREMENT: mode |= U_W_VAL | U_R_VAL; default: mode |= u_rval(mode); case '(': ret = do_expression(mode, unop); break; case '&': if ((expr = peek_preop(unop, '*'))) goto again; ret = alloc_symbol(unop->pos, SYM_PTR); ret->ctype.base_type = do_expression(u_addr(mode), unop); break; case '*': if ((expr = peek_preop(unop, '&'))) goto again; if (mode & (U_MASK << U_SHIFT)) mode |= U_R_VAL; mode <<= U_SHIFT; if (mode & (U_R_AOF << U_SHIFT)) mode |= U_R_VAL; if (mode & (U_W_VAL << U_SHIFT)) mode |= U_W_AOF; ret = do_expression(mode, unop); ret = is_ptr(ret) ? base_type(ret) : &bad_ctype; } } break; case EXPR_DEREF: { struct symbol *p_type; usage_t p_mode; p_mode = mode & U_SELF; if (!(mode & U_MASK) && (mode & (U_MASK << U_SHIFT))) p_mode = U_R_VAL; p_type = do_expression(p_mode, expr->deref); ret = report_member(mode, &expr->pos, p_type, lookup_member(p_type, expr->member, NULL)); } break; case EXPR_OFFSETOF: { struct symbol *in = base_type(expr->in); do { if (expr->op == '.') { in = report_member(U_VOID, &expr->pos, in, lookup_member(in, expr->ident, NULL)); } else { do_expression(U_R_VAL, expr->index); in = in->ctype.base_type; } } while ((expr = expr->down)); } break; case EXPR_SYMBOL: ret = report_symbol(mode, expr); } return ret; } static void do_asm_xputs(usage_t mode, struct 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: do_expression(u_lval(return_type), stmt->expression); break; case STMT_ASM: do_expression(U_R_VAL, stmt->asm_string); do_asm_xputs(U_W_VAL, stmt->asm_outputs); do_asm_xputs(U_R_VAL, stmt->asm_inputs); break; case STMT_COMPOUND: { int count; count = statement_list_size(stmt->stmts); DO_LIST(stmt->stmts, st, ret = do_statement(--count ? U_VOID : mode, st)); } break; case STMT_ITERATOR: do_sym_list(stmt->iterator_syms); do_statement(U_VOID, stmt->iterator_pre_statement); do_expression(U_R_VAL, stmt->iterator_pre_condition); do_statement(U_VOID, stmt->iterator_post_statement); do_statement(U_VOID, stmt->iterator_statement); do_expression(U_R_VAL, stmt->iterator_post_condition); break; case STMT_IF: do_expression(U_R_VAL, stmt->if_conditional); do_statement(U_VOID, stmt->if_true); do_statement(U_VOID, stmt->if_false); break; case STMT_SWITCH: do_expression(U_R_VAL, stmt->switch_expression); do_statement(U_VOID, stmt->switch_statement); break; case STMT_CASE: do_expression(U_R_VAL, stmt->case_expression); do_expression(U_R_VAL, stmt->case_to); do_statement(U_VOID, stmt->case_statement); break; case STMT_GOTO: do_expression(U_R_PTR, stmt->goto_expression); break; case STMT_LABEL: do_statement(mode, stmt->label_statement); } return ret; } static struct symbol *do_initializer(struct symbol *type, struct expression *expr) { struct symbol *m_type; struct expression *m_expr; int m_addr; if (expr) switch (expr->type) { default: do_expression(u_lval(type), expr); break; case EXPR_INDEX: do_initializer(base_type(type), expr->idx_expression); break; case EXPR_INITIALIZER: m_addr = 0; FOR_EACH_PTR(expr->expr_list, m_expr) { if (type->type == SYM_ARRAY) { m_type = base_type(type); if (m_expr->type == EXPR_INDEX) m_expr = m_expr->idx_expression; } else { int *m_atop = &m_addr; m_type = type; while (m_expr->type == EXPR_IDENTIFIER) { m_type = report_member(U_W_VAL, &m_expr->pos, m_type, lookup_member(m_type, m_expr->expr_ident, m_atop)); m_expr = m_expr->ident_expression; m_atop = NULL; } if (m_atop) { m_type = report_member(U_W_VAL, &m_expr->pos, m_type, lookup_member(m_type, NULL, m_atop)); } if (m_expr->type != EXPR_INITIALIZER) report_implicit(U_W_VAL, &m_expr->pos, m_type); } do_initializer(m_type, m_expr); m_addr++; } END_FOR_EACH_PTR(m_expr); } return type; } static inline struct symbol *do_symbol(struct symbol *sym) { struct symbol *type; type = base_type(sym); if (reporter->r_symdef) reporter->r_symdef(sym); switch (type->type) { default: if (!sym->initializer) break; if (reporter->r_symbol) reporter->r_symbol(U_W_VAL, &sym->pos, sym); do_initializer(type, sym->initializer); break; case SYM_FN: do_sym_list(type->arguments); return_type = base_type(type); do_statement(U_VOID, sym->ctype.modifiers & MOD_INLINE ? type->inline_stmt : type->stmt); } return type; } static void do_sym_list(struct symbol_list *list) { DO_LIST(list, sym, do_symbol(sym)); } void dissect(struct symbol_list *list, struct reporter *rep) { reporter = rep; do_sym_list(list); } sparse-0.6.1/dissect.h000066400000000000000000000010631355073133200146240ustar00rootroot00000000000000#ifndef DISSECT_H #define DISSECT_H #include #include "parse.h" #include "expression.h" #define U_SHIFT 8 #define U_R_AOF 0x01 #define U_W_AOF 0x02 #define U_R_VAL 0x04 #define U_W_VAL 0x08 #define U_R_PTR (U_R_VAL << U_SHIFT) #define U_W_PTR (U_W_VAL << U_SHIFT) struct reporter { void (*r_symdef)(struct symbol *); void (*r_symbol)(unsigned, struct position *, struct symbol *); void (*r_member)(unsigned, struct position *, struct symbol *, struct symbol *); }; extern void dissect(struct symbol_list *, struct reporter *); #endif sparse-0.6.1/dominate.c000066400000000000000000000065561355073133200147750ustar00rootroot00000000000000// 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.1/dominate.h000066400000000000000000000004021355073133200147620ustar00rootroot00000000000000#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.1/evaluate.c000066400000000000000000002747361355073133200150120ustar00rootroot00000000000000/* * 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 *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; sym->array_size = alloc_const_expression(expr->pos, length); sym->bit_size = bytes_to_bits(length); sym->ctype.alignment = 1; sym->string = 1; sym->ctype.modifiers = MOD_STATIC; sym->ctype.base_type = array; sym->initializer = initstr; initstr->ctype = sym; initstr->string = expr->string; array->array_size = sym->array_size; array->bit_size = bytes_to_bits(length); array->ctype.alignment = 1; array->ctype.modifiers = MOD_STATIC; array->ctype.base_type = &char_ctype; addr->symbol = sym; addr->ctype = &lazy_ptr_ctype; 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 (mod & (MOD_CHAR | MOD_SHORT)) { if (mod & MOD_UNSIGNED) return &uint_ctype; return &int_ctype; } return type; } /* * integer part of usual arithmetic conversions: * integer promotions are applied * if left and right are identical, we are done * if signedness is the same, convert one with lower rank * unless unsigned argument has rank lower than signed one, convert the * signed one. * if signed argument is bigger than unsigned one, convert the unsigned. * otherwise, convert signed. * * Leaving aside the integer promotions, that is equivalent to * if identical, don't convert * if left is bigger than right, convert right * if right is bigger than left, convert right * otherwise, if signedness is the same, convert one with lower rank * otherwise convert the signed one. */ static struct symbol *bigger_int_type(struct symbol *left, struct symbol *right) { unsigned long lmod, rmod; left = integer_promotion(left); right = integer_promotion(right); if (left == right) goto left; if (left->bit_size > right->bit_size) goto left; if (right->bit_size > left->bit_size) goto right; lmod = left->ctype.modifiers; rmod = right->ctype.modifiers; if ((lmod ^ rmod) & MOD_UNSIGNED) { if (lmod & MOD_UNSIGNED) goto left; } else if ((lmod & ~rmod) & (MOD_LONG_ALL)) goto left; right: left = right; left: return left; } static int same_cast_type(struct symbol *orig, struct symbol *new) { return orig->bit_size == new->bit_size && orig->bit_offset == new->bit_offset; } static struct symbol *base_type(struct symbol *node, unsigned long *modp, 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. We often want to try to move the * cast down, because the ops involved may have been * implicitly cast up, and we can get rid of the casts * early. */ static struct expression * cast_to(struct expression *old, struct symbol *type) { struct expression *expr; warn_for_different_enum_types (old->pos, old->ctype, type); if (old->ctype != &null_ctype && is_same_type(old, type)) return old; /* * See if we can simplify the op. Move the cast down. */ switch (old->type) { case EXPR_PREOP: if (old->ctype->bit_size < type->bit_size) break; if (old->op == '~') { old->ctype = type; old->unop = cast_to(old->unop, type); return old; } break; case EXPR_IMPLIED_CAST: warn_for_different_enum_types(old->pos, old->ctype, type); if (old->ctype->bit_size >= type->bit_size) { struct expression *orig = old->cast_expression; if (same_cast_type(orig->ctype, type)) return orig; if (old->ctype->bit_offset == type->bit_offset) { old->ctype = type; old->cast_type = type; return old; } } break; default: /* nothing */; } expr = alloc_expression(old->pos, EXPR_IMPLIED_CAST); expr->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 = evaluate_expression(type->initializer); if (!type) type = &bad_ctype; else if (type->type == SYM_NODE) type = type->ctype.base_type; } if (type->type == SYM_ENUM) type = type->ctype.base_type; *base = type; if (type->type == SYM_BASETYPE) { if (type->ctype.base_type == &int_type) return TYPE_NUM; if (type->ctype.base_type == &fp_type) return TYPE_NUM | TYPE_FLOAT; } return type_class[type->type]; } #define is_int(class) ((class & (TYPE_NUM | TYPE_FLOAT)) == TYPE_NUM) static inline int is_string_type(struct symbol *type) { if (type->type == SYM_NODE) type = type->ctype.base_type; return type->type == SYM_ARRAY && is_byte_type(type->ctype.base_type); } static struct symbol *bad_expr_type(struct expression *expr) { 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) { unsigned long lmod = ltype->ctype.modifiers; unsigned long rmod = rtype->ctype.modifiers; if (rmod & ~lmod & (MOD_LONG_ALL)) return rtype; else return ltype; } else return ltype; Restr: ctype = restricted_binop_type(op, left, right, lclass, rclass, ltype, rtype); if (ctype) return ctype; unrestrict(left, lclass, <ype); unrestrict(right, rclass, &rtype); goto Normal; } static inline int lvalue_expression(struct expression *expr) { return expr->type == EXPR_PREOP && expr->op == '*'; } static struct symbol *evaluate_ptr_add(struct expression *expr, struct symbol *itype) { struct expression *index = expr->right; struct symbol *ctype, *base; int multiply; classify_type(degenerate(expr->left), &ctype); base = examine_pointer_target(ctype); /* * 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_PURE) 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"; diff = (mod1 ^ mod2) & ~MOD_IGNORE; if (!diff) return NULL; if (diff & MOD_SIZE) return "different type sizes"; else if (diff & ~MOD_SIGNEDNESS) return "different modifiers"; else return "different signedness"; } t1 = base1; t2 = base2; } if (as1 != as2) return "different address spaces"; if ((mod1 ^ mod2) & ~MOD_IGNORE & ~MOD_SIGNEDNESS) return "different modifiers"; return NULL; } static void bad_null(struct expression *expr) { if (Wnon_pointer_null) warning(expr->pos, "Using plain integer as NULL pointer"); } static unsigned long target_qualifiers(struct symbol *type) { unsigned long mod = type->ctype.modifiers & MOD_IGN; if (type->ctype.base_type && type->ctype.base_type->type == SYM_ARRAY) mod = 0; return mod; } static struct symbol *evaluate_ptr_sub(struct expression *expr) { const char *typediff; struct symbol *ltype, *rtype; struct expression *l = expr->left; struct expression *r = expr->right; struct symbol *lbase; classify_type(degenerate(l), <ype); classify_type(degenerate(r), &rtype); lbase = examine_pointer_target(ltype); examine_pointer_target(rtype); typediff = type_difference(<ype->ctype, &rtype->ctype, target_qualifiers(rtype), target_qualifiers(ltype)); if (typediff) expression_error(expr, "subtraction of different types can't work (%s)", typediff); if (is_function(lbase)) { expression_error(expr, "subtraction of functions? Share your drugs"); return NULL; } expr->ctype = ssize_t_ctype; if (lbase->bit_size > bits_in_char) { struct expression *sub = alloc_expression(expr->pos, EXPR_BINOP); struct expression *div = expr; struct expression *val = alloc_expression(expr->pos, EXPR_VALUE); unsigned long value = bits_to_bytes(lbase->bit_size); val->ctype = size_t_ctype; val->value = value; if (value & (value-1)) { if (Wptr_subtraction_blows) { warning(expr->pos, "potentially expensive pointer subtraction"); 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 = 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)) { left = cast_to(left, rtype); goto OK; } if (is_null2 && (lclass & TYPE_PTR)) { right = cast_to(right, ltype); goto OK; } } /* both should be pointers */ if (!(lclass & rclass & TYPE_PTR)) return bad_expr_type(expr); expr->op = modify_for_unsigned(expr->op); lbase = examine_pointer_target(ltype); rbase = examine_pointer_target(rtype); /* they also have special treatment for pointers to void */ if (expr->op == SPECIAL_EQUAL || expr->op == SPECIAL_NOTEQUAL) { if (ltype->ctype.as == rtype->ctype.as) { if (lbase == &void_ctype) { right = cast_to(right, ltype); goto OK; } if (rbase == &void_ctype) { left = cast_to(left, rtype); goto OK; } } } typediff = type_difference(<ype->ctype, &rtype->ctype, target_qualifiers(rtype), target_qualifiers(ltype)); if (!typediff) goto OK; expression_error(expr, "incompatible types in comparison expression (%s):", typediff); 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->right, sclass, &s); target = integer_promotion(s); 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->ctype.modifiers & t2->ctype.modifiers & MOD_CHAR) return 1; if ((t1->ctype.modifiers ^ t2->ctype.modifiers) & MOD_SIZE) return 0; return !Wtypesign; } static int check_assignment_types(struct symbol *target, struct expression **rp, const char **typediff) { struct symbol *source = degenerate(*rp); struct symbol *t, *s; int tclass = classify_type(target, &t); int sclass = classify_type(source, &s); if (tclass & sclass & TYPE_NUM) { if (tclass & TYPE_RESTRICT) { /* allowed assignments unfoul */ if (sclass & TYPE_FOULED && unfoul(s) == t) goto Cast; if (!restricted_value(*rp, target)) return 1; if (s == t) return 1; } else if (!(sclass & TYPE_RESTRICT)) goto Cast; if (t == &bool_ctype) { if (is_fouled_type(s)) warning((*rp)->pos, "%s degrades to integer", show_typename(s->ctype.base_type)); goto Cast; } *typediff = "different base types"; return 0; } if (tclass == TYPE_PTR) { unsigned long mod1, mod2; struct symbol *b1, *b2; // NULL pointer is always OK int is_null = is_null_pointer_constant(*rp); if (is_null) { if (is_null == 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 = target_qualifiers(t); mod2 = target_qualifiers(s); if (whitelist_pointers(b1, b2)) { /* * assignments to/from void * are OK, provided that * we do not remove qualifiers from pointed to [C] * or mix address spaces [sparse]. */ if (t->ctype.as != s->ctype.as) { *typediff = "different address spaces"; return 0; } /* * If this is a function pointer assignment, it is * actually fine to assign a pointer to const data to * it, as a function pointer points to const data * implicitly, i.e., dereferencing it does not produce * an lvalue. */ if (b1->type == SYM_FN) mod1 |= MOD_CONST; if (mod2 & ~mod1) { *typediff = "different modifiers"; return 0; } goto Cast; } /* It's OK if the target is more volatile or const than the source */ *typediff = type_difference(&t->ctype, &s->ctype, 0, mod1); if (*typediff) return 0; return 1; } if ((tclass & TYPE_COMPOUND) && s == t) return 1; if (tclass & TYPE_NUM) { /* XXX: need to turn into comparison with NULL */ if (t == &bool_ctype && (sclass & TYPE_PTR)) goto Cast; *typediff = "different base types"; return 0; } *typediff = "invalid types"; return 0; Cast: *rp = cast_to(*rp, target); return 1; } static int compatible_assignment_types(struct expression *expr, struct symbol *target, struct expression **rp, const char *where) { const char *typediff; struct symbol *source = degenerate(*rp); if (!check_assignment_types(target, rp, &typediff)) { warning(expr->pos, "incorrect type in %s (%s)", where, typediff); info(expr->pos, " expected %s", show_typename(target)); info(expr->pos, " got %s", show_typename(source)); *rp = cast_to(*rp, target); return 0; } return 1; } static int compatible_transparent_union(struct symbol *target, struct expression **rp) { struct symbol *t, *member; classify_type(target, &t); if (t->type != SYM_UNION || !t->transparent_union) return 0; FOR_EACH_PTR(t->symbol_list, member) { const char *typediff; if (check_assignment_types(member, rp, &typediff)) return 1; } END_FOR_EACH_PTR(member); return 0; } static int compatible_argument_type(struct expression *expr, struct symbol *target, struct expression **rp, const char *where) { if (compatible_transparent_union(target, rp)) return 1; return compatible_assignment_types(expr, target, rp, where); } static void mark_assigned(struct expression *expr) { struct symbol *sym; if (!expr) return; switch (expr->type) { case EXPR_SYMBOL: sym = expr->symbol; if (!sym) return; if (sym->type != SYM_NODE) return; sym->ctype.modifiers |= MOD_ASSIGNED; return; case EXPR_BINOP: mark_assigned(expr->left); mark_assigned(expr->right); return; case EXPR_CAST: case EXPR_FORCE_CAST: mark_assigned(expr->cast_expression); return; case EXPR_SLICE: mark_assigned(expr->base); return; default: /* Hmm? */ return; } } static void evaluate_assign_to(struct expression *left, struct symbol *type) { if (type->ctype.modifiers & MOD_CONST) expression_error(left, "assignment to const expression"); /* We know left is an lvalue, so it's a "preop-*" */ mark_assigned(left->unop); } static struct symbol *evaluate_assignment(struct expression *expr) { struct expression *left = expr->left; struct 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; } *expr = *expr->unop; ctype = create_pointer(expr, ctype, 1); expr->ctype = ctype; default: /* nothing */; } return ctype; } static struct symbol *evaluate_addressof(struct expression *expr) { struct expression *op = expr->unop; struct symbol *ctype; if (op->op != '*' || op->type != EXPR_PREOP) { expression_error(expr, "not addressable"); return NULL; } ctype = op->ctype; *expr = *op->unop; if (expr->type == EXPR_SYMBOL) { struct symbol *sym = expr->symbol; sym->ctype.modifiers |= MOD_ADDRESSABLE; } /* * symbol expression evaluation is lazy about the type * of the sub-expression, so we may have to generate * the type here if so.. */ if (expr->ctype == &lazy_ptr_ctype) { ctype = create_pointer(expr, ctype, 0); expr->ctype = ctype; } return expr->ctype; } static struct symbol *evaluate_dereference(struct expression *expr) { struct expression *op = expr->unop; struct symbol *ctype = op->ctype, *node, *target; /* Simplify: *&(expr) => (expr) */ if (op->type == EXPR_PREOP && op->op == '&') { *expr = *op->unop; expr->flags = 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; examine_symbol_type(target); switch (ctype->type) { default: expression_error(expr, "cannot dereference this type"); return NULL; case SYM_FN: *expr = *op; return expr->ctype; case SYM_PTR: 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; } if ((class & TYPE_RESTRICT) && restricted_unop(expr->op, &ctype)) unrestrict(expr, class, &ctype); if (class & TYPE_NUM) { multiply = 1; } else if (class == TYPE_PTR) { struct symbol *target = examine_pointer_target(ctype); if (!is_function(target)) multiply = bits_to_bytes(target->bit_size); } if (multiply) { evaluate_assign_to(op, op->ctype); expr->op_value = multiply; expr->ctype = ctype; return ctype; } expression_error(expr, "bad argument type for ++/--"); return NULL; } static struct symbol *evaluate_sign(struct expression *expr) { struct symbol *ctype = expr->unop->ctype; int class = classify_type(ctype, &ctype); 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_nrbits = member->bit_size; expr->r_bitpos += member->bit_offset; expr->ctype = member; return member; } deref = deref->unop; expr->deref = deref; add = evaluate_offset(deref, offset); expr->type = EXPR_PREOP; expr->op = '*'; expr->unop = add; expr->ctype = member; return member; } static int is_promoted(struct expression *expr) { while (1) { switch (expr->type) { case EXPR_BINOP: case EXPR_SELECT: case EXPR_CONDITIONAL: return 1; case EXPR_COMMA: expr = expr->right; continue; case EXPR_PREOP: switch (expr->op) { case '(': expr = expr->unop; continue; case '+': case '-': case '~': return 1; default: return 0; } default: return 0; } } } static struct symbol *evaluate_cast(struct expression *); static struct symbol *evaluate_type_information(struct expression *expr) { struct symbol *sym = expr->cast_type; if (!sym) { sym = evaluate_expression(expr->cast_expression); if (!sym) return NULL; /* * Expressions of restricted types will possibly get * promoted - check that here */ if (is_restricted_type(sym)) { if (sym->bit_size < bits_in_int && is_promoted(expr)) sym = &int_ctype; } else if (is_fouled_type(sym)) { sym = &int_ctype; } } examine_symbol_type(sym); if (is_bitfield_type(sym)) { expression_error(expr, "trying to examine bitfield type"); return NULL; } return sym; } static struct symbol *evaluate_sizeof(struct expression *expr) { struct symbol *type; int size; type = evaluate_type_information(expr); if (!type) return NULL; size = type->bit_size; if (size < 0 && is_void_type(type)) { 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 (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; } static int evaluate_arguments(struct symbol *fn, struct expression_list *head) { struct expression *expr; struct symbol_list *argument_types = fn->arguments; struct symbol *argtype; int i = 1; PREPARE_PTR_LIST(argument_types, argtype); FOR_EACH_PTR (head, expr) { struct expression **p = THIS_ADDRESS(expr); struct symbol *ctype, *target; ctype = evaluate_expression(expr); if (!ctype) return 0; target = argtype; if (!target) { struct symbol *type; int class = classify_type(ctype, &type); if (is_int(class)) { *p = cast_to(expr, integer_promotion(type)); } else if (class & TYPE_FLOAT) { unsigned long mod = type->ctype.modifiers; if (!(mod & (MOD_LONG_ALL))) *p = cast_to(expr, &double_ctype); } else if (class & TYPE_PTR) { if (expr->ctype == &null_ctype) *p = cast_to(expr, &ptr_ctype); else degenerate(expr); } } else if (!target->forced_arg){ static char where[30]; examine_symbol_type(target); sprintf(where, "argument %d", i); compatible_argument_type(expr, target, p, where); } i++; NEXT_PTR_LIST(argtype); } END_FOR_EACH_PTR(expr); FINISH_PTR_LIST(argtype); return 1; } static void convert_index(struct expression *e) { struct expression *child = e->idx_expression; unsigned from = e->idx_from; unsigned to = e->idx_to + 1; e->type = EXPR_POS; e->init_offset = from * bits_to_bytes(e->ctype->bit_size); e->init_nr = to - from; e->init_expr = child; } static void convert_ident(struct expression *e) { struct expression *child = e->ident_expression; int offset = e->offset; e->type = EXPR_POS; e->init_offset = offset; e->init_nr = 1; e->init_expr = child; } static void convert_designators(struct expression *e) { while (e) { if (e->type == EXPR_INDEX) convert_index(e); else if (e->type == EXPR_IDENTIFIER) convert_ident(e); else break; e = e->init_expr; } } static void excess(struct expression *e, const char *s) { warning(e->pos, "excessive elements in %s initializer", s); } /* * implicit designator for the first element */ static struct expression *first_subobject(struct symbol *ctype, int class, struct expression **v) { struct expression *e = *v, *new; if (ctype->type == SYM_NODE) ctype = ctype->ctype.base_type; if (class & TYPE_PTR) { /* array */ if (!ctype->bit_size) return NULL; new = alloc_expression(e->pos, EXPR_INDEX); new->idx_expression = e; new->ctype = ctype->ctype.base_type; } else { struct symbol *field, *p; PREPARE_PTR_LIST(ctype->symbol_list, p); while (p && !p->ident && is_bitfield_type(p)) NEXT_PTR_LIST(p); field = p; FINISH_PTR_LIST(p); if (!field) return NULL; new = alloc_expression(e->pos, EXPR_IDENTIFIER); new->ident_expression = e; new->field = new->ctype = field; new->offset = field->offset; } *v = new; return new; } /* * sanity-check explicit designators; return the innermost one or NULL * in case of error. Assign types. */ static struct expression *check_designators(struct expression *e, struct symbol *ctype) { struct expression *last = NULL; const char *err; while (1) { if (ctype->type == SYM_NODE) ctype = ctype->ctype.base_type; if (e->type == EXPR_INDEX) { struct symbol *type; if (ctype->type != SYM_ARRAY) { err = "array index in non-array"; break; } type = ctype->ctype.base_type; if (ctype->bit_size >= 0 && type->bit_size >= 0) { unsigned offset = array_element_offset(type->bit_size, e->idx_to); if (offset >= ctype->bit_size) { err = "index out of bounds in"; break; } } e->ctype = ctype = type; ctype = type; last = e; if (!e->idx_expression) { err = "invalid"; break; } e = e->idx_expression; } else if (e->type == EXPR_IDENTIFIER) { int offset = 0; if (ctype->type != SYM_STRUCT && ctype->type != SYM_UNION) { err = "field name not in struct or union"; break; } ctype = find_identifier(e->expr_ident, ctype->symbol_list, &offset); if (!ctype) { err = "unknown field name in"; break; } e->offset = offset; e->field = e->ctype = ctype; last = e; if (!e->ident_expression) { err = "invalid"; break; } e = e->ident_expression; } else if (e->type == EXPR_POS) { err = "internal front-end error: EXPR_POS in"; break; } else return last; } expression_error(e, "%s initializer", err); return NULL; } /* * choose the next subobject to initialize. * * Get designators for next element, switch old ones to EXPR_POS. * Return the resulting expression or NULL if we'd run out of subobjects. * The innermost designator is returned in *v. Designators in old * are assumed to be already sanity-checked. */ static struct expression *next_designators(struct expression *old, struct symbol *ctype, struct expression *e, struct expression **v) { struct expression *new = NULL; if (!old) return NULL; if (old->type == EXPR_INDEX) { struct expression *copy; unsigned n; copy = next_designators(old->idx_expression, old->ctype, e, v); if (!copy) { n = old->idx_to + 1; if (array_element_offset(old->ctype->bit_size, n) == ctype->bit_size) { convert_index(old); return NULL; } copy = e; *v = new = alloc_expression(e->pos, EXPR_INDEX); } else { n = old->idx_to; new = alloc_expression(e->pos, EXPR_INDEX); } new->idx_from = new->idx_to = n; new->idx_expression = copy; new->ctype = old->ctype; convert_index(old); } else if (old->type == EXPR_IDENTIFIER) { struct expression *copy; struct symbol *field; int offset = 0; copy = next_designators(old->ident_expression, old->ctype, e, v); if (!copy) { field = old->field->next_subobject; if (!field) { convert_ident(old); return NULL; } copy = e; *v = new = alloc_expression(e->pos, EXPR_IDENTIFIER); /* * We can't necessarily trust "field->offset", * because the field might be in an anonymous * union, and the field offset is then the offset * within that union. * * The "old->offset - old->field->offset" * would be the offset of such an anonymous * union. */ offset = old->offset - old->field->offset; } else { field = old->field; new = alloc_expression(e->pos, EXPR_IDENTIFIER); } new->field = field; new->expr_ident = field->ident; new->ident_expression = copy; new->ctype = field; new->offset = field->offset + offset; convert_ident(old); } return new; } static int handle_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; FOR_EACH_PTR(expr->expr_list, e) { struct expression **v; struct symbol *type; int lclass; if (e->type != EXPR_INDEX && e->type != EXPR_IDENTIFIER) { struct symbol *struct_sym; if (!top) { top = e; last = first_subobject(ctype, class, &top); } else { last = next_designators(last, ctype, e, &top); } if (!last) { excess(e, class & TYPE_PTR ? "array" : "struct or union"); DELETE_CURRENT_PTR(e); continue; } struct_sym = ctype->type == SYM_NODE ? ctype->ctype.base_type : ctype; if (Wdesignated_init && struct_sym->designated_init) warning(e->pos, "%s%.*s%spositional init of field in %s %s, declared with attribute designated_init", ctype->ident ? "in initializer for " : "", ctype->ident ? ctype->ident->len : 0, ctype->ident ? ctype->ident->name : "", ctype->ident ? ": " : "", get_type_name(struct_sym->type), show_ident(struct_sym->ident)); if (jumped) { warning(e->pos, "advancing past deep designator"); jumped = 0; } REPLACE_CURRENT_PTR(e, last); } else { next = check_designators(e, ctype); if (!next) { DELETE_CURRENT_PTR(e); continue; } top = next; /* deeper than one designator? */ jumped = top != e; convert_designators(last); last = e; } found: lclass = classify_type(top->ctype, &type); if (top->type == EXPR_INDEX) v = &top->idx_expression; else v = &top->ident_expression; 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) { int is_string = is_string_type(ctype); struct expression *e = *ep, *p; struct symbol *type; if (!e) return 0; /* scalar */ if (!(class & TYPE_COMPOUND)) { e = handle_scalar(e, nested); if (!e) return 0; *ep = e; if (!evaluate_expression(e)) return 1; compatible_assignment_types(e, ctype, ep, "initializer"); /* * 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) { 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) { *ep = e; goto String; } return 0; } /* struct or union can be initialized by compatible */ if (class != TYPE_COMPOUND) return 0; type = evaluate_expression(e); if (!type) return 0; if (ctype->type == SYM_NODE) ctype = ctype->ctype.base_type; if (type->type == SYM_NODE) type = type->ctype.base_type; if (ctype == type) return 1; return 0; String: p = alloc_expression(e->pos, EXPR_STRING); *p = *e; type = evaluate_expression(p); if (ctype->bit_size != -1) { if (ctype->bit_size + bits_in_char < type->bit_size) warning(e->pos, "too long initializer-string for array of char"); else if (Winit_cstring && ctype->bit_size + bits_in_char == type->bit_size) { warning(e->pos, "too long initializer-string for array of char(no space for nul char)"); } } *ep = p; return 1; } static void evaluate_initializer(struct symbol *ctype, struct expression **ep) { struct symbol *type; int class = classify_type(ctype, &type); if (!handle_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); 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; } static struct symbol *evaluate_cast(struct expression *expr) { struct expression *source = expr->cast_expression; struct symbol *ctype; struct symbol *ttype, *stype; 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) { struct symbol *sym = expr->cast_type; struct expression *addr = alloc_expression(expr->pos, EXPR_SYMBOL); 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->unop = addr; expr->ctype = sym; return sym; } ctype = examine_symbol_type(expr->cast_type); 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)) 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); if (ctype->ctype.modifiers & MOD_INLINE) { int ret; struct symbol *curr = current_fn; if (ctype->definition) ctype = ctype->definition; current_fn = ctype->ctype.base_type; ret = inline_function(expr, ctype); /* restore the old function */ current_fn = curr; return ret; } return 0; } static struct symbol *evaluate_call(struct expression *expr) { int args, fnargs; struct symbol *ctype, *sym; struct expression *fn = expr->fn; struct expression_list *arglist = expr->args; if (!evaluate_expression(fn)) return NULL; sym = ctype = fn->ctype; if (ctype->type == SYM_NODE) ctype = ctype->ctype.base_type; if (ctype->type == SYM_PTR) ctype = get_base_type(ctype); if (ctype->type != SYM_FN) { struct expression *arg; expression_error(expr, "not a function %s", show_ident(sym->ident)); /* do typechecking in arguments */ FOR_EACH_PTR (arglist, arg) { evaluate_expression(arg); } END_FOR_EACH_PTR(arg); return NULL; } examine_fn_arguments(ctype); if (sym->type == SYM_NODE && fn->type == EXPR_PREOP && sym->op && sym->op->args) { if (!sym->op->args(expr)) return NULL; } else { if (!evaluate_arguments(ctype, arglist)) return NULL; args = expression_list_size(expr->args); fnargs = symbol_list_size(ctype->arguments); if (args < fnargs) { expression_error(expr, "not enough arguments for function %s", show_ident(sym->ident)); 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; } 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; return &ptr_ctype; case EXPR_TYPE: /* Evaluate the type of the symbol .. */ evaluate_symbol(expr->symbol); /* .. but the type of the _expression_ is a "type" */ expr->ctype = &type_ctype; return &type_ctype; case EXPR_OFFSETOF: return evaluate_offsetof(expr); /* These can not exist as stand-alone expressions */ case EXPR_INITIALIZER: case EXPR_IDENTIFIER: case EXPR_INDEX: case EXPR_POS: expression_error(expr, "internal front-end error: initializer in expression"); return NULL; case EXPR_SLICE: expression_error(expr, "internal front-end error: SLICE re-evaluated"); return NULL; } return NULL; } void check_duplicates(struct symbol *sym) { int declared = 0; struct symbol *next = sym; int initialized = sym->initializer != NULL; while ((next = next->same_symbol) != NULL) { const char *typediff; evaluate_symbol(next); if (initialized && next->initializer) { sparse_error(sym->pos, "symbol '%s' has multiple initializers (originally initialized at %s:%d)", show_ident(sym->ident), stream_name(next->pos.stream), next->pos.line); /* Only warn once */ initialized = 0; } declared++; typediff = type_difference(&sym->ctype, &next->ctype, 0, 0); if (typediff) { sparse_error(sym->pos, "symbol '%s' redeclared with different type (originally declared at %s:%d) - %s", show_ident(sym->ident), stream_name(next->pos.stream), next->pos.line, typediff); return; } } if (!declared) { unsigned long mod = sym->ctype.modifiers; if (mod & (MOD_STATIC | MOD_REGISTER | 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 = base_type; examine_fn_arguments(base_type); if (!base_type->stmt && base_type->inline_stmt) uninline(sym); if (base_type->stmt) evaluate_statement(base_type->stmt); current_fn = curr; } return base_type; } void evaluate_symbol_list(struct symbol_list *list) { struct symbol *sym; FOR_EACH_PTR(list, sym) { has_error &= ~ERROR_CURR_PHASE; evaluate_symbol(sym); check_duplicates(sym); } END_FOR_EACH_PTR(sym); } static struct symbol *evaluate_return_expression(struct statement *stmt) { struct expression *expr = stmt->expression; struct symbol *fntype; evaluate_expression(expr); fntype = current_fn->ctype.base_type; if (!fntype || fntype == &void_ctype) { if (expr && expr->ctype != &void_ctype) expression_error(expr, "return expression in %s function", fntype?"void":"typeless"); if (expr && Wreturn_void) warning(stmt->pos, "returning void-valued expression"); return NULL; } if (!expr) { sparse_error(stmt->pos, "return with no return value"); return NULL; } if (!expr->ctype) return NULL; compatible_assignment_types(expr, fntype, &stmt->expression, "return expression"); return NULL; } static void evaluate_if_statement(struct statement *stmt) { if (!stmt->if_conditional) return; evaluate_conditional(stmt->if_conditional, 0); evaluate_statement(stmt->if_true); evaluate_statement(stmt->if_false); } static void evaluate_iterator(struct statement *stmt) { evaluate_symbol_list(stmt->iterator_syms); evaluate_conditional(stmt->iterator_pre_condition, 1); evaluate_conditional(stmt->iterator_post_condition,1); evaluate_statement(stmt->iterator_pre_statement); evaluate_statement(stmt->iterator_statement); evaluate_statement(stmt->iterator_post_statement); } static void 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: // FIXME: arch-specific (and 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 && !label->stmt && label->ident && !lookup_keyword(label->ident, NS_KEYWORD)) sparse_error(stmt->pos, "label '%s' was not declared", show_ident(label->ident)); evaluate_expression(stmt->goto_expression); } struct symbol *evaluate_statement(struct statement *stmt) { if (!stmt) return NULL; switch (stmt->type) { case STMT_DECLARATION: { struct symbol *s; FOR_EACH_PTR(stmt->declaration, s) { evaluate_symbol(s); } END_FOR_EACH_PTR(s); return NULL; } case STMT_RETURN: return evaluate_return_expression(stmt); case STMT_EXPRESSION: if (!evaluate_expression(stmt->expression)) return NULL; if (stmt->expression->ctype == &null_ctype) stmt->expression = cast_to(stmt->expression, &ptr_ctype); return degenerate(stmt->expression); case STMT_COMPOUND: { struct statement *s; struct symbol *type = NULL; /* Evaluate the return symbol in the compound statement */ evaluate_symbol(stmt->ret); /* * Then, evaluate each statement, making the type of the * compound statement be the type of the last statement */ type = evaluate_statement(stmt->args); FOR_EACH_PTR(stmt->stmts, s) { type = evaluate_statement(s); } END_FOR_EACH_PTR(s); if (!type) type = &void_ctype; return type; } case STMT_IF: evaluate_if_statement(stmt); return NULL; case STMT_ITERATOR: evaluate_iterator(stmt); return NULL; case STMT_SWITCH: evaluate_switch_statement(stmt); return NULL; case STMT_CASE: evaluate_case_statement(stmt); return NULL; case STMT_LABEL: return evaluate_statement(stmt->label_statement); case STMT_GOTO: evaluate_goto_statement(stmt); return NULL; case STMT_NONE: break; case STMT_ASM: evaluate_asm_statement(stmt); return NULL; case STMT_CONTEXT: evaluate_expression(stmt->expression); return NULL; case STMT_RANGE: evaluate_expression(stmt->range_expression); evaluate_expression(stmt->range_low); evaluate_expression(stmt->range_high); return NULL; } return NULL; } sparse-0.6.1/evaluate.h000066400000000000000000000013211355073133200147710ustar00rootroot00000000000000#ifndef EVALUATE_H #define EVALUATE_H struct expression; 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); #endif sparse-0.6.1/example.c000066400000000000000000001300231355073133200146130ustar00rootroot00000000000000/* * 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_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) { 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_SETVAL likewise doesn't actually generate any * code. On use, the "def" of the pseudo will be * looked up. */ case OP_SETVAL: break; case OP_STORE: generate_store(insn, state); break; case OP_LOAD: generate_load(insn, state); break; case OP_DEATHNOTE: mark_pseudo_dead(state, insn->target); return; case OP_COPY: generate_copy(state, insn); break; case OP_ADD: case OP_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) { 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.1/expand.c000066400000000000000000000765751355073133200144640ustar00rootroot00000000000000/* * 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->ctype.modifiers & MOD_LONGLONG) && \ !(newtype->ctype.modifiers & MOD_LONGLONGLONG)) { if ((newtype->ctype.modifiers & MOD_LONG)) expr->fvalue = (double)expr->fvalue; else expr->fvalue = (float)expr->fvalue; } expr->type = EXPR_FVALUE; } static void warn_shift_count(struct expression *expr, struct symbol *ctype, long long count) { if (count < 0) { if (!Wshift_count_negative) return; warning(expr->pos, "shift count is negative (%lld)", count); return; } if (ctype->type == SYM_NODE) ctype = ctype->ctype.base_type; if (!Wshift_count_overflow) return; warning(expr->pos, "shift too big (%llu) for type %s", count, show_typename(ctype)); } /* 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; if (!conservative) warn_shift_count(expr, ctype, count); 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; unsigned long mod = expr->ctype->ctype.modifiers; long double l, r, res; if (left->type != EXPR_FVALUE || right->type != EXPR_FVALUE) return 0; l = left->fvalue; r = right->fvalue; if (mod & MOD_LONGLONG) { switch (expr->op) { case '+': res = l + r; break; case '-': res = l - r; break; case '*': res = l * r; break; case '/': if (!r) goto Div; res = l / r; break; default: return 0; } } else if (mod & MOD_LONG) { switch (expr->op) { case '+': res = (double) l + (double) r; break; case '-': res = (double) l - (double) r; break; case '*': res = (double) l * (double) r; break; case '/': if (!r) goto Div; res = (double) l / (double) r; break; default: return 0; } } else { switch (expr->op) { case '+': res = (float)l + (float)r; break; case '-': res = (float)l - (float)r; break; case '*': res = (float)l * (float)r; break; case '/': if (!r) goto Div; res = (float)l / (float)r; break; default: return 0; } } expr->type = EXPR_FVALUE; expr->fvalue = res; return 1; Div: if (!conservative) warning(expr->pos, "division by zero"); return 0; } static int simplify_float_cmp(struct expression *expr, struct symbol *ctype) { struct expression *left = expr->left, *right = expr->right; long double l, r; if (left->type != EXPR_FVALUE || right->type != EXPR_FVALUE) return 0; l = left->fvalue; r = right->fvalue; switch (expr->op) { case '<': expr->value = l < r; break; case '>': expr->value = l > r; break; case SPECIAL_LTE: expr->value = l <= r; break; case SPECIAL_GTE: expr->value = l >= r; break; case SPECIAL_EQUAL: expr->value = l == r; break; case SPECIAL_NOTEQUAL: expr->value = l != r; break; } expr->type = EXPR_VALUE; expr->taint = 0; return 1; } static int expand_binop(struct expression *expr) { int cost; cost = expand_expression(expr->left); cost += expand_expression(expr->right); if (simplify_int_binop(expr, expr->ctype)) return 0; if (simplify_float_binop(expr)) return 0; return cost + 1; } static int expand_logical(struct expression *expr) { struct expression *left = expr->left; struct expression *right; int cost, rcost; /* Do immediate short-circuiting ... */ cost = expand_expression(left); if (left->type == EXPR_VALUE) { if (expr->op == SPECIAL_LOGICAL_AND) { if (!left->value) { expr->type = EXPR_VALUE; expr->value = 0; expr->taint = left->taint; return 0; } } else { if (left->value) { expr->type = EXPR_VALUE; expr->value = 1; expr->taint = left->taint; return 0; } } } right = expr->right; rcost = expand_expression(right); if (left->type == EXPR_VALUE && right->type == EXPR_VALUE) { /* * We know the left value doesn't matter, since * otherwise we would have short-circuited it.. */ expr->type = EXPR_VALUE; expr->value = right->value != 0; expr->taint = left->taint | right->taint; return 0; } /* * If the right side is safe and cheaper than a branch, * just avoid the branch and turn it into a regular binop * style SAFELOGICAL. */ if (rcost < BRANCH_COST) { expr->type = EXPR_BINOP; rcost -= BRANCH_COST - 1; } return cost + BRANCH_COST + rcost; } static int expand_comma(struct expression *expr) { int cost; cost = expand_expression(expr->left); cost += expand_expression(expr->right); if (expr->left->type == EXPR_VALUE || expr->left->type == EXPR_FVALUE) { unsigned flags = expr->flags; unsigned taint; taint = expr->left->type == EXPR_VALUE ? expr->left->taint : 0; *expr = *expr->right; expr->flags = flags; if (expr->type == EXPR_VALUE) expr->taint |= Taint_comma | taint; } return cost; } #define MOD_IGN (MOD_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); } /* * Look up a trustable initializer value at the requested offset. * * Return NULL if no such value can be found or statically trusted. * * FIXME!! We should check that the size is right! */ static struct expression *constant_symbol_value(struct symbol *sym, int offset) { struct expression *value; if (sym->ctype.modifiers & MOD_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) return NULL; return entry->init_expr; } END_FOR_EACH_PTR(entry); return NULL; } return value; } static int expand_dereference(struct expression *expr) { struct expression *unop = expr->unop; unsigned int offset; expand_expression(unop); /* * NOTE! We get a bogus warning right now for some special * cases: apparently I've screwed up the optimization of * a zero-offset dereference, and the ctype is wrong. * * Leave the warning in anyway, since this is also a good * test for me to get the type evaluation right.. */ if (expr->ctype->ctype.modifiers & MOD_NODEREF) warning(unop->pos, "dereference of noderef expression"); /* * Is it "symbol" or "symbol + offset"? */ offset = 0; if (unop->type == EXPR_BINOP && unop->op == '+') { struct expression *right = unop->right; if (right->type == EXPR_VALUE) { offset = right->value; unop = unop->left; } } if (unop->type == EXPR_SYMBOL) { struct symbol *sym = unop->symbol; struct expression *value = constant_symbol_value(sym, offset); /* Const symbol with a constant initializer? */ if (value) { /* FIXME! We should check that the size is right! */ if (value->type == EXPR_VALUE) { 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) { 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->op && ctype->op->expand) return ctype->op->expand(expr, cost); if (ctype->ctype.modifiers & MOD_PURE) return cost + 1; return SIDE_EFFECTS; } static int expand_call(struct expression *expr) { int cost; struct symbol *sym; struct expression *fn = expr->fn; cost = expand_arguments(expr->args); sym = fn->ctype; if (!sym) { expression_error(expr, "function has no type"); return SIDE_EFFECTS; } if (sym->type == SYM_NODE) return expand_symbol_call(expr, cost); return SIDE_EFFECTS; } static int expand_expression_list(struct expression_list *list) { int cost = 0; struct expression *expr; FOR_EACH_PTR(list, expr) { cost += expand_expression(expr); } END_FOR_EACH_PTR(expr); return cost; } /* * We can simplify nested position expressions if * this is a simple (single) positional expression. */ static int expand_pos_expression(struct expression *expr) { struct expression *nested = expr->init_expr; unsigned long offset = expr->init_offset; int nr = expr->init_nr; if (nr == 1) { switch (nested->type) { case EXPR_POS: offset += nested->init_offset; *expr = *nested; expr->init_offset = offset; nested = expr; break; case EXPR_INITIALIZER: { struct expression *reuse = nested, *entry; *expr = *nested; FOR_EACH_PTR(expr->expr_list, entry) { if (entry->type == EXPR_POS) { entry->init_offset += offset; } else { if (!reuse) { /* * This happens rarely, but it can happen * with bitfields that are all at offset * zero.. */ reuse = alloc_expression(entry->pos, EXPR_POS); } reuse->type = EXPR_POS; reuse->ctype = entry->ctype; reuse->init_offset = offset; reuse->init_nr = 1; reuse->init_expr = entry; REPLACE_CURRENT_PTR(entry, reuse); reuse = NULL; } } END_FOR_EACH_PTR(entry); nested = expr; break; } default: break; } } return expand_expression(nested); } static unsigned long bit_offset(const struct expression *expr) { unsigned long offset = 0; while (expr->type == EXPR_POS) { offset += bytes_to_bits(expr->init_offset); expr = expr->init_expr; } if (expr && expr->ctype) offset += expr->ctype->bit_offset; return offset; } static unsigned long bit_range(const struct expression *expr) { unsigned long range = 0; unsigned long size = 0; while (expr->type == EXPR_POS) { unsigned long nr = expr->init_nr; size = expr->ctype->bit_size; range += (nr - 1) * size; expr = expr->init_expr; } range += size; return range; } static int compare_expressions(const void *_a, const void *_b) { const struct expression *a = _a; const struct expression *b = _b; unsigned long a_pos = bit_offset(a); unsigned long b_pos = bit_offset(b); return (a_pos < b_pos) ? -1 : (a_pos == b_pos) ? 0 : 1; } static void sort_expression_list(struct expression_list **list) { sort_list((struct ptr_list **)list, compare_expressions); } static void verify_nonoverlapping(struct expression_list **list, struct expression *expr) { struct expression *a = NULL; unsigned long max = 0; unsigned long whole = expr->ctype->bit_size; struct expression *b; if (!Woverride_init) return; FOR_EACH_PTR(*list, b) { unsigned long off, end; if (!b->ctype || !b->ctype->bit_size) continue; off = bit_offset(b); if (a && off < max) { warning(a->pos, "Initializer entry defined twice"); info(b->pos, " also defined here"); if (!Woverride_init_all) return; } end = off + bit_range(b); if (!a && !Woverride_init_whole_range) { // If first entry is the whole range, do not let // any warning about it (this allow to initialize // an array with some default value and then override // some specific entries). if (off == 0 && end == whole) continue; } if (end > max) { max = end; a = b; } } END_FOR_EACH_PTR(b); } static int expand_expression(struct expression *expr) { if (!expr) return 0; if (!expr->ctype || expr->ctype == &bad_ctype) return UNSAFE; switch (expr->type) { case EXPR_VALUE: case EXPR_FVALUE: case EXPR_STRING: return 0; case EXPR_TYPE: case EXPR_SYMBOL: return expand_symbol_expression(expr); case EXPR_BINOP: return expand_binop(expr); case EXPR_LOGICAL: return expand_logical(expr); case EXPR_COMMA: return expand_comma(expr); case EXPR_COMPARE: return expand_compare(expr); case EXPR_ASSIGNMENT: return expand_assignment(expr); case EXPR_PREOP: return expand_preop(expr); case EXPR_POSTOP: return expand_postop(expr); case EXPR_CAST: case EXPR_FORCE_CAST: case EXPR_IMPLIED_CAST: return expand_cast(expr); case EXPR_CALL: return expand_call(expr); case EXPR_DEREF: warning(expr->pos, "we should not have an EXPR_DEREF left at expansion time"); return UNSAFE; case EXPR_SELECT: case EXPR_CONDITIONAL: return expand_conditional(expr); case EXPR_STATEMENT: { struct statement *stmt = expr->statement; int cost = expand_statement(stmt); if (stmt->type == STMT_EXPRESSION && stmt->expression) *expr = *stmt->expression; return cost; } case EXPR_LABEL: return 0; case EXPR_INITIALIZER: sort_expression_list(&expr->expr_list); verify_nonoverlapping(&expr->expr_list, expr); return expand_expression_list(expr->expr_list); case EXPR_IDENTIFIER: return UNSAFE; case EXPR_INDEX: return UNSAFE; case EXPR_SLICE: return expand_expression(expr->base) + 1; case EXPR_POS: return expand_pos_expression(expr); case EXPR_SIZEOF: case EXPR_PTRSIZEOF: case EXPR_ALIGNOF: case EXPR_OFFSETOF: expression_error(expr, "internal front-end error: sizeof in expansion?"); return UNSAFE; } return SIDE_EFFECTS; } static void expand_const_expression(struct expression *expr, const char *where) { if (expr) { expand_expression(expr); if (expr->type != EXPR_VALUE) expression_error(expr, "Expected constant expression in %s", where); } } int expand_symbol(struct symbol *sym) { int retval; struct symbol *base_type; if (!sym) return 0; base_type = sym->ctype.base_type; if (!base_type) return 0; retval = expand_expression(sym->initializer); /* expand the body of the symbol */ if (base_type->type == SYM_FN) { if (base_type->stmt) expand_statement(base_type->stmt); } return retval; } static void expand_return_expression(struct statement *stmt) { expand_expression(stmt->expression); } static int expand_if_statement(struct statement *stmt) { struct expression *expr = stmt->if_conditional; if (!expr || !expr->ctype || expr->ctype == &bad_ctype) return UNSAFE; expand_expression(expr); /* This is only valid if nobody jumps into the "dead" side */ #if 0 /* Simplify constant conditionals without even evaluating the false side */ if (expr->type == EXPR_VALUE) { struct statement *simple; simple = expr->value ? stmt->if_true : stmt->if_false; /* Nothing? */ if (!simple) { stmt->type = STMT_NONE; return 0; } expand_statement(simple); *stmt = *simple; return SIDE_EFFECTS; } #endif expand_statement(stmt->if_true); expand_statement(stmt->if_false); return SIDE_EFFECTS; } 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.1/expand.h000066400000000000000000000030001355073133200144360ustar00rootroot00000000000000#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.1/expression.c000066400000000000000000000610301355073133200153600ustar00rootroot00000000000000/* * sparse/expression.c * * Copyright (C) 2003 Transmeta Corp. * 2003-2004 Linus Torvalds * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * * This is the expression parsing part of parsing C. */ #include #include #include #include #include #include #include #include #include #include "lib.h" #include "allocate.h" #include "token.h" #include "parse.h" #include "symbol.h" #include "scope.h" #include "expression.h" #include "target.h" #include "char.h" static int match_oplist(int op, ...) { va_list args; int nextop; va_start(args, op); do { nextop = va_arg(args, int); } while (nextop != 0 && nextop != op); va_end(args); return nextop != 0; } static struct token *comma_expression(struct token *, struct expression **); struct token *parens_expression(struct token *token, struct expression **expr, const char *where) { 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_symbol_scope(); token = compound_statement(token->next, stmt); end_symbol_scope(); token = expect(token, '}', "at end of statement expression"); } else token = parse_expression(token, expr); 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; /* function-scope, but in NS_SYMBOL */ bind_symbol(decl, ident, NS_LABEL); decl->namespace = NS_SYMBOL; len = current_fn->ident->len; string = __alloc_string(len + 1); memcpy(string->data, current_fn->ident->name, len); string->data[len] = 0; string->length = len + 1; decl->initializer = alloc_expression(token->pos, EXPR_STRING); decl->initializer->string = string; decl->initializer->ctype = decl; decl->array_size = alloc_const_expression(token->pos, len + 1); array->array_size = decl->array_size; decl->bit_size = array->bit_size = bytes_to_bits(len + 1); return decl; } static struct token *parse_type(struct token *token, struct expression **tree) { struct symbol *sym; *tree = alloc_expression(token->pos, EXPR_TYPE); 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: error_die(expr->pos, "constant %s is not a valid number", show_token(token)); } struct token *primary_expression(struct token *token, struct expression **tree) { struct expression *expr = NULL; switch (token_type(token)) { case TOKEN_CHAR ... TOKEN_WIDE_CHAR_EMBEDDED_3: expr = alloc_expression(token->pos, EXPR_VALUE); expr->flags = 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; } } 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); if (!(sym->ctype.modifiers & MOD_ADDRESSABLE)) { sym->ctype.modifiers |= MOD_ADDRESSABLE; add_symbol(&function_computed_target_list, sym); } 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->left = *tree; *tree = expr; token = parse_expression(token->next, &expr->cond_true); token = expect(token, ':', "in conditional expression"); token = conditional_expression(token, &expr->cond_false); } return token; } struct token *assignment_expression(struct token *token, struct expression **tree) { token = conditional_expression(token, tree); if (*tree && token_type(token) == TOKEN_SPECIAL) { static const int assignments[] = { '=', SPECIAL_ADD_ASSIGN, SPECIAL_SUB_ASSIGN, SPECIAL_MUL_ASSIGN, SPECIAL_DIV_ASSIGN, SPECIAL_MOD_ASSIGN, SPECIAL_SHL_ASSIGN, SPECIAL_SHR_ASSIGN, SPECIAL_AND_ASSIGN, SPECIAL_OR_ASSIGN, SPECIAL_XOR_ASSIGN }; int i, op = token->special; for (i = 0; i < ARRAY_SIZE(assignments); i++) if (assignments[i] == op) { struct expression * expr = alloc_expression(token->pos, EXPR_ASSIGNMENT); expr->left = *tree; expr->op = op; *tree = expr; return assignment_expression(token->next, &expr->right); } } return token; } static struct token *comma_expression(struct token *token, struct expression **tree) { LR_BINOP_EXPRESSION( token, tree, EXPR_COMMA, assignment_expression, (op == ',') ); } struct token *parse_expression(struct token *token, struct expression **tree) { return comma_expression(token,tree); } sparse-0.6.1/expression.h000066400000000000000000000211461355073133200153710ustar00rootroot00000000000000#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, }; /* * 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 expression { enum expression_type type:8; unsigned flags:8; int op; struct position pos; struct symbol *ctype; union { // EXPR_VALUE struct { unsigned long long value; unsigned taint; }; // EXPR_FVALUE long double fvalue; // EXPR_STRING struct { int wide; struct string *string; }; // EXPR_UNOP, EXPR_PREOP and EXPR_POSTOP struct /* unop */ { struct expression *unop; unsigned long op_value; }; // EXPR_SYMBOL, EXPR_TYPE struct /* symbol_arg */ { struct symbol *symbol; struct ident *symbol_name; }; // EXPR_STATEMENT struct statement *statement; // EXPR_BINOP, EXPR_COMMA, EXPR_COMPARE, EXPR_LOGICAL and EXPR_ASSIGNMENT struct /* binop_arg */ { struct expression *left, *right; }; // EXPR_DEREF struct /* deref_arg */ { struct expression *deref; struct ident *member; }; // EXPR_SLICE struct /* slice */ { struct expression *base; unsigned r_bitpos, r_nrbits; }; // EXPR_CAST, 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; }; }; }; }; /// // 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.1/flow.c000066400000000000000000000452561355073133200141440ustar00rootroot00000000000000/* * Flow - walk the linearized flowgraph, simplifying it as we * go along. * * Copyright (C) 2004 Linus Torvalds */ #include #include #include #include #include #include #include "parse.h" #include "expression.h" #include "linearize.h" #include "flow.h" #include "target.h" unsigned long bb_generation; /* * Dammit, if we have a phi-node followed by a conditional * branch on that phi-node, we should damn well be able to * do something about the source. Maybe. */ static int rewrite_branch(struct basic_block *bb, struct basic_block **ptr, struct basic_block *old, struct basic_block *new) { if (*ptr != old || new == old || !bb->ep) return 0; /* We might find new if-conversions or non-dominating CSEs */ /* we may also create new dead cycles */ repeat_phase |= REPEAT_CSE | REPEAT_CFG_CLEANUP; *ptr = new; replace_bb_in_list(&bb->children, old, new, 1); remove_bb_from_list(&old->parents, bb, 1); add_bb(&new->parents, bb); return 1; } /* * Return the known truth value of a pseudo, or -1 if * it's not known. */ static int pseudo_truth_value(pseudo_t pseudo) { switch (pseudo->type) { case PSEUDO_VAL: return !!pseudo->value; case PSEUDO_REG: { struct instruction *insn = pseudo->def; /* A symbol address is always considered true.. */ if (insn->opcode == OP_SYMADDR && insn->target == pseudo) return 1; } /* Fall through */ default: return -1; } } /* * Does a basic block depend on the pseudos that "src" defines? */ static int bb_depends_on(struct basic_block *target, struct basic_block *src) { pseudo_t pseudo; FOR_EACH_PTR(src->defines, pseudo) { if (pseudo_in_list(target->needs, pseudo)) return 1; } END_FOR_EACH_PTR(pseudo); return 0; } /* * This really should be handled by bb_depends_on() * which efficiently check the dependence using the * defines - needs liveness info. Problem is that * there is no liveness done on OP_PHI & OP_PHISRC. * * This function add the missing dependency checks. */ static int bb_depends_on_phi(struct basic_block *target, struct basic_block *src) { struct instruction *insn; FOR_EACH_PTR(src->insns, insn) { if (!insn->bb) continue; if (insn->opcode != OP_PHI) continue; if (pseudo_in_list(target->needs, insn->target)) return 1; } END_FOR_EACH_PTR(insn); return 0; } /* * When we reach here, we have: * - a basic block that ends in a conditional branch and * that has no side effects apart from the pseudos it * may change. * - the phi-node that the conditional branch depends on * - full pseudo liveness information * * We need to check if any of the _sources_ of the phi-node * may be constant, and not actually need this block at all. */ static int try_to_simplify_bb(struct basic_block *bb, struct instruction *first, struct instruction *second) { int changed = 0; pseudo_t phi; int bogus; /* * This a due to improper dominance tracking during * simplify_symbol_usage()/conversion to SSA form. * No sane simplification can be done when we have this. */ bogus = bb_list_size(bb->parents) != pseudo_list_size(first->phi_list); FOR_EACH_PTR(first->phi_list, phi) { struct instruction *def = phi->def; struct basic_block *source, *target; pseudo_t pseudo; struct instruction *br; int 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; insert_branch(target, insn, final); return 1; } static int simplify_one_branch(struct basic_block *bb, struct instruction *br) { if (simplify_phi_branch(bb, br)) return 1; return simplify_branch_branch(bb, br, &br->bb_true, 1) | simplify_branch_branch(bb, br, &br->bb_false, 0); } static int simplify_branch_nodes(struct entrypoint *ep) { int changed = 0; struct basic_block *bb; FOR_EACH_PTR(ep->bbs, bb) { struct instruction *br = last_instruction(bb->insns); if (!br || br->opcode != OP_CBR) continue; changed |= simplify_one_branch(bb, br); } END_FOR_EACH_PTR(bb); return changed; } /* * This is called late - when we have intra-bb liveness information.. */ int simplify_flow(struct entrypoint *ep) { return simplify_branch_nodes(ep); } static inline void concat_user_list(struct pseudo_user_list *src, struct pseudo_user_list **dst) { 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; } void convert_load_instruction(struct instruction *insn, pseudo_t src) { convert_instruction_target(insn, src); kill_instruction(insn); repeat_phase |= REPEAT_SYMBOL_CLEANUP; } static int overlapping_memop(struct instruction *a, struct instruction *b) { unsigned int a_start = bytes_to_bits(a->offset); unsigned int b_start = bytes_to_bits(b->offset); unsigned int a_size = a->size; unsigned int b_size = b->size; if (a_size + a_start <= b_start) return 0; if (b_size + b_start <= a_start) return 0; return 1; } static inline int same_memop(struct instruction *a, struct instruction *b) { return a->offset == b->offset && a->size == b->size; } static inline int distinct_symbols(pseudo_t a, pseudo_t b) { if (a->type != PSEUDO_SYM) return 0; if (b->type != PSEUDO_SYM) return 0; return a->sym != b->sym; } /* * Return 1 if "dom" dominates the access to "pseudo" * in "insn". * * Return 0 if it doesn't, and -1 if you don't know. */ int dominates(pseudo_t pseudo, struct instruction *insn, struct instruction *dom, int local) { int opcode = dom->opcode; if (opcode == OP_CALL || opcode == OP_ENTRY) return local ? 0 : -1; if (opcode != OP_LOAD && opcode != OP_STORE) return 0; if (dom->src != pseudo) { if (local) return 0; /* We don't think two explicitly different symbols ever alias */ if (distinct_symbols(insn->src, dom->src)) return 0; /* We could try to do some alias analysis here */ return -1; } if (!same_memop(insn, dom)) { if (dom->opcode == OP_LOAD) return 0; if (!overlapping_memop(insn, dom)) return 0; return -1; } return 1; } /* * We should probably sort the phi list just to make it easier to compare * later for equality. */ void rewrite_load_instruction(struct instruction *insn, struct pseudo_list *dominators) { pseudo_t new, phi; /* * Check for somewhat common case of duplicate * phi nodes. */ new = first_pseudo(dominators)->def->phi_src; FOR_EACH_PTR(dominators, phi) { if (new != phi->def->phi_src) goto complex_phi; new->ident = new->ident ? : phi->ident; } END_FOR_EACH_PTR(phi); /* * All the same pseudo - mark the phi-nodes unused * and convert the load into a LNOP and replace the * pseudo. */ convert_load_instruction(insn, new); FOR_EACH_PTR(dominators, phi) { kill_instruction(phi->def); } END_FOR_EACH_PTR(phi); goto end; complex_phi: /* We leave symbol pseudos with a bogus usage list here */ if (insn->src->type != PSEUDO_SYM) kill_use(&insn->src); insn->opcode = OP_PHI; insn->phi_list = dominators; end: repeat_phase |= REPEAT_SYMBOL_CLEANUP; } /* 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); } void pack_basic_blocks(struct entrypoint *ep) { struct basic_block *bb; /* See if we can merge a bb into another one.. */ FOR_EACH_PTR(ep->bbs, bb) { struct instruction *first, *insn; struct basic_block *parent, *child, *last; if (!bb_reachable(bb)) continue; /* * Just a branch? */ FOR_EACH_PTR(bb->insns, first) { if (!first->bb) continue; switch (first->opcode) { case OP_NOP: case OP_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); /* * Merge the two. */ repeat_phase |= REPEAT_CFG_CLEANUP; parent->children = bb->children; bb->children = NULL; bb->parents = NULL; FOR_EACH_PTR(parent->children, child) { replace_bb_in_list(&child->parents, bb, parent, 0); } END_FOR_EACH_PTR(child); kill_instruction(delete_last_instruction(&parent->insns)); FOR_EACH_PTR(bb->insns, insn) { if (!insn->bb) continue; assert(insn->bb == bb); insn->bb = parent; add_instruction(&parent->insns, insn); } END_FOR_EACH_PTR(insn); bb->insns = NULL; no_merge: /* nothing to do */; } END_FOR_EACH_PTR(bb); } sparse-0.6.1/flow.h000066400000000000000000000027761355073133200141510ustar00rootroot00000000000000#ifndef FLOW_H #define FLOW_H #include "lib.h" extern unsigned long bb_generation; #define REPEAT_CSE (1 << 0) #define REPEAT_SYMBOL_CLEANUP (1 << 1) #define REPEAT_CFG_CLEANUP (1 << 2) struct entrypoint; struct instruction; 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 void convert_instruction_target(struct instruction *insn, pseudo_t src); extern void remove_dead_insns(struct entrypoint *); extern int simplify_instruction(struct instruction *); 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); void convert_load_instruction(struct instruction *, pseudo_t); void rewrite_load_instruction(struct instruction *, struct pseudo_list *); int dominates(pseudo_t pseudo, struct instruction *insn, struct instruction *dom, int local); extern void vrfy_flow(struct entrypoint *ep); extern int pseudo_in_list(struct pseudo_list *list, pseudo_t pseudo); #endif sparse-0.6.1/flowgraph.c000066400000000000000000000113461355073133200151570ustar00rootroot00000000000000// 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); // 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.1/flowgraph.h000066400000000000000000000004121355073133200151540ustar00rootroot00000000000000#ifndef FLOWGRAPH_H #define FLOWGRAPH_H #include struct entrypoint; struct basic_block; int cfg_postorder(struct entrypoint *ep); void domtree_build(struct entrypoint *ep); bool domtree_dominates(struct basic_block *a, struct basic_block *b); #endif sparse-0.6.1/gcc-attr-list.h000066400000000000000000000105771355073133200156550ustar00rootroot00000000000000GCC_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.1/gdbhelpers000066400000000000000000000127101355073133200150600ustar00rootroot00000000000000 # 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_CHAR) printf "MOD_CHAR " end if ($arg0->modifiers & MOD_SHORT) printf "MOD_SHORT " end if ($arg0->modifiers & MOD_LONG) printf "MOD_LONG " end if ($arg0->modifiers & MOD_LONGLONG) printf "MOD_LONGLONG " end if ($arg0->modifiers & MOD_LONGLONGLONG) printf "MOD_LONGLONGLONG " end if ($arg0->modifiers & MOD_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_TYPE) printf "MOD_TYPE " end if ($arg0->modifiers & MOD_SAFE) printf "MOD_SAFE " end if ($arg0->modifiers & MOD_USERTYPE) printf "MOD_USERTYPE " end if ($arg0->modifiers & MOD_EXPLICITLY_SIGNED) printf "MOD_EXPLICITLY_SIGNED" end if ($arg0->modifiers & MOD_BITWISE) printf "MOD_BITWISE " end if (!$arg0->modifiers) printf "0" end printf ", alignment = %d", $arg0->alignment if ($arg0->as) printf ", address_space = %d", $arg0->as end printf "\n" set $ntabs++ if ($arg0->base_type) gdb_tabs printf "base_type = " gdb_show_symbol($arg0->base_type) end set $ntabs-- end define gdb_show_symbol printf "(%x) type = ", $arg0 output $arg0->type printf ", namespace = " output $arg0->namespace if ($arg0->ident) printf ", ident = %s\n", show_ident($arg0->ident) else printf ", ident = NULL\n" end # print "zz" gdb_tabs printf "dump:\n" call show_symbol($arg0) set $ntabs++ if ($arg0->namespace == NS_SYMBOL) gdb_tabs printf "ctype = " gdb_show_ctype(&($arg0->ctype)) end set $ntabs-- end # non-recursive define gdb_show_symbols_next_id set $sym = $arg0 printf "{\n" set $ntabs++ while ($sym) gdb_tabs printf "symbol = " gdb_show_symbol($sym) set $sym = $sym->next_id end set $ntabs-- gdb_tabs printf "}\n" end define gdb_show_ident if ($arg0) printf "(%p) '%s'\n", $arg0, show_ident($arg0) else printf "NULL\n" end if (! $showing_ident) set $showing_ident = 1 set $ntabs++ set $ident = $arg0 if ($ident->symbols) gdb_tabs printf "symbols = " gdb_show_symbols_next_id($ident->symbols) end set $ntabs-- set $showing_ident = 0 end end define gdb_show_token printf "%p: '%s', type = ", $arg0, show_token($arg0) output (enum token_type) ($arg0)->pos.type printf "\n" if (! $showing_token) set $showing_token = 1 set $ntabs++ set $token = $arg0 if ($token->pos.type == TOKEN_IDENT) gdb_tabs printf "ident = " gdb_show_ident $token.ident end if ($token->pos.type == TOKEN_MACRO_ARGUMENT) gdb_tabs printf "argnum = %d\n", $token->argnum end if ($token->pos.type == TOKEN_SPECIAL) gdb_tabs printf "special = \"%s\"\n", show_special($token.special) end set $ntabs-- set $showing_token = 0 end end # non-recursive define gdb_show_tokens set $t = $arg0 printf "{\n" set $ntabs++ while ($t != &eof_token_entry) gdb_tabs printf "token = " gdb_show_token($t) set $t = ($t)->next end set $ntabs-- gdb_tabs printf "}\n" end sparse-0.6.1/graph.c000066400000000000000000000132421355073133200142640ustar00rootroot00000000000000/* 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.1/gvpr/000077500000000000000000000000001355073133200137735ustar00rootroot00000000000000sparse-0.6.1/gvpr/return-paths000077500000000000000000000041371355073133200163620ustar00rootroot00000000000000#!/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.1/gvpr/subg-fwd000077500000000000000000000031471355073133200154440ustar00rootroot00000000000000#!/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.1/gvpr/subg-rev000077500000000000000000000036271355073133200154630ustar00rootroot00000000000000#!/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.1/ident-list.h000066400000000000000000000041271355073133200152460ustar00rootroot00000000000000 #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(__has_attribute); IDENT(__has_builtin); __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); #undef __IDENT #undef IDENT #undef IDENT_RESERVED sparse-0.6.1/inline.c000066400000000000000000000365641355073133200144550ustar00rootroot00000000000000/* * 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; } 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; fn->expanding = 1; name_list = fn->arguments; expr->type = EXPR_STATEMENT; expr->statement = stmt; expr->ctype = fn->ctype.base_type; fn_symbol_list = create_symbol_list(sym->inline_symbol_list); arg_decl = NULL; PREPARE_PTR_LIST(name_list, name); FOR_EACH_PTR(arg_list, arg) { struct symbol *a = alloc_symbol(arg->pos, SYM_NODE); a->ctype.base_type = arg->ctype; if (name) { *a = *name; set_replace(name, a); add_symbol(&fn_symbol_list, a); } a->initializer = arg; add_symbol(&arg_decl, a); NEXT_PTR_LIST(name); } END_FOR_EACH_PTR(arg); FINISH_PTR_LIST(name); copy_statement(fn->inline_stmt, stmt); if (arg_decl) { struct statement *decl = alloc_statement(expr->pos, STMT_DECLARATION); decl->declaration = arg_decl; stmt->args = decl; } stmt->inline_fn = sym; unset_replace_list(fn_symbol_list); evaluate_statement(stmt); fn->expanding = 0; return 1; } void uninline(struct symbol *sym) { struct symbol *fn = sym->ctype.base_type; struct symbol_list *arg_list = fn->arguments; struct symbol *p; sym->symbol_list = create_symbol_list(sym->inline_symbol_list); FOR_EACH_PTR(arg_list, p) { p->replace = p; } END_FOR_EACH_PTR(p); fn->stmt = alloc_statement(fn->pos, STMT_COMPOUND); copy_statement(fn->inline_stmt, fn->stmt); unset_replace_list(sym->symbol_list); unset_replace_list(arg_list); } sparse-0.6.1/ir.c000066400000000000000000000077441355073133200136070ustar00rootroot00000000000000// 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_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.1/ir.h000066400000000000000000000001451355073133200136000ustar00rootroot00000000000000#ifndef _IR_H #define _IR_H #include "linearize.h" int ir_validate(struct entrypoint *ep); #endif sparse-0.6.1/lib.c000066400000000000000000001146601355073133200137370ustar00rootroot00000000000000/* * '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 "version.h" #include "bits.h" int verbose, optimize_level, optimize_size, preprocessing; int die_if_error = 0; int has_error = 0; int do_output = 1; #ifndef __GNUC__ # define __GNUC__ 2 # define __GNUC_MINOR__ 95 # define __GNUC_PATCHLEVEL__ 0 #endif int gcc_major = __GNUC__; int gcc_minor = __GNUC_MINOR__; int gcc_patchlevel = __GNUC_PATCHLEVEL__; const char *base_filename; static const char *diag_prefix = ""; static const char *gcc_base_dir = GCC_BASE; static const char *multiarch_dir = MULTIARCH_TRIPLET; static const char *outfile = NULL; 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. 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)); } unsigned int hexval(unsigned int c) { int retval = 256; switch (c) { case '0'...'9': retval = c - '0'; break; case 'a'...'f': retval = c - 'a' + 10; break; case 'A'...'F': retval = c - 'A' + 10; break; } return retval; } static void do_warn(const char *type, struct position pos, const char * fmt, va_list args) { static char buffer[512]; const char *name; /* Shut up warnings if position is bad_token.pos */ if (pos.type == TOKEN_BAD) return; vsprintf(buffer, fmt, args); name = stream_name(pos.stream); fflush(stdout); fprintf(stderr, "%s:%d:%d: %s%s%s\n", name, pos.line, pos.pos, diag_prefix, type, buffer); } unsigned int fmax_warnings = 100; static int show_info = 1; void info(struct position pos, const char * fmt, ...) { va_list args; if (!show_info) return; va_start(args, fmt); do_warn("", pos, fmt, args); va_end(args); } static void do_error(struct position pos, const char * fmt, va_list args) { static int errors = 0; die_if_error = 1; show_info = 1; /* Shut up warnings 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 > 100) { static int once = 0; show_info = 0; if (once) return; fmt = "too many errors"; once = 1; } do_warn("error: ", pos, fmt, args); errors++; } void warning(struct position pos, const char * fmt, ...) { va_list args; if (Wsparse_error) { va_start(args, fmt); do_error(pos, fmt, args); va_end(args); return; } if (!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_end = NULL; 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 Wimplicit_int = 1; int Winit_cstring = 0; int Wint_to_pointer_cast = 1; int Wenum_mismatch = 1; int Wsparse_error = 0; int Wmemcpy_max_count = 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 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 Wstrict_prototypes = 1; int Wtautological_compare = 0; int Wtransparent_union = 0; int Wtypesign = 0; int Wundef = 0; int Wuninitialized = 1; int Wunknown_attribute = 0; int Wvla = 1; int dump_macro_defs = 0; int dump_macros_only = 0; int dbg_compound = 0; int dbg_dead = 0; int dbg_domtree = 0; int dbg_entry = 0; int dbg_ir = 0; int dbg_postorder = 0; unsigned long fdump_ir; int fmem_report = 0; unsigned long long fmemcpy_max_count = 100000; unsigned long fpasses = ~0UL; int funsigned_char = UNSIGNED_CHAR; int preprocess_only; static enum { STANDARD_C89, STANDARD_C94, STANDARD_C99, STANDARD_C11, STANDARD_GNU11, STANDARD_GNU89, STANDARD_GNU99, } standard = STANDARD_GNU89; static int arch_msize_long = 0; int arch_m64 = ARCH_M64_DEFAULT; int arch_big_endian = ARCH_BIG_ENDIAN; int arch_mach = MACH_NATIVE; #define CMDLINE_INCLUDE 20 static int cmdline_include_nr = 0; static char *cmdline_include[CMDLINE_INCLUDE]; void add_pre_buffer(const char *fmt, ...) { va_list args; unsigned int size; struct token *begin, *end; char buffer[4096]; va_start(args, fmt); size = vsnprintf(buffer, sizeof(buffer), fmt, args); va_end(args); begin = tokenize_buffer(buffer, size, &end); if (!pre_buffer_begin) pre_buffer_begin = begin; if (pre_buffer_end) pre_buffer_end->next = begin; pre_buffer_end = end; } static char **handle_switch_D(char *arg, char **next) { const char *name = arg + 1; const char *value = "1"; if (!*name) { 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_E(char *arg, char **next) { if (arg[1] == '\0') preprocess_only = 1; return next; } static char **handle_switch_I(char *arg, char **next) { char *path = arg+1; switch (arg[1]) { case '-': add_pre_buffer("#split_include\n"); break; case '\0': /* Plain "-I" */ path = *++next; if (!path) die("missing argument for -I option"); /* Fall through */ default: add_pre_buffer("#add_include \"%s/\"\n", path); } return next; } static void add_cmdline_include(char *filename) { if (cmdline_include_nr >= CMDLINE_INCLUDE) die("too many include files for %s\n", filename); cmdline_include[cmdline_include_nr++] = filename; } static char **handle_switch_i(char *arg, char **next) { if (*next && !strcmp(arg, "include")) add_cmdline_include(*++next); else if (*next && !strcmp(arg, "imacros")) add_cmdline_include(*++next); else if (*next && !strcmp(arg, "isystem")) { char *path = *++next; if (!path) die("missing argument for -isystem option"); add_pre_buffer("#add_isystem \"%s/\"\n", path); } else if (*next && !strcmp(arg, "idirafter")) { char *path = *++next; if (!path) die("missing argument for -idirafter option"); add_pre_buffer("#add_dirafter \"%s/\"\n", path); } return next; } static char **handle_switch_M(char *arg, char **next) { if (!strcmp(arg, "MF") || !strcmp(arg,"MQ") || !strcmp(arg,"MT")) { if (!*next) die("missing argument for -%s option", arg); return next + 1; } return next; } static char **handle_multiarch_dir(char *arg, char **next) { multiarch_dir = *++next; if (!multiarch_dir) die("missing argument for -multiarch-dir option"); return next; } static char **handle_switch_m(char *arg, char **next) { if (!strcmp(arg, "m64")) { arch_m64 = ARCH_LP64; } else if (!strcmp(arg, "m32") || !strcmp(arg, "m16")) { arch_m64 = ARCH_LP32; } else if (!strcmp(arg, "mx32")) { arch_m64 = ARCH_X32; } else if (!strcmp(arg, "msize-llp64")) { arch_m64 = ARCH_LLP64; } else if (!strcmp(arg, "msize-long")) { arch_msize_long = 1; } else if (!strcmp(arg, "multiarch-dir")) { return handle_multiarch_dir(arg, next); } else if (!strcmp(arg, "mbig-endian")) { arch_big_endian = 1; } else if (!strcmp(arg, "mlittle-endian")) { arch_big_endian = 0; } return next; } static void handle_arch_msize_long_finalize(void) { if (arch_msize_long) { size_t_ctype = &ulong_ctype; ssize_t_ctype = &long_ctype; } } static void handle_arch_finalize(void) { handle_arch_msize_long_finalize(); } 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 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 struct flag { const char *name; int *flag; int (*fun)(const char *arg, const char *opt, const struct flag *, int options); unsigned long mask; }; 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_INVERSE) val = !val; *flags->flag = val; return 1; } } // not handled return 0; } #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) 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 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 }, { "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 }, { "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 }, { "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 }, { "unknown-attribute", &Wunknown_attribute }, { "vla", &Wvla }, }; enum { WARNING_OFF, WARNING_ON, WARNING_FORCE_OFF }; static char **handle_onoff_switch(char *arg, char **next, const struct flag warnings[], int n) { int flag = WARNING_ON; char *p = arg + 1; unsigned i; if (!strcmp(p, "sparse-all")) { for (i = 0; i < n; i++) { if (*warnings[i].flag != WARNING_FORCE_OFF && warnings[i].flag != &Wsparse_error) *warnings[i].flag = WARNING_ON; } return NULL; } // Prefixes "no" and "no-" mean to turn warning off. if (p[0] == 'n' && p[1] == 'o') { p += 2; if (p[0] == '-') p++; flag = WARNING_FORCE_OFF; } for (i = 0; i < n; i++) { if (!strcmp(p,warnings[i].name)) { *warnings[i].flag = flag; return next; } } // Unknown. return NULL; } static char **handle_switch_W(char *arg, char **next) { char ** ret = handle_onoff_switch(arg, next, warnings, ARRAY_SIZE(warnings)); if (ret) return ret; // Unknown. return next; } static struct 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, ARRAY_SIZE(debugs)); if (ret) return ret; // Unknown. do { verbose++; } while (*++arg == 'v'); 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 void handle_onoff_switch_finalize(const struct flag warnings[], int n) { unsigned i; for (i = 0; i < n; i++) { if (*warnings[i].flag == WARNING_FORCE_OFF) *warnings[i].flag = WARNING_OFF; } } static void handle_switch_W_finalize(void) { handle_onoff_switch_finalize(warnings, ARRAY_SIZE(warnings)); /* default Wdeclarationafterstatement based on the C dialect */ if (-1 == Wdeclarationafterstatement) { switch (standard) { case STANDARD_C89: case STANDARD_C94: Wdeclarationafterstatement = 1; break; case STANDARD_C99: case STANDARD_GNU89: case STANDARD_GNU99: case STANDARD_C11: case STANDARD_GNU11: Wdeclarationafterstatement = 0; break; default: assert (0); } } } static void handle_switch_v_finalize(void) { handle_onoff_switch_finalize(debugs, ARRAY_SIZE(debugs)); } static char **handle_switch_U(char *arg, char **next) { const char *name = arg + 1; add_pre_buffer ("#undef %s\n", name); return next; } static char **handle_switch_O(char *arg, char **next) { int level = 1; if (arg[1] >= '0' && arg[1] <= '9') level = arg[1] - '0'; optimize_level = level; optimize_size = arg[1] == 's'; 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_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 }, { "linearize", NULL, handle_fpasses, PASS_LINEARIZE }, { "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 }, { "signed-char", &funsigned_char, NULL, OPT_INVERSE }, { "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_switch_a(char *arg, char **next) { if (!strcmp (arg, "ansi")) standard = STANDARD_C89; 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 die ("Unsupported C dialect"); } return next; } static char **handle_nostdinc(char *arg, char **next) { add_pre_buffer("#nostdinc\n"); return next; } static char **handle_switch_n(char *arg, char **next) { if (!strcmp (arg, "nostdinc")) return handle_nostdinc(arg, next); return next; } static char **handle_base_dir(char *arg, char **next) { gcc_base_dir = *++next; if (!gcc_base_dir) die("missing argument for -gcc-base-dir option"); return next; } static char **handle_switch_g(char *arg, char **next) { if (!strcmp (arg, "gcc-base-dir")) return handle_base_dir(arg, next); return next; } static char **handle_switch_x(char *arg, char **next) { if (!*++next) die("missing argument for -x option"); return next; } static char **handle_version(char *arg, char **next) { printf("%s\n", SPARSE_VERSION); exit(0); } static char **handle_param(char *arg, char **next) { char *value = NULL; /* For now just skip any '--param=*' or '--param *' */ if (*arg == '\0') { value = *++next; } else if (isspace((unsigned char)*arg) || *arg == '=') { value = ++arg; } if (!value) die("missing argument for --param option"); return next; } struct switches { const char *name; char **(*fn)(char *, char **); unsigned int prefix:1; }; static char **handle_long_options(char *arg, char **next) { static struct switches cmd[] = { { "param", handle_param, 1 }, { "version", handle_version }, { NULL, NULL } }; struct switches *s = cmd; while (s->name) { int optlen = strlen(s->name); if (!strncmp(s->name, arg, optlen + !s->prefix)) return s->fn(arg + optlen, next); s++; } return next; } static char **handle_switch(char *arg, char **next) { switch (*arg) { case 'a': return handle_switch_a(arg, next); case 'D': return handle_switch_D(arg, next); case 'd': return handle_switch_d(arg, next); case 'E': return handle_switch_E(arg, next); case 'f': return handle_switch_f(arg, next); case 'g': return handle_switch_g(arg, next); case 'G': return handle_switch_G(arg, next); case 'I': return handle_switch_I(arg, next); case 'i': return handle_switch_i(arg, next); case 'M': return handle_switch_M(arg, next); case 'm': return handle_switch_m(arg, next); case 'n': return handle_switch_n(arg, next); case 'o': return handle_switch_o(arg, next); case 'O': return handle_switch_O(arg, next); case 's': return handle_switch_s(arg, next); case 'U': return handle_switch_U(arg, next); case 'v': return handle_switch_v(arg, next); case 'W': return handle_switch_W(arg, next); case '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; } #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, unsigned bits) { char buf[32]; snprintf(buf, sizeof(buf), "__%s_WIDTH__", name); predefine(buf, 1, "%d", bits); } 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)) predefine(buf, 1, "(-__%s_MAX__ - 1)", 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, bits); } static 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"); switch (standard) { case STANDARD_C89: predefine("__STRICT_ANSI__", 1, "1"); break; case STANDARD_C94: predefine("__STDC_VERSION__", 1, "199409L"); predefine("__STRICT_ANSI__", 1, "1"); break; case STANDARD_C99: predefine("__STDC_VERSION__", 1, "199901L"); predefine("__STRICT_ANSI__", 1, "1"); break; case STANDARD_GNU89: default: break; case STANDARD_GNU99: predefine("__STDC_VERSION__", 1, "199901L"); break; case STANDARD_C11: predefine("__STRICT_ANSI__", 1, "1"); case STANDARD_GNU11: predefine("__STDC_NO_ATOMICS__", 1, "1"); predefine("__STDC_NO_COMPLEX__", 1, "1"); predefine("__STDC_NO_THREADS__", 1, "1"); predefine("__STDC_VERSION__", 1, "201112L"); break; } 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", &uint_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_sizeof("INT128", "", 128); predefined_ctype("INTMAX", intmax_ctype, PTYPE_MAX|PTYPE_TYPE|PTYPE_WIDTH); predefined_ctype("UINTMAX", uintmax_ctype, PTYPE_MAX|PTYPE_TYPE); predefined_ctype("INTPTR", ssize_t_ctype, PTYPE_MAX|PTYPE_TYPE|PTYPE_WIDTH); predefined_ctype("UINTPTR", size_t_ctype, PTYPE_MAX|PTYPE_TYPE); predefined_ctype("PTRDIFF", ssize_t_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_sizeof("FLOAT", "", bits_in_float); predefined_sizeof("DOUBLE", "", bits_in_double); predefined_sizeof("LONG_DOUBLE", "", bits_in_longdouble); 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"); // 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; } switch (arch_mach) { case MACH_ARM64: predefine("__aarch64__", 1, "1"); break; case MACH_ARM: predefine("__arm__", 1, "1"); break; case MACH_M68K: predefine("__m68k__", 1, "1"); break; case MACH_MIPS64: if (arch_m64 == ARCH_LP64) predefine("__mips64", 1, "64"); /* fall-through */ case MACH_MIPS32: 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); break; case MACH_PPC64: if (arch_m64 == ARCH_LP64) { predefine("__powerpc64__", 1, "1"); predefine("__ppc64__", 1, "1"); predefine("__PPC64__", 1, "1"); } /* fall-through */ case MACH_PPC32: predefine("__powerpc__", 1, "1"); predefine("__powerpc", 1, "1"); predefine("__ppc__", 1, "1"); predefine("__PPC__", 1, "1"); break; case MACH_RISCV64: case MACH_RISCV32: predefine("__riscv", 1, "1"); predefine("__riscv_xlen", 1, "%d", ptr_ctype.bit_size); break; case MACH_S390X: predefine("__zarch__", 1, "1"); predefine("__s390x__", 1, "1"); predefine("__s390__", 1, "1"); break; case MACH_SPARC64: if (arch_m64 == ARCH_LP64) { predefine("__sparc_v9__", 1, "1"); predefine("__sparcv9__", 1, "1"); predefine("__sparcv9", 1, "1"); predefine("__sparc64__", 1, "1"); predefine("__arch64__", 1, "1"); } /* fall-through */ case MACH_SPARC32: predefine("__sparc__", 1, "1"); predefine("__sparc", 1, "1"); break; case MACH_X86_64: if (arch_m64 != ARCH_LP32) { predefine("__x86_64__", 1, "1"); predefine("__x86_64", 1, "1"); break; } /* fall-through */ case MACH_I386: predefine("__i386__", 1, "1"); predefine("__i386", 1, "1"); break; } } 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 __has_builtin(x) 0\n"); add_pre_buffer("#define __has_attribute(x) 0\n"); 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(filename, fd, NULL, includepath); close(fd); return sparse_tokenstream(token); } /* * This handles the "-include" directive etc: we're in global * scope, and all types/macros etc will affect all the following * files. * * NOTE NOTE NOTE! "#undef" of anything in this stage will * affect all subsequent files too, i.e. we can have non-local * behaviour between files! */ static struct symbol_list *sparse_initial(void) { int i; // Prepend any "include" file to the stream. // We're in global scope, it will affect all files! for (i = 0; i < cmdline_include_nr; i++) add_pre_buffer("#argv_include \"%s\"\n", cmdline_include[i]); return sparse_tokenstream(pre_buffer_begin); } struct symbol_list *sparse_initialize(int argc, char **argv, struct string_list **filelist) { char **args; struct symbol_list *list; // Initialize symbol stream first, so that we can add defines etc init_symbols(); args = argv; for (;;) { char *arg = *++args; if (!arg) break; if (arg[0] == '-' && arg[1]) { args = handle_switch(arg+1, args); continue; } add_ptr_list(filelist, arg); } handle_switch_W_finalize(); handle_switch_v_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 init_target(); handle_arch_finalize(); init_ctype(); predefined_macros(); create_builtin_stream(); declare_builtins(); 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.1/lib.h000066400000000000000000000213171355073133200137400ustar00rootroot00000000000000#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" #define DO_STRINGIFY(x) #x #define STRINGIFY(x) DO_STRINGIFY(x) #ifndef ARRAY_SIZE #define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0])) #endif extern int verbose, optimize_level, optimize_size, preprocessing; extern int die_if_error; extern int repeat_phase; extern int do_output; extern int gcc_major, gcc_minor, gcc_patchlevel; extern const char *base_filename; extern unsigned int hexval(unsigned int c); struct position { unsigned int type:6, stream:14, newline:1, whitespace:1, pos:10; unsigned int line:31, noexpand:1; }; struct ident; struct token; struct symbol; struct statement; struct 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; struct token *skip_to(struct token *, int); struct token *expect(struct token *, int, const char *); void unexpected(struct token *, const char *errmsg); #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 int preprocess_only; 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 Wsparse_error; extern int Wimplicit_int; extern int Winit_cstring; extern int Wint_to_pointer_cast; extern int Wmemcpy_max_count; 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 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 Wstrict_prototypes; extern int Wtautological_compare; extern int Wtransparent_union; extern int Wtypesign; extern int Wundef; extern int Wuninitialized; extern int Wunknown_attribute; extern int Wvla; extern int dump_macro_defs; extern int dump_macros_only; 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 unsigned int fmax_warnings; extern int fmem_report; extern unsigned long fdump_ir; extern unsigned long long fmemcpy_max_count; extern unsigned long fpasses; extern int funsigned_char; extern int arch_m64; extern int arch_big_endian; extern int arch_mach; 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 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.1/linearize.c000066400000000000000000001763241355073133200151600ustar00rootroot00000000000000/* * 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 add_setval(struct entrypoint *ep, struct symbol *ctype, struct expression *val); static pseudo_t linearize_one_symbol(struct entrypoint *ep, struct symbol *sym); struct access_data; static pseudo_t add_load(struct entrypoint *ep, struct access_data *); static pseudo_t linearize_initializer(struct entrypoint *ep, struct expression *initializer, struct access_data *); static pseudo_t cast_pseudo(struct entrypoint *ep, pseudo_t src, struct symbol *from, struct symbol *to); struct pseudo void_pseudo = {}; static struct position current_pos; ALLOCATOR(pseudo_user, "pseudo_user"); static struct instruction *alloc_instruction(int opcode, int size) { struct instruction * insn = __alloc_instruction(0); insn->opcode = opcode; insn->size = size; insn->pos = current_pos; return insn; } static inline int type_size(struct symbol *type) { return type ? type->bit_size > 0 ? type->bit_size : 0 : 0; } static struct instruction *alloc_typed_instruction(int opcode, struct symbol *type) { struct instruction *insn = alloc_instruction(opcode, type_size(type)); insn->type = type; return insn; } static struct entrypoint *alloc_entrypoint(void) { return __alloc_entrypoint(0); } static struct basic_block *alloc_basic_block(struct entrypoint *ep, struct position pos) { static int nr; struct basic_block *bb = __alloc_basic_block(0); bb->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_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", /* Memory */ [OP_LOAD] = "load", [OP_STORE] = "store", [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_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_PHISOURCE: { struct instruction *phi; buf += sprintf(buf, "%s <- %s ", show_pseudo(insn->target), show_pseudo(insn->phi_src)); FOR_EACH_PTR(insn->phi_users, phi) { buf += sprintf(buf, " (%s)", show_pseudo(phi->target)); } END_FOR_EACH_PTR(phi); break; } case OP_PHI: { pseudo_t phi; const char *s = " <-"; buf += sprintf(buf, "%s", show_pseudo(insn->target)); FOR_EACH_PTR(insn->phi_list, phi) { 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 <- %d[%s]", show_pseudo(insn->target), insn->offset, show_pseudo(insn->src)); break; case OP_STORE: buf += sprintf(buf, "%s -> %d[%s]", show_pseudo(insn->target), insn->offset, show_pseudo(insn->src)); break; case OP_INLINED_CALL: case OP_CALL: { struct pseudo *arg; if (insn->target && insn->target != VOID) buf += sprintf(buf, "%s <- ", show_pseudo(insn->target)); buf += sprintf(buf, "%s", show_pseudo(insn->func)); FOR_EACH_PTR(insn->arguments, arg) { buf += sprintf(buf, ", %s", show_pseudo(arg)); } END_FOR_EACH_PTR(arg); break; } case OP_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: buf += sprintf(buf, "%s <- %s, %s, %s", show_pseudo(insn->target), show_pseudo(insn->src1), show_pseudo(insn->src2), show_pseudo(insn->src3)); break; case OP_SLICE: buf += sprintf(buf, "%s <- %s, %d, %d", show_pseudo(insn->target), show_pseudo(insn->base), insn->from, insn->len); break; case OP_NOT: case OP_NEG: 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"); } static void show_symbol_usage(pseudo_t pseudo) { struct pseudo_user *pu; if (pseudo) { FOR_EACH_PTR(pseudo->users, pu) { printf("\t%s\n", show_instruction(pu->insn)); } END_FOR_EACH_PTR(pu); } } void show_entry(struct entrypoint *ep) { struct symbol *sym; struct basic_block *bb; printf("%s:\n", show_ident(ep->name->ident)); if (verbose) { printf("ep %p: %s\n", ep, show_ident(ep->name->ident)); FOR_EACH_PTR(ep->syms, sym) { if (!sym->pseudo) continue; if (!sym->pseudo->users) continue; printf(" sym: %p %s\n", sym, show_ident(sym->ident)); if (sym->ctype.modifiers & (MOD_EXTERN | MOD_STATIC | MOD_ADDRESSABLE)) printf("\texternal visibility\n"); show_symbol_usage(sym->pseudo); } END_FOR_EACH_PTR(sym); printf("\n"); } FOR_EACH_PTR(ep->bbs, bb) { if (!bb) continue; if (!bb->parents && !bb->children && !bb->insns && verbose < 2) continue; show_bb(bb); printf("\n"); } END_FOR_EACH_PTR(bb); printf("\n"); } static void bind_label(struct symbol *label, struct basic_block *bb, struct position pos) { if (label->bb_target) warning(pos, "label '%s' already bound", show_ident(label->ident)); label->bb_target = bb; } static struct basic_block * get_bound_block(struct entrypoint *ep, struct symbol *label) { struct basic_block *bb = label->bb_target; if (!bb) { bb = alloc_basic_block(ep, label->pos); label->bb_target = bb; } return bb; } static void finish_block(struct entrypoint *ep) { struct basic_block *src = ep->active; if (bb_reachable(src)) ep->active = NULL; } static void add_goto(struct entrypoint *ep, struct basic_block *dst) { struct basic_block *src = ep->active; if (bb_reachable(src)) { struct instruction *br = alloc_instruction(OP_BR, 0); br->bb_true = dst; add_bb(&dst->parents, src); add_bb(&src->children, dst); br->bb = src; add_instruction(&src->insns, br); ep->active = NULL; } } static void add_one_insn(struct entrypoint *ep, struct instruction *insn) { struct basic_block *bb = ep->active; if (bb_reachable(bb)) { insn->bb = bb; add_instruction(&bb->insns, insn); } } static void set_activeblock(struct entrypoint *ep, struct basic_block *bb) { if (!bb_terminated(ep->active)) add_goto(ep, bb); ep->active = bb; if (bb_reachable(bb)) add_bb(&ep->bbs, bb); } static void remove_parent(struct basic_block *child, struct basic_block *parent) { remove_bb_from_list(&child->parents, parent, 1); if (!child->parents) repeat_phase |= REPEAT_CFG_CLEANUP; } /* Change a "switch" or a conditional branch into a branch */ void insert_branch(struct basic_block *bb, struct instruction *jmp, struct basic_block *target) { struct instruction *br, *old; struct basic_block *child; /* Remove the switch */ old = delete_last_instruction(&bb->insns); assert(old == jmp); kill_instruction(old); br = alloc_instruction(OP_BR, 0); br->bb = bb; br->bb_true = target; add_instruction(&bb->insns, br); FOR_EACH_PTR(bb->children, child) { if (child == target) { target = NULL; /* Trigger just once */ continue; } DELETE_CURRENT_PTR(child); remove_parent(child, bb); } END_FOR_EACH_PTR(child); PACK_PTR_LIST(&bb->children); } void insert_select(struct basic_block *bb, struct instruction *br, struct instruction *phi_node, pseudo_t if_true, pseudo_t if_false) { pseudo_t target; struct instruction *select; /* Remove the 'br' */ delete_last_instruction(&bb->insns); select = alloc_typed_instruction(OP_SEL, phi_node->type); select->bb = bb; assert(br->cond); use_pseudo(select, br->cond, &select->src1); target = phi_node->target; assert(target->def == phi_node); select->target = target; target->def = select; use_pseudo(select, if_true, &select->src2); use_pseudo(select, if_false, &select->src3); add_instruction(&bb->insns, select); add_instruction(&bb->insns, br); } static inline int bb_empty(struct basic_block *bb) { return !bb->insns; } /* Add a label to the currently active block, return new active block */ static struct basic_block * add_label(struct entrypoint *ep, struct symbol *label) { struct basic_block *bb = label->bb_target; if (bb) { set_activeblock(ep, bb); return bb; } bb = ep->active; if (!bb_reachable(bb) || !bb_empty(bb)) { bb = alloc_basic_block(ep, label->pos); set_activeblock(ep, bb); } label->bb_target = bb; return bb; } static void add_branch(struct entrypoint *ep, 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 .. unsigned int 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) return base->ctype.base_type; } return sym; } static int linearize_address_gen(struct entrypoint *ep, struct expression *expr, struct access_data *ad) { struct symbol *ctype = expr->ctype; if (!ctype) return 0; ad->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_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 symbol *sym) { struct instruction *insn = alloc_instruction(OP_SYMADDR, bits_in_pointer); pseudo_t target = alloc_pseudo(insn); insn->target = target; use_pseudo(insn, symbol_pseudo(ep, sym), &insn->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->len = expr->r_nrbits; use_pseudo(insn, pre, &insn->base); add_one_insn(ep, insn); return new; } static pseudo_t linearize_regular_preop(struct entrypoint *ep, struct expression *expr) { pseudo_t pre = linearize_expression(ep, expr->unop); struct symbol *ctype = expr->ctype; switch (expr->op) { case '+': return pre; case '!': { pseudo_t zero = value_pseudo(0); return add_binary_op(ep, ctype, OP_SET_EQ, 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_binary_op(ep, &bool_ctype, op, src, zero); } static pseudo_t linearize_expression_to_bool(struct entrypoint *ep, struct expression *expr) { pseudo_t dst; dst = linearize_expression(ep, expr); dst = add_convert_to_bool(ep, dst, expr->ctype); return dst; } static pseudo_t linearize_assignment(struct entrypoint *ep, struct expression *expr) { struct access_data ad = { NULL, }; struct expression *target = expr->left; struct expression *src = expr->right; struct symbol *ctype; pseudo_t value; value = linearize_expression(ep, src); if (!target || !linearize_address_gen(ep, target, &ad)) return value; if (expr->op != '=') { pseudo_t oldvalue = linearize_load_gen(ep, &ad); pseudo_t dst; static const int op_trans[] = { [SPECIAL_ADD_ASSIGN - SPECIAL_BASE] = OP_ADD, [SPECIAL_SUB_ASSIGN - SPECIAL_BASE] = OP_SUB, [SPECIAL_MUL_ASSIGN - SPECIAL_BASE] = OP_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 = alloc_typed_instruction(OP_CALL, expr->ctype); 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; ctype = &fntype->ctype; if (fntype->type == SYM_NODE) fntype = fntype->ctype.base_type; 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); } 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; if (phi2 == VOID) return phi1; phi_node = alloc_typed_instruction(OP_PHI, expr->ctype); use_pseudo(phi_node, phi1, add_pseudo(&phi_node->phi_list, phi1)); use_pseudo(phi_node, phi2, add_pseudo(&phi_node->phi_list, phi2)); phi_node->target = target = alloc_pseudo(phi_node); add_one_insn(ep, phi_node); return target; } static pseudo_t linearize_short_conditional(struct entrypoint *ep, struct expression *expr, struct expression *cond, struct expression *expr_false) { pseudo_t src1, src2; struct basic_block *bb_false; struct basic_block *merge; 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 *br = delete_last_instruction(&parent->insns); pseudo_t phi = alloc_phi(parent, src, ctype); add_instruction(&parent->insns, br); use_pseudo(node, phi, add_pseudo(&node->phi_list, phi)); } 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); use_pseudo(node, phi2, add_pseudo(&node->phi_list, 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, }; int op = opcode_float(cmpop[expr->op], expr->right->ctype); pseudo_t src1 = linearize_expression(ep, expr->left); pseudo_t src2 = linearize_expression(ep, expr->right); pseudo_t dst = add_binary_op(ep, expr->ctype, op, 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); return VOID; case EXPR_FVALUE: add_goto(ep, expr->fvalue ? bb_true : bb_false); return VOID; case EXPR_LOGICAL: linearize_logical_branch(ep, expr, bb_true, bb_false); return VOID; case EXPR_COMPARE: cond = linearize_compare(ep, expr); add_branch(ep, 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); return VOID; } } return VOID; } static pseudo_t linearize_logical_branch(struct entrypoint *ep, struct expression *expr, struct basic_block *bb_true, struct basic_block *bb_false) { struct basic_block *next = alloc_basic_block(ep, expr->pos); if (expr->op == SPECIAL_LOGICAL_OR) linearize_cond_branch(ep, expr->left, bb_true, next); else linearize_cond_branch(ep, expr->left, next, bb_false); set_activeblock(ep, next); linearize_cond_branch(ep, expr->right, bb_true, bb_false); return VOID; } static pseudo_t linearize_cast(struct entrypoint *ep, struct expression *expr) { pseudo_t src; struct expression *orig = expr->cast_expression; if (!orig) return VOID; src = linearize_expression(ep, orig); return cast_pseudo(ep, src, orig->ctype, expr->ctype); } static pseudo_t linearize_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->symbol); 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; use_pseudo(phi_node, phi, add_pseudo(&phi_node->phi_list, 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; use_pseudo(insn, symbol_pseudo(ep, stmt->inline_fn), &insn->func); 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_input(struct entrypoint *ep, struct instruction *insn, struct asm_operand *op) { pseudo_t pseudo = linearize_expression(ep, op->expr); struct asm_constraint *rule = __alloc_asm_constraint(0); rule->ident = op->name; rule->constraint = op->constraint ? op->constraint->string->data : ""; use_pseudo(insn, pseudo, &rule->pseudo); add_ptr_list(&insn->asm_rules->inputs, rule); } static void add_asm_output(struct entrypoint *ep, struct instruction *insn, struct asm_operand *op) { struct access_data ad = { NULL, }; pseudo_t pseudo; struct asm_constraint *rule; if (op->is_memory) { pseudo = linearize_expression(ep, op->expr); } else { if (!linearize_address_gen(ep, op->expr, &ad)) return; pseudo = alloc_pseudo(insn); linearize_store_gen(ep, pseudo, &ad); } 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(&insn->asm_rules->outputs, rule); } static pseudo_t linearize_asm_statement(struct entrypoint *ep, struct statement *stmt) { struct instruction *insn; struct expression *expr; 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); 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); 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 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) 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); return ep; } struct entrypoint *linearize_symbol(struct symbol *sym) { struct symbol *base_type; if (!sym) return NULL; current_pos = sym->pos; base_type = sym->ctype.base_type; if (!base_type) return NULL; if (base_type->type == SYM_FN) return linearize_fn(sym, base_type); return NULL; } sparse-0.6.1/linearize.h000066400000000000000000000175461355073133200151650ustar00rootroot00000000000000#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 *, pseudo_t); enum pseudo_type { PSEUDO_VOID, PSEUDO_UNDEF, PSEUDO_REG, PSEUDO_SYM, PSEUDO_VAL, PSEUDO_ARG, PSEUDO_PHI, }; struct pseudo { int nr; enum pseudo_type type; struct pseudo_user_list *users; struct ident *ident; union { struct symbol *sym; struct instruction *def; long long value; }; void *priv; }; extern struct pseudo void_pseudo; #define VOID (&void_pseudo) 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; } 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_list *phi_users; }; struct /* unops */ { pseudo_t src; struct symbol *orig_type; /* casts */ }; struct /* memops */ { pseudo_t addr; /* alias .src */ unsigned int offset; unsigned int is_volatile:1; }; struct /* binops and sel */ { pseudo_t src1, src2, src3; }; struct /* slice */ { pseudo_t base; unsigned from, len; }; 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; }; }; }; struct basic_block_list; struct instruction_list; struct basic_block { struct position pos; unsigned long generation; union { int context; int postorder_nr; /* postorder number */ int dom_level; /* level in the dominance tree */ }; 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 */ struct basic_block_list *doms; /* list of BB idominated by this one */ struct phi_map *phi_map; struct pseudo_list *needs, *defines; union { unsigned int nr; /* unique id for label's names */ 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 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 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 multi_users(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 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); extern void insert_branch(struct basic_block *bb, struct instruction *br, struct basic_block *target); 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); const char *show_pseudo(pseudo_t pseudo); void show_bb(struct basic_block *bb); const char *show_instruction(struct instruction *insn); const char *show_label(struct basic_block *bb); #endif /* LINEARIZE_H */ sparse-0.6.1/liveness.c000066400000000000000000000162611355073133200150170ustar00rootroot00000000000000/* * 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: USES(src1); DEFINES(target); break; case OP_SEL: USES(src1); USES(src2); USES(src3); DEFINES(target); break; /* Memory */ case OP_LOAD: USES(src); DEFINES(target); break; case OP_STORE: USES(src); USES(target); break; case OP_SETVAL: 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_SLICE: USES(base); DEFINES(target); break; case OP_ASM: asm_liveness(bb, insn, def, use); break; case OP_RANGE: USES(src1); USES(src2); USES(src3); break; case OP_BADOP: case OP_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); add_ptr_list(&def->phi_users, insn); } END_FOR_EACH_PTR(phi); } static void track_bb_phi_uses(struct basic_block *bb) { struct instruction *insn; FOR_EACH_PTR(bb->insns, insn) { if (insn->bb && insn->opcode == OP_PHI) track_phi_uses(insn); } END_FOR_EACH_PTR(insn); } static struct pseudo_list **live_list; static struct pseudo_list *dead_list; static void death_def(struct basic_block *bb, pseudo_t pseudo) { } static void death_use(struct basic_block *bb, pseudo_t pseudo) { if (trackable_pseudo(pseudo) && !pseudo_in_list(*live_list, pseudo)) { add_pseudo(&dead_list, pseudo); add_pseudo(live_list, pseudo); } } static void track_pseudo_death_bb(struct basic_block *bb) { struct pseudo_list *live = NULL; struct basic_block *child; struct instruction *insn; FOR_EACH_PTR(bb->children, child) { merge_pseudo_list(child->needs, &live); } END_FOR_EACH_PTR(child); live_list = &live; FOR_EACH_PTR_REVERSE(bb->insns, insn) { if (!insn->bb) continue; dead_list = NULL; track_instruction_usage(bb, insn, death_def, death_use); if (dead_list) { pseudo_t dead; FOR_EACH_PTR(dead_list, dead) { struct instruction *deathnote = __alloc_instruction(0); deathnote->bb = bb; deathnote->opcode = OP_DEATHNOTE; deathnote->target = dead; INSERT_CURRENT(deathnote, insn); } END_FOR_EACH_PTR(dead); free_ptr_list(&dead_list); } } END_FOR_EACH_PTR_REVERSE(insn); free_ptr_list(&live); } void track_pseudo_death(struct entrypoint *ep) { struct basic_block *bb; FOR_EACH_PTR(ep->bbs, bb) { track_bb_phi_uses(bb); } END_FOR_EACH_PTR(bb); FOR_EACH_PTR(ep->bbs, bb) { track_pseudo_death_bb(bb); } END_FOR_EACH_PTR(bb); } sparse-0.6.1/liveness.h000066400000000000000000000003431355073133200150160ustar00rootroot00000000000000#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.1/machine.h000066400000000000000000000034451355073133200146000ustar00rootroot00000000000000#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 { 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_M68K, MACH_S390X, MACH_UNKNOWN }; #if defined(__aarch64__) #define MACH_NATIVE MACH_ARM64 #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__) #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 #else #define MACH_NATIVE MACH_UNKNOWN #endif #if defined(__CHAR_UNSIGNED__) #define UNSIGNED_CHAR 1 #else #define UNSIGNED_CHAR 0 #endif #endif sparse-0.6.1/memops.c000066400000000000000000000117751355073133200144740ustar00rootroot00000000000000/* * memops - try to combine memory ops. * * Copyright (C) 2004 Linus Torvalds */ #include #include #include #include #include #include #include "parse.h" #include "expression.h" #include "linearize.h" #include "flow.h" static int find_dominating_parents(pseudo_t pseudo, struct instruction *insn, struct basic_block *bb, unsigned long generation, struct pseudo_list **dominators, int local) { struct basic_block *parent; FOR_EACH_PTR(bb->parents, parent) { struct instruction *one; struct instruction *br; pseudo_t phi; FOR_EACH_PTR_REVERSE(parent->insns, one) { int dominance; if (!one->bb) continue; if (one == insn) goto no_dominance; dominance = dominates(pseudo, insn, one, local); if (dominance < 0) { if (one->opcode == OP_LOAD) continue; return 0; } if (!dominance) continue; goto found_dominator; } END_FOR_EACH_PTR_REVERSE(one); no_dominance: if (parent->generation == generation) continue; parent->generation = generation; if (!find_dominating_parents(pseudo, insn, parent, generation, dominators, local)) return 0; continue; found_dominator: br = delete_last_instruction(&parent->insns); phi = alloc_phi(parent, one->target, one->type); phi->ident = phi->ident ? : one->target->ident; add_instruction(&parent->insns, br); use_pseudo(insn, phi, add_pseudo(dominators, phi)); } END_FOR_EACH_PTR(parent); return 1; } static int address_taken(pseudo_t pseudo) { struct pseudo_user *pu; FOR_EACH_PTR(pseudo->users, pu) { struct instruction *insn = pu->insn; if (insn->bb && (insn->opcode != OP_LOAD && insn->opcode != OP_STORE)) return 1; 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 void simplify_loads(struct basic_block *bb) { struct instruction *insn; FOR_EACH_PTR_REVERSE(bb->insns, insn) { if (!insn->bb) continue; if (insn->opcode == OP_LOAD) { struct instruction *dom; pseudo_t pseudo = insn->src; int local = local_pseudo(pseudo); struct pseudo_list *dominators; unsigned long generation; /* Check for illegal offsets.. */ check_access(insn); if (insn->is_volatile) continue; RECURSE_PTR_REVERSE(insn, dom) { int dominance; if (!dom->bb) continue; dominance = dominates(pseudo, insn, dom, local); if (dominance) { /* possible partial dominance? */ if (dominance < 0) { if (dom->opcode == OP_LOAD) continue; goto next_load; } /* Yeehaa! Found one! */ convert_load_instruction(insn, dom->target); goto next_load; } } END_FOR_EACH_PTR_REVERSE(dom); /* OK, go find the parents */ generation = ++bb_generation; bb->generation = generation; dominators = NULL; if (find_dominating_parents(pseudo, insn, bb, generation, &dominators, local)) { /* This happens with initial assignments to structures etc.. */ if (!dominators) { if (local) { assert(pseudo->type != PSEUDO_ARG); convert_load_instruction(insn, value_pseudo(0)); } goto next_load; } rewrite_load_instruction(insn, dominators); } else { // cleanup pending phi-sources pseudo_t phi; FOR_EACH_PTR(dominators, phi) { kill_instruction(phi->def); } END_FOR_EACH_PTR(phi); } } next_load: /* Do the next one */; } END_FOR_EACH_PTR_REVERSE(insn); } static void kill_dominated_stores(struct basic_block *bb) { struct instruction *insn; FOR_EACH_PTR_REVERSE(bb->insns, insn) { if (!insn->bb) continue; if (insn->opcode == OP_STORE) { struct instruction *dom; pseudo_t pseudo = insn->src; int local; if (!insn->type) continue; if (insn->is_volatile) continue; local = local_pseudo(pseudo); RECURSE_PTR_REVERSE(insn, dom) { int dominance; if (!dom->bb) continue; dominance = dominates(pseudo, insn, dom, local); if (dominance) { /* possible partial dominance? */ if (dominance < 0) goto next_store; if (dom->opcode == OP_LOAD) goto next_store; /* Yeehaa! Found one! */ kill_instruction_force(dom); } } END_FOR_EACH_PTR_REVERSE(dom); /* OK, we should check the parents now */ } next_store: /* Do the next one */; } END_FOR_EACH_PTR_REVERSE(insn); } void simplify_memops(struct entrypoint *ep) { struct basic_block *bb; 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.1/obfuscate.c000066400000000000000000000042431355073133200151370ustar00rootroot00000000000000/* * 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.1/opcode.c000066400000000000000000000026631355073133200144410ustar00rootroot00000000000000/* * 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,TF,N,FL) \ [OP_##OP] = { \ .negate = OP_##NG, \ .swap = OP_##SW, \ .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.1/opcode.def000066400000000000000000000134451355073133200147550ustar00rootroot00000000000000// OPCODE negated swaped float arity, flags OPCODE(BADOP, BADOP, BADOP, BADOP, 0, OPF_NONE) /* Entry */ OPCODE(ENTRY, BADOP, BADOP, BADOP, 0, OPF_NONE) /* Terminator */ OPCODE(RET, BADOP, BADOP, BADOP, 1, OPF_NONE) OPCODE(BR, BADOP, BADOP, BADOP, 0, OPF_NONE) OPCODE(CBR, BADOP, BADOP, BADOP, 1, OPF_NONE) OPCODE(SWITCH, BADOP, BADOP, BADOP, 1, OPF_NONE) OPCODE(COMPUTEDGOTO, BADOP, BADOP, BADOP, 1, OPF_NONE) OPCODE_RANGE(TERMINATOR, RET, COMPUTEDGOTO) /* Binary */ OPCODE(ADD, BADOP, BADOP, FADD, 2, OPF_TARGET) OPCODE(SUB, BADOP, BADOP, FSUB, 2, OPF_TARGET) OPCODE(MUL, BADOP, BADOP, FMUL, 2, OPF_TARGET) OPCODE(DIVU, BADOP, BADOP, FDIV, 2, OPF_TARGET) OPCODE(DIVS, BADOP, BADOP, FDIV, 2, OPF_TARGET) OPCODE(MODU, BADOP, BADOP, BADOP, 2, OPF_TARGET) OPCODE(MODS, BADOP, BADOP, BADOP, 2, OPF_TARGET) OPCODE(SHL, BADOP, BADOP, BADOP, 2, OPF_TARGET) OPCODE(LSR, BADOP, BADOP, BADOP, 2, OPF_TARGET) OPCODE(ASR, BADOP, BADOP, BADOP, 2, OPF_TARGET) /* Floating-point binops */ OPCODE(FADD, BADOP, BADOP, BADOP, 2, OPF_TARGET) OPCODE(FSUB, BADOP, BADOP, BADOP, 2, OPF_TARGET) OPCODE(FMUL, BADOP, BADOP, BADOP, 2, OPF_TARGET) OPCODE(FDIV, BADOP, BADOP, BADOP, 2, OPF_TARGET) /* Logical */ OPCODE(AND_BOOL, BADOP, BADOP, BADOP, 2, OPF_TARGET) OPCODE(OR_BOOL, BADOP, BADOP, BADOP, 2, OPF_TARGET) OPCODE(AND, BADOP, BADOP, BADOP, 2, OPF_TARGET) OPCODE(OR, BADOP, BADOP, BADOP, 2, OPF_TARGET) OPCODE(XOR, BADOP, BADOP, BADOP, 2, OPF_TARGET) OPCODE_RANGE(BINARY, ADD, XOR) /* floating-point comparison */ OPCODE(FCMP_ORD, FCMP_UNO, FCMP_ORD, BADOP, 2, OPF_TARGET) OPCODE(FCMP_OEQ, FCMP_UNE, FCMP_OEQ, BADOP, 2, OPF_TARGET) OPCODE(FCMP_ONE, FCMP_UEQ, FCMP_ONE, BADOP, 2, OPF_TARGET) OPCODE(FCMP_UEQ, FCMP_ONE, FCMP_UEQ, BADOP, 2, OPF_TARGET) OPCODE(FCMP_UNE, FCMP_OEQ, FCMP_UNE, BADOP, 2, OPF_TARGET) OPCODE(FCMP_OLT, FCMP_UGE, FCMP_OGT, BADOP, 2, OPF_TARGET) OPCODE(FCMP_OLE, FCMP_UGT, FCMP_OGE, BADOP, 2, OPF_TARGET) OPCODE(FCMP_OGE, FCMP_ULT, FCMP_OLE, BADOP, 2, OPF_TARGET) OPCODE(FCMP_OGT, FCMP_ULE, FCMP_OLT, BADOP, 2, OPF_TARGET) OPCODE(FCMP_ULT, FCMP_OGE, FCMP_UGT, BADOP, 2, OPF_TARGET) OPCODE(FCMP_ULE, FCMP_OGT, FCMP_UGE, BADOP, 2, OPF_TARGET) OPCODE(FCMP_UGE, FCMP_OLT, FCMP_ULE, BADOP, 2, OPF_TARGET) OPCODE(FCMP_UGT, FCMP_OLE, FCMP_ULT, BADOP, 2, OPF_TARGET) OPCODE(FCMP_UNO, FCMP_ORD, FCMP_UNO, BADOP, 2, OPF_TARGET) OPCODE_RANGE(FPCMP, FCMP_ORD, FCMP_UNO) /* Binary comparison */ OPCODE(SET_EQ, SET_NE, SET_EQ, FCMP_OEQ, 2, OPF_TARGET) OPCODE(SET_LT, SET_GE, SET_GT, FCMP_OLT, 2, OPF_TARGET) OPCODE(SET_LE, SET_GT, SET_GE, FCMP_OLE, 2, OPF_TARGET) OPCODE(SET_GE, SET_LT, SET_LE, FCMP_OGE, 2, OPF_TARGET) OPCODE(SET_GT, SET_LE, SET_LT, FCMP_OGT, 2, OPF_TARGET) OPCODE(SET_B, SET_AE, SET_A, FCMP_OLT, 2, OPF_TARGET) OPCODE(SET_BE, SET_A, SET_AE, FCMP_OLE, 2, OPF_TARGET) OPCODE(SET_AE, SET_B, SET_BE, FCMP_OGE, 2, OPF_TARGET) OPCODE(SET_A, SET_BE, SET_B, FCMP_OGT, 2, OPF_TARGET) OPCODE(SET_NE, SET_EQ, SET_NE, FCMP_UNE, 2, OPF_TARGET) OPCODE_RANGE(BINCMP, SET_EQ, SET_NE) /* Uni */ OPCODE(NOT, BADOP, BADOP, BADOP, 1, OPF_TARGET) OPCODE(NEG, BADOP, BADOP, FNEG, 1, OPF_TARGET) OPCODE(FNEG, BADOP, BADOP, BADOP, 1, OPF_TARGET) OPCODE(TRUNC, BADOP, BADOP, BADOP, 1, OPF_TARGET) OPCODE(ZEXT, BADOP, BADOP, BADOP, 1, OPF_TARGET) OPCODE(SEXT, BADOP, BADOP, BADOP, 1, OPF_TARGET) OPCODE(FCVTU, BADOP, BADOP, BADOP, 1, OPF_TARGET) OPCODE(FCVTS, BADOP, BADOP, BADOP, 1, OPF_TARGET) OPCODE(UCVTF, BADOP, BADOP, BADOP, 1, OPF_TARGET) OPCODE(SCVTF, BADOP, BADOP, BADOP, 1, OPF_TARGET) OPCODE(FCVTF, BADOP, BADOP, BADOP, 1, OPF_TARGET) OPCODE(UTPTR, BADOP, BADOP, BADOP, 1, OPF_TARGET) OPCODE(PTRTU, BADOP, BADOP, BADOP, 1, OPF_TARGET) OPCODE(PTRCAST, BADOP, BADOP, BADOP, 1, OPF_TARGET) OPCODE_RANGE(UNOP, NOT, PTRCAST) OPCODE(SYMADDR, BADOP, BADOP, BADOP, 1, OPF_TARGET) OPCODE(SLICE, BADOP, BADOP, BADOP, 1, OPF_TARGET) /* Select - three input values */ OPCODE(SEL, BADOP, BADOP, BADOP, 3, OPF_TARGET) /* Memory */ OPCODE(LOAD, BADOP, BADOP, BADOP, 1, OPF_TARGET) OPCODE(STORE, BADOP, BADOP, BADOP, 1, OPF_NONE) /* Other */ OPCODE(PHISOURCE, BADOP, BADOP, BADOP, 1, OPF_TARGET) OPCODE(PHI, BADOP, BADOP, BADOP, 0, OPF_TARGET) OPCODE(SETVAL, BADOP, BADOP, BADOP, 0, OPF_TARGET) OPCODE(SETFVAL, BADOP, BADOP, BADOP, 0, OPF_TARGET) OPCODE(CALL, BADOP, BADOP, BADOP, 1, OPF_TARGET) OPCODE(INLINED_CALL, BADOP, BADOP, BADOP, 0, OPF_NONE) OPCODE(NOP, BADOP, BADOP, BADOP, 0, OPF_NONE) OPCODE(DEATHNOTE, BADOP, BADOP, BADOP, 0, OPF_NONE) OPCODE(ASM, BADOP, BADOP, BADOP, 0, OPF_NONE) /* Sparse tagging (line numbers, context, whatever) */ OPCODE(CONTEXT, BADOP, BADOP, BADOP, 0, OPF_NONE) OPCODE(RANGE, BADOP, BADOP, BADOP, 3, OPF_NONE) /* Needed to translate SSA back to normal form */ OPCODE(COPY, BADOP, BADOP, BADOP, 1, OPF_TARGET) sparse-0.6.1/opcode.h000066400000000000000000000012111355073133200144320ustar00rootroot00000000000000#ifndef OPCODE_H #define OPCODE_H #include "symbol.h" enum opcode { #define OPCODE(OP,NG,SW,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 to_float:8; unsigned int arity:2; unsigned int flags:6; #define OPF_NONE 0 #define OPF_TARGET (1 << 0) } opcode_table[]; 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.1/optimize.c000066400000000000000000000046661355073133200150350ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // optimize.c - main optimization loop // // Copyright (C) 2004 Linus Torvalds // Copyright (C) 2004 Christopher Li #include #include "optimize.h" #include "flowgraph.h" #include "linearize.h" #include "liveness.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) { 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); } 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); 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); //ir_validate(ep); do { repeat_phase = 0; clean_up_insns(ep); if (repeat_phase & REPEAT_CFG_CLEANUP) kill_unreachable_bbs(ep); cse_eliminate(ep); if (repeat_phase & REPEAT_SYMBOL_CLEANUP) simplify_memops(ep); //ir_validate(ep); } while (repeat_phase); pack_basic_blocks(ep); //ir_validate(ep); if (repeat_phase & REPEAT_CFG_CLEANUP) kill_unreachable_bbs(ep); //ir_validate(ep); } while (repeat_phase); //ir_validate(ep); vrfy_flow(ep); /* Cleanup */ clear_symbol_pseudos(ep); /* And track pseudo register usage */ track_pseudo_liveness(ep); /* * Some flow optimizations can only effectively * be done when we've done liveness analysis. But * if they trigger, we need to start all over * again */ if (simplify_flow(ep)) { //ir_validate(ep); clear_liveness(ep); if (repeat_phase & REPEAT_CFG_CLEANUP) kill_unreachable_bbs(ep); goto repeat; } //ir_validate(ep); /* Finally, add deathnotes to pseudos now that we have them */ if (dbg_dead) track_pseudo_death(ep); } sparse-0.6.1/optimize.h000066400000000000000000000001721355073133200150260ustar00rootroot00000000000000#ifndef OPTIMIZE_H #define OPTIMIZE_H struct entrypoint; /* optimize.c */ void optimize(struct entrypoint *ep); #endif sparse-0.6.1/parse.c000066400000000000000000002474241355073133200143100ustar00rootroot00000000000000/* * Stupid C parser, version 1e-6. * * Let's see how hard this is to do. * * Copyright (C) 2003 Transmeta Corp. * 2003-2004 Linus Torvalds * Copyright (C) 2004 Christopher Li * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include #include #include #include #include #include #include "lib.h" #include "allocate.h" #include "token.h" #include "parse.h" #include "symbol.h" #include "scope.h" #include "expression.h" #include "target.h" static struct symbol_list **function_symbol_list; struct symbol_list *function_computed_target_list; struct statement_list *function_computed_goto_list; static struct token *statement(struct token *token, struct statement **tree); static struct token *handle_attributes(struct token *token, struct decl_state *ctx, unsigned int keywords); typedef struct token *declarator_t(struct token *, struct decl_state *); static declarator_t struct_specifier, union_specifier, enum_specifier, attribute_specifier, typeof_specifier, parse_asm_declarator, typedef_specifier, inline_specifier, auto_specifier, register_specifier, static_specifier, extern_specifier, thread_specifier, const_qualifier, volatile_qualifier; static declarator_t restrict_qualifier; static declarator_t atomic_qualifier; static struct token *parse_if_statement(struct token *token, struct statement *stmt); static struct token *parse_return_statement(struct token *token, struct statement *stmt); static struct token *parse_loop_iterator(struct token *token, struct statement *stmt); static struct token *parse_default_statement(struct token *token, struct statement *stmt); static struct token *parse_case_statement(struct token *token, struct statement *stmt); static struct token *parse_switch_statement(struct token *token, struct statement *stmt); static struct token *parse_for_statement(struct token *token, struct statement *stmt); static struct token *parse_while_statement(struct token *token, struct statement *stmt); static struct token *parse_do_statement(struct token *token, struct statement *stmt); static struct token *parse_goto_statement(struct token *token, struct statement *stmt); static struct token *parse_context_statement(struct token *token, struct statement *stmt); static struct token *parse_range_statement(struct token *token, struct statement *stmt); static struct token *parse_asm_statement(struct token *token, struct statement *stmt); static struct token *toplevel_asm_declaration(struct token *token, struct symbol_list **list); static struct token *parse_static_assert(struct token *token, struct symbol_list **unused); typedef struct token *attr_t(struct token *, struct symbol *, struct decl_state *); static attr_t attribute_packed, attribute_aligned, attribute_modifier, attribute_ext_visible, 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, CChar, CSChar, CUChar, }; enum { SNone = 0, STypedef, SAuto, SRegister, SExtern, SStatic, SForced, SMax, }; 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 void asm_modifier_volatile(struct token *token, unsigned long *mods) { asm_modifier(token, mods, MOD_VOLATILE); } static void asm_modifier_inline(struct token *token, unsigned long *mods) { asm_modifier(token, mods, MOD_INLINE); } static struct symbol_op typedef_op = { .type = KW_MODIFIER, .declarator = typedef_specifier, }; static struct symbol_op inline_op = { .type = KW_MODIFIER, .declarator = inline_specifier, .asm_modifier = asm_modifier_inline, }; static declarator_t noreturn_specifier; static struct symbol_op noreturn_op = { .type = KW_MODIFIER, .declarator = noreturn_specifier, }; static declarator_t alignas_specifier; static struct symbol_op alignas_op = { .type = KW_MODIFIER, .declarator = alignas_specifier, }; static struct symbol_op auto_op = { .type = KW_MODIFIER, .declarator = auto_specifier, }; static struct symbol_op register_op = { .type = KW_MODIFIER, .declarator = register_specifier, }; static struct symbol_op static_op = { .type = KW_MODIFIER, .declarator = static_specifier, }; static struct symbol_op extern_op = { .type = KW_MODIFIER, .declarator = extern_specifier, }; static struct symbol_op thread_op = { .type = KW_MODIFIER, .declarator = thread_specifier, }; static struct symbol_op const_op = { .type = KW_QUALIFIER, .declarator = const_qualifier, }; static struct symbol_op volatile_op = { .type = KW_QUALIFIER, .declarator = volatile_qualifier, .asm_modifier = asm_modifier_volatile, }; static struct symbol_op restrict_op = { .type = KW_QUALIFIER, .declarator = restrict_qualifier, }; static struct symbol_op atomic_op = { .type = KW_QUALIFIER, .declarator = atomic_qualifier, }; static struct symbol_op typeof_op = { .type = KW_SPECIFIER, .declarator = typeof_specifier, .test = Set_Any, .set = Set_S|Set_T, }; static struct symbol_op attribute_op = { .type = KW_ATTRIBUTE, .declarator = attribute_specifier, }; static struct symbol_op struct_op = { .type = KW_SPECIFIER, .declarator = struct_specifier, .test = Set_Any, .set = Set_S|Set_T, }; static struct symbol_op union_op = { .type = KW_SPECIFIER, .declarator = union_specifier, .test = Set_Any, .set = Set_S|Set_T, }; static struct symbol_op enum_op = { .type = KW_SPECIFIER, .declarator = enum_specifier, .test = Set_Any, .set = Set_S|Set_T, }; static struct symbol_op spec_op = { .type = KW_SPECIFIER | KW_EXACT, .test = Set_Any, .set = Set_S|Set_T, }; static struct symbol_op char_op = { .type = KW_SPECIFIER, .test = Set_T|Set_Long|Set_Short, .set = Set_T|Set_Char, .class = CChar, }; static struct symbol_op int_op = { .type = KW_SPECIFIER, .test = Set_T, .set = Set_T|Set_Int, }; static struct symbol_op double_op = { .type = KW_SPECIFIER, .test = Set_T|Set_Signed|Set_Unsigned|Set_Short|Set_Vlong, .set = Set_T|Set_Double, .class = CReal, }; static struct symbol_op float_op = { .type = KW_SPECIFIER | KW_SHORT, .test = Set_T|Set_Signed|Set_Unsigned|Set_Short|Set_Long, .set = Set_T|Set_Float, .class = CReal, }; static struct symbol_op short_op = { .type = KW_SPECIFIER | KW_SHORT, .test = Set_S|Set_Char|Set_Float|Set_Double|Set_Long|Set_Short, .set = Set_Short, }; static struct symbol_op signed_op = { .type = KW_SPECIFIER, .test = Set_S|Set_Float|Set_Double|Set_Signed|Set_Unsigned, .set = Set_Signed, .class = CSInt, }; static struct symbol_op unsigned_op = { .type = KW_SPECIFIER, .test = Set_S|Set_Float|Set_Double|Set_Signed|Set_Unsigned, .set = Set_Unsigned, .class = CUInt, }; static struct symbol_op long_op = { .type = KW_SPECIFIER | KW_LONG, .test = Set_S|Set_Char|Set_Float|Set_Short|Set_Vlong, .set = Set_Long, }; static struct symbol_op int128_op = { .type = KW_SPECIFIER | KW_LONG, .test = Set_S|Set_T|Set_Char|Set_Short|Set_Int|Set_Float|Set_Double|Set_Long|Set_Vlong|Set_Int128, .set = Set_T|Set_Int128, }; static struct symbol_op if_op = { .statement = parse_if_statement, }; static struct symbol_op return_op = { .statement = parse_return_statement, }; static struct symbol_op loop_iter_op = { .statement = parse_loop_iterator, }; static struct symbol_op default_op = { .statement = parse_default_statement, }; static struct symbol_op case_op = { .statement = parse_case_statement, }; static struct symbol_op switch_op = { .statement = parse_switch_statement, }; static struct symbol_op for_op = { .statement = parse_for_statement, }; static struct symbol_op while_op = { .statement = parse_while_statement, }; static struct symbol_op do_op = { .statement = parse_do_statement, }; static struct symbol_op goto_op = { .statement = parse_goto_statement, }; static struct symbol_op __context___op = { .statement = parse_context_statement, }; static struct symbol_op range_op = { .statement = parse_range_statement, }; static struct symbol_op asm_op = { .type = KW_ASM, .declarator = parse_asm_declarator, .statement = parse_asm_statement, .toplevel = toplevel_asm_declaration, }; static struct symbol_op static_assert_op = { .toplevel = parse_static_assert, }; static struct symbol_op packed_op = { .attribute = attribute_packed, }; static struct symbol_op aligned_op = { .attribute = attribute_aligned, }; static struct symbol_op attr_mod_op = { .attribute = attribute_modifier, }; static struct symbol_op ext_visible_op = { .attribute = attribute_ext_visible, }; 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 }; /* Using NS_TYPEDEF will also make the keyword a reserved one */ static struct init_keyword { const char *name; enum namespace ns; unsigned long modifiers; struct symbol_op *op; struct symbol *type; } keyword_table[] = { /* Type qualifiers */ { "const", NS_TYPEDEF, .op = &const_op }, { "__const", NS_TYPEDEF, .op = &const_op }, { "__const__", NS_TYPEDEF, .op = &const_op }, { "volatile", NS_TYPEDEF, .op = &volatile_op }, { "__volatile", NS_TYPEDEF, .op = &volatile_op }, { "__volatile__", NS_TYPEDEF, .op = &volatile_op }, { "restrict", NS_TYPEDEF, .op = &restrict_op}, { "__restrict", NS_TYPEDEF, .op = &restrict_op}, { "__restrict__", NS_TYPEDEF, .op = &restrict_op}, { "_Atomic", NS_TYPEDEF, .op = &atomic_op}, /* Typedef.. */ { "typedef", NS_TYPEDEF, .op = &typedef_op }, /* Type specifiers */ { "void", NS_TYPEDEF, .type = &void_ctype, .op = &spec_op}, { "char", NS_TYPEDEF, .op = &char_op }, { "short", NS_TYPEDEF, .op = &short_op }, { "int", NS_TYPEDEF, .op = &int_op }, { "long", NS_TYPEDEF, .op = &long_op }, { "float", NS_TYPEDEF, .op = &float_op }, { "double", NS_TYPEDEF, .op = &double_op }, { "signed", NS_TYPEDEF, .op = &signed_op }, { "__signed", NS_TYPEDEF, .op = &signed_op }, { "__signed__", NS_TYPEDEF, .op = &signed_op }, { "unsigned", NS_TYPEDEF, .op = &unsigned_op }, { "__int128", NS_TYPEDEF, .op = &int128_op }, { "_Bool", NS_TYPEDEF, .type = &bool_ctype, .op = &spec_op }, /* Predeclared types */ { "__builtin_va_list", NS_TYPEDEF, .type = &ptr_ctype, .op = &spec_op }, { "__builtin_ms_va_list", NS_TYPEDEF, .type = &ptr_ctype, .op = &spec_op }, { "__int128_t", NS_TYPEDEF, .type = &lllong_ctype, .op = &spec_op }, { "__uint128_t",NS_TYPEDEF, .type = &ulllong_ctype, .op = &spec_op }, { "_Float32", NS_TYPEDEF, .type = &float32_ctype, .op = &spec_op }, { "_Float32x", NS_TYPEDEF, .type = &float32x_ctype, .op = &spec_op }, { "_Float64", NS_TYPEDEF, .type = &float64_ctype, .op = &spec_op }, { "_Float64x", NS_TYPEDEF, .type = &float64x_ctype, .op = &spec_op }, { "_Float128", NS_TYPEDEF, .type = &float128_ctype, .op = &spec_op }, /* Extended types */ { "typeof", NS_TYPEDEF, .op = &typeof_op }, { "__typeof", NS_TYPEDEF, .op = &typeof_op }, { "__typeof__", NS_TYPEDEF, .op = &typeof_op }, { "__attribute", NS_TYPEDEF, .op = &attribute_op }, { "__attribute__", NS_TYPEDEF, .op = &attribute_op }, { "struct", NS_TYPEDEF, .op = &struct_op }, { "union", NS_TYPEDEF, .op = &union_op }, { "enum", NS_TYPEDEF, .op = &enum_op }, { "inline", NS_TYPEDEF, .op = &inline_op }, { "__inline", NS_TYPEDEF, .op = &inline_op }, { "__inline__", NS_TYPEDEF, .op = &inline_op }, { "_Noreturn", NS_TYPEDEF, .op = &noreturn_op }, { "_Alignas", NS_TYPEDEF, .op = &alignas_op }, /* Static assertion */ { "_Static_assert", NS_KEYWORD, .op = &static_assert_op }, /* Storage class */ { "auto", NS_TYPEDEF, .op = &auto_op }, { "register", NS_TYPEDEF, .op = ®ister_op }, { "static", NS_TYPEDEF, .op = &static_op }, { "extern", NS_TYPEDEF, .op = &extern_op }, { "__thread", NS_TYPEDEF, .op = &thread_op }, { "_Thread_local", NS_TYPEDEF, .op = &thread_op }, /* Statement */ { "if", NS_KEYWORD, .op = &if_op }, { "return", NS_KEYWORD, .op = &return_op }, { "break", NS_KEYWORD, .op = &loop_iter_op }, { "continue", NS_KEYWORD, .op = &loop_iter_op }, { "default", NS_KEYWORD, .op = &default_op }, { "case", NS_KEYWORD, .op = &case_op }, { "switch", NS_KEYWORD, .op = &switch_op }, { "for", NS_KEYWORD, .op = &for_op }, { "while", NS_KEYWORD, .op = &while_op }, { "do", NS_KEYWORD, .op = &do_op }, { "goto", NS_KEYWORD, .op = &goto_op }, { "__context__",NS_KEYWORD, .op = &__context___op }, { "__range__", NS_KEYWORD, .op = &range_op }, { "asm", NS_KEYWORD, .op = &asm_op }, { "__asm", NS_KEYWORD, .op = &asm_op }, { "__asm__", NS_KEYWORD, .op = &asm_op }, /* Attribute */ { "packed", NS_KEYWORD, .op = &packed_op }, { "__packed__", NS_KEYWORD, .op = &packed_op }, { "aligned", NS_KEYWORD, .op = &aligned_op }, { "__aligned__",NS_KEYWORD, .op = &aligned_op }, { "nocast", NS_KEYWORD, MOD_NOCAST, .op = &attr_mod_op }, { "noderef", NS_KEYWORD, MOD_NODEREF, .op = &attr_mod_op }, { "safe", NS_KEYWORD, MOD_SAFE, .op = &attr_mod_op }, { "force", NS_KEYWORD, .op = &attr_force_op }, { "bitwise", NS_KEYWORD, MOD_BITWISE, .op = &attr_bitwise_op }, { "__bitwise__",NS_KEYWORD, MOD_BITWISE, .op = &attr_bitwise_op }, { "address_space",NS_KEYWORD, .op = &address_space_op }, { "context", NS_KEYWORD, .op = &context_op }, { "designated_init", NS_KEYWORD, .op = &designated_init_op }, { "__designated_init__", NS_KEYWORD, .op = &designated_init_op }, { "transparent_union", NS_KEYWORD, .op = &transparent_union_op }, { "__transparent_union__", NS_KEYWORD, .op = &transparent_union_op }, { "noreturn", NS_KEYWORD, MOD_NORETURN, .op = &attr_mod_op }, { "__noreturn__", NS_KEYWORD, MOD_NORETURN, .op = &attr_mod_op }, { "pure", NS_KEYWORD, MOD_PURE, .op = &attr_mod_op }, {"__pure__", NS_KEYWORD, MOD_PURE, .op = &attr_mod_op }, {"const", NS_KEYWORD, MOD_PURE, .op = &attr_mod_op }, {"__const", NS_KEYWORD, MOD_PURE, .op = &attr_mod_op }, {"__const__", NS_KEYWORD, MOD_PURE, .op = &attr_mod_op }, {"externally_visible", NS_KEYWORD, .op = &ext_visible_op }, {"__externally_visible__", NS_KEYWORD, .op = &ext_visible_op }, { "mode", NS_KEYWORD, .op = &mode_op }, { "__mode__", NS_KEYWORD, .op = &mode_op }, { "QI", NS_KEYWORD, .op = &mode_QI_op }, { "__QI__", NS_KEYWORD, .op = &mode_QI_op }, { "HI", NS_KEYWORD, .op = &mode_HI_op }, { "__HI__", NS_KEYWORD, .op = &mode_HI_op }, { "SI", NS_KEYWORD, .op = &mode_SI_op }, { "__SI__", NS_KEYWORD, .op = &mode_SI_op }, { "DI", NS_KEYWORD, .op = &mode_DI_op }, { "__DI__", NS_KEYWORD, .op = &mode_DI_op }, { "TI", NS_KEYWORD, .op = &mode_TI_op }, { "__TI__", NS_KEYWORD, .op = &mode_TI_op }, { "byte", NS_KEYWORD, .op = &mode_QI_op }, { "__byte__", NS_KEYWORD, .op = &mode_QI_op }, { "pointer", NS_KEYWORD, .op = &mode_pointer_op }, { "__pointer__",NS_KEYWORD, .op = &mode_pointer_op }, { "word", NS_KEYWORD, .op = &mode_word_op }, { "__word__", NS_KEYWORD, .op = &mode_word_op }, }; static const char *ignored_attributes[] = { #define GCC_ATTR(x) \ STRINGIFY(x), \ STRINGIFY(__##x##__), #include "gcc-attr-list.h" #undef GCC_ATTR "bounded", "__bounded__", "__noclone", "__nonnull", "__nothrow", }; void init_parser(int stream) { int i; for (i = 0; i < ARRAY_SIZE(keyword_table); i++) { struct init_keyword *ptr = keyword_table + i; struct symbol *sym = create_symbol(stream, ptr->name, SYM_KEYWORD, ptr->ns); sym->ident->keyword = 1; if (ptr->ns == NS_TYPEDEF) sym->ident->reserved = 1; sym->ctype.modifiers = ptr->modifiers; sym->ctype.base_type = ptr->type; sym->op = ptr->op; } for (i = 0; i < ARRAY_SIZE(ignored_attributes); i++) { const char * name = ignored_attributes[i]; struct symbol *sym = create_symbol(stream, name, SYM_KEYWORD, NS_KEYWORD); if (!sym->op) { sym->ident->keyword = 1; sym->op = &ignore_attr_op; } } } // Add a symbol to the list of function-local symbols static void fn_local_symbol(struct symbol *sym) { if (function_symbol_list) add_symbol(function_symbol_list, sym); } static int SENTINEL_ATTR match_idents(struct token *token, ...) { va_list args; struct ident * next; if (token_type(token) != TOKEN_IDENT) return 0; va_start(args, token); do { next = va_arg(args, struct ident *); } while (next && token->ident != next); va_end(args); return next && token->ident == next; } struct statement *alloc_statement(struct position pos, int type) { struct statement *stmt = __alloc_statement(0); stmt->type = type; stmt->pos = pos; return stmt; } static struct token *struct_declaration_list(struct token *token, struct symbol_list **list); static void apply_modifiers(struct position pos, struct decl_state *ctx) { struct symbol *ctype; if (!ctx->mode) return; ctype = ctx->mode->to_mode(ctx->ctype.base_type); if (!ctype) sparse_error(pos, "don't know how to apply mode to %s", show_typename(ctx->ctype.base_type)); else ctx->ctype.base_type = ctype; } static struct symbol * alloc_indirect_symbol(struct position pos, struct ctype *ctype, int type) { struct symbol *sym = alloc_symbol(pos, type); sym->ctype.base_type = ctype->base_type; sym->ctype.modifiers = ctype->modifiers; ctype->base_type = sym; ctype->modifiers = 0; return sym; } /* * NOTE! NS_LABEL is not just a different namespace, * it also ends up using function scope instead of the * regular symbol scope. */ struct symbol *label_symbol(struct token *token) { struct symbol *sym = lookup_symbol(token->ident, NS_LABEL); if (!sym) { sym = alloc_symbol(token->pos, SYM_LABEL); bind_symbol(sym, token->ident, NS_LABEL); fn_local_symbol(sym); } return sym; } static struct token *struct_union_enum_specifier(enum type type, struct token *token, struct decl_state *ctx, struct token *(*parse)(struct token *, struct symbol *)) { struct symbol *sym; struct position *repos; token = handle_attributes(token, ctx, KW_ATTRIBUTE); if (token_type(token) == TOKEN_IDENT) { sym = lookup_symbol(token->ident, NS_STRUCT); if (!sym || (is_outer_scope(sym->scope) && (match_op(token->next,';') || match_op(token->next,'{')))) { // Either a new symbol, or else an out-of-scope // symbol being redefined. sym = alloc_symbol(token->pos, type); bind_symbol(sym, token->ident, NS_STRUCT); } if (sym->type != type) error_die(token->pos, "invalid tag applied to %s", show_typename (sym)); ctx->ctype.base_type = sym; repos = &token->pos; token = token->next; if (match_op(token, '{')) { // The following test is actually wrong for empty // structs, but (1) they are not C99, (2) gcc does // the same thing, and (3) it's easier. if (sym->symbol_list) error_die(token->pos, "redefinition of %s", show_typename (sym)); sym->pos = *repos; token = parse(token->next, sym); token = expect(token, '}', "at end of struct-union-enum-specifier"); // Mark the structure as needing re-examination sym->examined = 0; sym->endpos = token->pos; } return token; } // private struct/union/enum type if (!match_op(token, '{')) { sparse_error(token->pos, "expected declaration"); ctx->ctype.base_type = &bad_ctype; return token; } sym = alloc_symbol(token->pos, type); token = parse(token->next, sym); ctx->ctype.base_type = sym; token = expect(token, '}', "at end of specifier"); sym->endpos = token->pos; return token; } static struct token *parse_struct_declaration(struct token *token, struct symbol *sym) { struct symbol *field, *last = NULL; struct token *res; res = struct_declaration_list(token, &sym->symbol_list); FOR_EACH_PTR(sym->symbol_list, field) { if (!field->ident) { struct symbol *base = field->ctype.base_type; if (base && base->type == SYM_BITFIELD) continue; } if (last) last->next_subobject = field; last = field; } END_FOR_EACH_PTR(field); return res; } static struct token *parse_union_declaration(struct token *token, struct symbol *sym) { return struct_declaration_list(token, &sym->symbol_list); } static struct token *struct_specifier(struct token *token, struct decl_state *ctx) { return struct_union_enum_specifier(SYM_STRUCT, token, ctx, parse_struct_declaration); } static struct token *union_specifier(struct token *token, struct decl_state *ctx) { return struct_union_enum_specifier(SYM_UNION, token, ctx, parse_union_declaration); } /// // 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 symbol *sym; if (match_op(next, '=')) { next = constant_expression(next->next, &expr); lastval = get_expression_value(expr); ctype = &void_ctype; if (expr && expr->ctype) ctype = expr->ctype; } else if (!ctype) { ctype = &int_ctype; } else if (is_int_type(ctype)) { lastval++; } else { error_die(token->pos, "can't increment the last enum member"); } if (!expr) { expr = alloc_expression(token->pos, EXPR_VALUE); expr->value = lastval; expr->ctype = ctype; } sym = alloc_symbol(token->pos, SYM_NODE); bind_symbol(sym, token->ident, NS_SYMBOL); sym->ctype.modifiers &= ~MOD_ADDRESSABLE; sym->initializer = expr; sym->enum_member = 1; sym->ctype.base_type = parent; add_ptr_list(&parent->symbol_list, sym); if (base_type != &bad_ctype) { if (ctype->type == SYM_NODE) ctype = ctype->ctype.base_type; if (ctype->type == SYM_ENUM) { if (ctype == parent) ctype = base_type; else ctype = ctype->ctype.base_type; } /* * base_type rules: * - if all enums are of the same type, then * the base_type is that type (two first * cases) * - if enums are of different types, they * all have to be integer types, and the * base type is at least "int_ctype". * - otherwise the base_type is "bad_ctype". */ if (!base_type || 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 decl_state *ctx) { struct token *ret = struct_union_enum_specifier(SYM_ENUM, token, ctx, parse_enum_declaration); struct ctype *ctype = &ctx->ctype.base_type->ctype; if (!ctype->base_type) ctype->base_type = &incomplete_ctype; return ret; } static void apply_ctype(struct position pos, struct ctype *thistype, struct ctype *ctype); static struct token *typeof_specifier(struct token *token, struct decl_state *ctx) { struct symbol *sym; if (!match_op(token, '(')) { sparse_error(token->pos, "expected '(' after typeof"); return token; } if (lookup_type(token->next)) { token = typename(token->next, &sym, NULL); ctx->ctype.base_type = sym->ctype.base_type; apply_ctype(token->pos, &sym->ctype, &ctx->ctype); } else { struct symbol *typeof_sym = alloc_symbol(token->pos, SYM_TYPEOF); token = parse_expression(token->next, &typeof_sym->initializer); typeof_sym->endpos = token->pos; if (!typeof_sym->initializer) { sparse_error(token->pos, "expected expression after the '(' token"); typeof_sym = &bad_ctype; } ctx->ctype.base_type = typeof_sym; } return expect(token, ')', "after typeof"); } static struct token *ignore_attribute(struct token *token, struct symbol *attr, struct decl_state *ctx) { struct expression *expr = NULL; if (match_op(token, '(')) token = parens_expression(token, &expr, "in attribute"); return token; } static struct token *attribute_packed(struct token *token, struct symbol *attr, struct decl_state *ctx) { if (!ctx->ctype.alignment) ctx->ctype.alignment = 1; return token; } static struct token *attribute_aligned(struct token *token, struct symbol *attr, struct decl_state *ctx) { int alignment = max_alignment; struct expression *expr = NULL; if (match_op(token, '(')) { token = parens_expression(token, &expr, "in attribute"); if (expr) alignment = const_expression_value(expr); } if (alignment & (alignment-1)) { warning(token->pos, "I don't like non-power-of-2 alignments"); return token; } else if (alignment > ctx->ctype.alignment) ctx->ctype.alignment = alignment; return token; } static void apply_qualifier(struct position *pos, struct ctype *ctx, unsigned long qual) { if (ctx->modifiers & qual) warning(*pos, "duplicate %s", modifier_string(qual)); ctx->modifiers |= qual; } static struct token *attribute_modifier(struct token *token, struct symbol *attr, struct decl_state *ctx) { apply_qualifier(&token->pos, &ctx->ctype, attr->ctype.modifiers); return token; } static struct token *attribute_ext_visible(struct token *token, struct symbol *attr, struct decl_state *ctx) { ctx->is_ext_visible = 1; 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 space 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 ? &ulllong_ctype : &slllong_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 decl_state *ctx) { token = expect(token, '(', "after attribute"); token = expect(token, '(', "after attribute"); for (;;) { struct ident *attribute_name; struct symbol *attr; if (eof_token(token)) break; if (match_op(token, ';')) break; if (token_type(token) != TOKEN_IDENT) break; attribute_name = token->ident; attr = lookup_keyword(attribute_name, NS_KEYWORD); if (attr && attr->op->attribute) token = attr->op->attribute(token->next, attr, ctx); else token = recover_unknown_attribute(token); if (!match_op(token, ',')) break; token = token->next; } token = expect(token, ')', "after attribute"); token = expect(token, ')', "after attribute"); return token; } static const char *storage_class[] = { [STypedef] = "typedef", [SAuto] = "auto", [SExtern] = "extern", [SStatic] = "static", [SRegister] = "register", [SForced] = "[force]" }; static unsigned long storage_modifiers(struct decl_state *ctx) { static unsigned long mod[SMax] = { [SAuto] = MOD_AUTO, [SExtern] = MOD_EXTERN, [SStatic] = MOD_STATIC, [SRegister] = MOD_REGISTER }; return mod[ctx->storage_class] | (ctx->is_inline ? MOD_INLINE : 0) | (ctx->is_tls ? MOD_TLS : 0) | (ctx->is_ext_visible ? MOD_EXT_VISIBLE : 0); } static void set_storage_class(struct position *pos, struct decl_state *ctx, int class) { /* __thread can be used alone, or with extern or static */ if (ctx->is_tls && (class != SStatic && class != SExtern)) { sparse_error(*pos, "__thread can only be used alone, or with " "extern or static"); return; } if (!ctx->storage_class) { ctx->storage_class = class; return; } if (ctx->storage_class == class) sparse_error(*pos, "duplicate %s", storage_class[class]); else sparse_error(*pos, "multiple storage classes"); } static struct token *typedef_specifier(struct token *next, struct decl_state *ctx) { set_storage_class(&next->pos, ctx, STypedef); return next; } static struct token *auto_specifier(struct token *next, struct decl_state *ctx) { set_storage_class(&next->pos, ctx, SAuto); return next; } static struct token *register_specifier(struct token *next, struct decl_state *ctx) { set_storage_class(&next->pos, ctx, SRegister); return next; } static struct token *static_specifier(struct token *next, struct decl_state *ctx) { set_storage_class(&next->pos, ctx, SStatic); return next; } static struct token *extern_specifier(struct token *next, struct decl_state *ctx) { set_storage_class(&next->pos, ctx, SExtern); return next; } static struct token *thread_specifier(struct token *next, struct decl_state *ctx) { /* This GCC extension can be used alone, or with extern or static */ if (!ctx->storage_class || ctx->storage_class == SStatic || ctx->storage_class == SExtern) { ctx->is_tls = 1; } else { sparse_error(next->pos, "__thread can only be used alone, or " "with extern or static"); } return next; } static struct token *attribute_force(struct token *token, struct symbol *attr, struct decl_state *ctx) { set_storage_class(&token->pos, ctx, SForced); return token; } static struct token *inline_specifier(struct token *next, struct decl_state *ctx) { ctx->is_inline = 1; return next; } static struct token *noreturn_specifier(struct token *next, struct decl_state *ctx) { apply_qualifier(&next->pos, &ctx->ctype, MOD_NORETURN); return next; } static struct token *alignas_specifier(struct token *token, struct decl_state *ctx) { int alignment = 0; if (!match_op(token, '(')) { sparse_error(token->pos, "expected '(' after _Alignas"); return token; } if (lookup_type(token->next)) { struct symbol *sym = NULL; token = typename(token->next, &sym, NULL); sym = examine_symbol_type(sym); alignment = sym->ctype.alignment; token = expect(token, ')', "after _Alignas(..."); } else { struct expression *expr = NULL; token = parens_expression(token, &expr, "after _Alignas"); if (!expr) return token; alignment = const_expression_value(expr); } if (alignment < 0) { warning(token->pos, "non-positive alignment"); return token; } if (alignment & (alignment-1)) { warning(token->pos, "non-power-of-2 alignment"); return token; } if (alignment > ctx->ctype.alignment) ctx->ctype.alignment = alignment; return token; } static struct token *const_qualifier(struct token *next, struct decl_state *ctx) { apply_qualifier(&next->pos, &ctx->ctype, MOD_CONST); return next; } static struct token *volatile_qualifier(struct token *next, struct decl_state *ctx) { apply_qualifier(&next->pos, &ctx->ctype, MOD_VOLATILE); return next; } static struct token *restrict_qualifier(struct token *next, struct decl_state *ctx) { apply_qualifier(&next->pos, &ctx->ctype, MOD_RESTRICT); return next; } static struct token *atomic_qualifier(struct token *next, struct decl_state *ctx) { apply_qualifier(&next->pos, &ctx->ctype, MOD_ATOMIC); return next; } static void apply_ctype(struct position pos, struct ctype *thistype, struct ctype *ctype) { unsigned long mod = thistype->modifiers; if (mod) apply_qualifier(&pos, ctype, mod); /* Context */ concat_ptr_list((struct ptr_list *)thistype->contexts, (struct ptr_list **)&ctype->contexts); /* Alignment */ if (thistype->alignment > ctype->alignment) ctype->alignment = thistype->alignment; /* Address space */ if (thistype->as) ctype->as = thistype->as; } static void specifier_conflict(struct position pos, int what, struct ident *new) { const char *old; if (what & (Set_S | Set_T)) goto Catch_all; if (what & Set_Char) old = "char"; else if (what & Set_Double) old = "double"; else if (what & Set_Float) old = "float"; else if (what & Set_Signed) old = "signed"; else if (what & Set_Unsigned) old = "unsigned"; else if (what & Set_Short) old = "short"; else if (what & Set_Long) old = "long"; else old = "long long"; sparse_error(pos, "impossible combination of type specifiers: %s %s", old, show_ident(new)); return; Catch_all: sparse_error(pos, "two or more data types in declaration specifiers"); } static struct symbol * const int_types[] = {&short_ctype, &int_ctype, &long_ctype, &llong_ctype, &lllong_ctype}; static struct symbol * const signed_types[] = {&sshort_ctype, &sint_ctype, &slong_ctype, &sllong_ctype, &slllong_ctype}; static struct symbol * const unsigned_types[] = {&ushort_ctype, &uint_ctype, &ulong_ctype, &ullong_ctype, &ulllong_ctype}; static struct symbol * const real_types[] = {&float_ctype, &double_ctype, &ldouble_ctype}; static struct symbol * const char_types[] = {&char_ctype, &schar_ctype, &uchar_ctype}; static struct symbol * const * const types[] = { int_types + 1, signed_types + 1, unsigned_types + 1, real_types + 1, char_types, char_types + 1, char_types + 2 }; struct symbol *ctype_integer(int size, int want_unsigned) { return types[want_unsigned ? CUInt : CInt][size]; } static struct token *handle_qualifiers(struct token *t, struct decl_state *ctx) { while (token_type(t) == TOKEN_IDENT) { struct symbol *s = lookup_symbol(t->ident, NS_TYPEDEF); if (!s) break; if (s->type != SYM_KEYWORD) break; if (!(s->op->type & (KW_ATTRIBUTE | KW_QUALIFIER))) break; t = t->next; if (s->op->declarator) t = s->op->declarator(t, ctx); } return t; } static struct token *declaration_specifiers(struct token *token, struct decl_state *ctx) { int seen = 0; int class = CInt; int size = 0; while (token_type(token) == TOKEN_IDENT) { struct symbol *s = lookup_symbol(token->ident, NS_TYPEDEF | NS_SYMBOL); if (!s || !(s->namespace & NS_TYPEDEF)) break; if (s->type != SYM_KEYWORD) { if (seen & Set_Any) break; seen |= Set_S | Set_T; ctx->ctype.base_type = s->ctype.base_type; apply_ctype(token->pos, &s->ctype, &ctx->ctype); token = token->next; continue; } if (s->op->type & KW_SPECIFIER) { if (seen & s->op->test) { specifier_conflict(token->pos, seen & s->op->test, token->ident); break; } seen |= s->op->set; class += s->op->class; if (s->op->set & Set_Int128) size = 2; if (s->op->type & KW_SHORT) { size = -1; } else if (s->op->type & KW_LONG && size++) { if (class == CReal) { specifier_conflict(token->pos, Set_Vlong, &double_ident); break; } seen |= Set_Vlong; } } token = token->next; if (s->op->declarator) token = s->op->declarator(token, ctx); if (s->op->type & KW_EXACT) { ctx->ctype.base_type = s->ctype.base_type; ctx->ctype.modifiers |= s->ctype.modifiers; } } if (!(seen & Set_S)) { /* not set explicitly? */ struct symbol *base = &incomplete_ctype; if (seen & Set_Any) base = types[class][size]; ctx->ctype.base_type = base; } if (ctx->ctype.modifiers & MOD_BITWISE) { struct symbol *type; ctx->ctype.modifiers &= ~MOD_BITWISE; if (!is_int_type(ctx->ctype.base_type)) { sparse_error(token->pos, "invalid modifier"); return token; } type = alloc_symbol(token->pos, SYM_BASETYPE); *type = *ctx->ctype.base_type; type->ctype.modifiers &= ~MOD_SPECIFIER; type->ctype.base_type = ctx->ctype.base_type; type->type = SYM_RESTRICT; ctx->ctype.base_type = type; create_fouled(type); } return token; } static struct token *abstract_array_static_declarator(struct token *token, int *has_static) { while (token->ident == &static_ident) { if (*has_static) sparse_error(token->pos, "duplicate array static declarator"); *has_static = 1; token = token->next; } return token; } static struct token *abstract_array_declarator(struct token *token, struct symbol *sym) { struct expression *expr = NULL; int has_static = 0; token = abstract_array_static_declarator(token, &has_static); if (match_idents(token, &restrict_ident, &__restrict_ident, &__restrict___ident, NULL)) token = abstract_array_static_declarator(token->next, &has_static); token = parse_expression(token, &expr); sym->array_size = expr; return token; } static struct token *parameter_type_list(struct token *, struct symbol *); static struct token *identifier_list(struct token *, struct symbol *); static struct token *declarator(struct token *token, struct decl_state *ctx); static struct token *skip_attribute(struct token *token) { token = token->next; if (match_op(token, '(')) { int depth = 1; token = token->next; while (depth && !eof_token(token)) { if (token_type(token) == TOKEN_SPECIAL) { if (token->special == '(') depth++; else if (token->special == ')') depth--; } token = token->next; } } return token; } static struct token *skip_attributes(struct token *token) { struct symbol *keyword; for (;;) { if (token_type(token) != TOKEN_IDENT) break; keyword = lookup_keyword(token->ident, NS_KEYWORD | NS_TYPEDEF); if (!keyword || keyword->type != SYM_KEYWORD) break; if (!(keyword->op->type & KW_ATTRIBUTE)) break; token = expect(token->next, '(', "after attribute"); token = expect(token, '(', "after attribute"); for (;;) { if (eof_token(token)) break; if (match_op(token, ';')) break; if (token_type(token) != TOKEN_IDENT) break; token = skip_attribute(token); if (!match_op(token, ',')) break; token = token->next; } token = expect(token, ')', "after attribute"); token = expect(token, ')', "after attribute"); } return token; } static struct token *handle_attributes(struct token *token, struct decl_state *ctx, unsigned int keywords) { struct symbol *keyword; for (;;) { if (token_type(token) != TOKEN_IDENT) break; keyword = lookup_keyword(token->ident, NS_KEYWORD | NS_TYPEDEF); if (!keyword || keyword->type != SYM_KEYWORD) break; if (!(keyword->op->type & keywords)) break; token = keyword->op->declarator(token->next, ctx); keywords &= KW_ATTRIBUTE; } return token; } static int is_nested(struct token *token, struct token **p, int prefer_abstract) { /* * This can be either a parameter list or a grouping. * For the direct (non-abstract) case, we know if must be * a parameter list if we already saw the identifier. * For the abstract case, we know if must be a parameter * list if it is empty or starts with a type. */ struct token *next = token->next; *p = next = skip_attributes(next); if (token_type(next) == TOKEN_IDENT) { if (lookup_type(next)) return !prefer_abstract; return 1; } if (match_op(next, ')') || match_op(next, SPECIAL_ELLIPSIS)) return 0; return 1; } enum kind { Empty, K_R, Proto, Bad_Func, }; static enum kind which_func(struct token *token, struct ident **n, int prefer_abstract) { struct token *next = token->next; if (token_type(next) == TOKEN_IDENT) { if (lookup_type(next)) return Proto; /* identifier list not in definition; complain */ if (prefer_abstract) warning(token->pos, "identifier list not in definition"); return K_R; } if (token_type(next) != TOKEN_SPECIAL) return Bad_Func; if (next->special == ')') { /* don't complain about those */ if (!n || match_op(next->next, ';') || 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, KW_ATTRIBUTE); token = declarator(next, ctx); token = expect(token, ')', "in nested declarator"); while (ctype->base_type != base_type) ctype = &ctype->base_type->ctype; p = NULL; } if (match_op(token, '(')) { enum kind kind = which_func(token, p, ctx->prefer_abstract); struct symbol *fn; fn = alloc_indirect_symbol(token->pos, ctype, SYM_FN); token = token->next; if (kind == K_R) token = identifier_list(token, fn); else if (kind == Proto) token = parameter_type_list(token, fn); token = expect(token, ')', "in function declarator"); fn->endpos = token->pos; return token; } while (match_op(token, '[')) { struct symbol *array; array = alloc_indirect_symbol(token->pos, ctype, SYM_ARRAY); token = abstract_array_declarator(token->next, array); token = expect(token, ']', "in abstract_array_declarator"); array->endpos = token->pos; ctype = &array->ctype; } return token; } static struct token *pointer(struct token *token, struct decl_state *ctx) { while (match_op(token,'*')) { struct symbol *ptr = alloc_symbol(token->pos, SYM_PTR); ptr->ctype.modifiers = ctx->ctype.modifiers; ptr->ctype.base_type = ctx->ctype.base_type; ptr->ctype.as = ctx->ctype.as; ptr->ctype.contexts = ctx->ctype.contexts; ctx->ctype.modifiers = 0; ctx->ctype.base_type = ptr; ctx->ctype.as = 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) { sparse_error(token->pos, "invalid bitfield width, %lld.", width); width = -1; } else if (*ctx->ident && width == 0) { sparse_error(token->pos, "invalid named zero-width bitfield `%s'", show_ident(*ctx->ident)); width = -1; } else if (*ctx->ident) { struct symbol *base_type = bitfield->ctype.base_type; struct symbol *bitfield_type = base_type == &int_type ? bitfield : base_type; int is_signed = !(bitfield_type->ctype.modifiers & MOD_UNSIGNED); if (Wone_bit_signed_bitfield && width == 1 && is_signed) { // Valid values are either {-1;0} or {0}, depending on integer // representation. The latter makes for very efficient code... sparse_error(token->pos, "dubious one-bit signed bitfield"); } if (Wdefault_bitfield_sign && bitfield_type->type != SYM_ENUM && !(bitfield_type->ctype.modifiers & MOD_EXPLICITLY_SIGNED) && is_signed) { // The sign of bitfields is unspecified by default. warning(token->pos, "dubious bitfield without explicit `signed' or `unsigned'"); } } bitfield->bit_size = width; bitfield->endpos = token->pos; return token; } static struct token *declaration_list(struct token *token, struct symbol_list **list) { struct decl_state ctx = {.prefer_abstract = 0}; struct ctype saved; unsigned long mod; token = declaration_specifiers(token, &ctx); mod = storage_modifiers(&ctx); saved = ctx.ctype; for (;;) { struct symbol *decl = alloc_symbol(token->pos, SYM_NODE); ctx.ident = &decl->ident; token = declarator(token, &ctx); if (match_op(token, ':')) token = handle_bitfield(token, &ctx); token = handle_attributes(token, &ctx, KW_ATTRIBUTE); apply_modifiers(token->pos, &ctx); decl->ctype = ctx.ctype; decl->ctype.modifiers |= mod; decl->endpos = token->pos; add_symbol(list, decl); if (!match_op(token, ',')) break; token = token->next; ctx.ctype = saved; } return token; } static struct token *struct_declaration_list(struct token *token, struct symbol_list **list) { while (!match_op(token, '}')) { if (match_ident(token, &_Static_assert_ident)) { token = parse_static_assert(token, NULL); continue; } if (!match_op(token, ';')) token = declaration_list(token, list); if (!match_op(token, ';')) { sparse_error(token->pos, "expected ; at end of declaration"); break; } token = token->next; } return token; } static struct token *parameter_declaration(struct token *token, struct symbol *sym) { struct decl_state ctx = {.prefer_abstract = 1}; token = declaration_specifiers(token, &ctx); ctx.ident = &sym->ident; token = declarator(token, &ctx); token = handle_attributes(token, &ctx, KW_ATTRIBUTE); apply_modifiers(token->pos, &ctx); sym->ctype = ctx.ctype; sym->ctype.modifiers |= storage_modifiers(&ctx); sym->endpos = token->pos; sym->forced_arg = ctx.storage_class == SForced; return token; } struct token *typename(struct token *token, struct symbol **p, int *forced) { struct decl_state ctx = {.prefer_abstract = 1}; int class; struct symbol *sym = alloc_symbol(token->pos, SYM_NODE); *p = sym; token = declaration_specifiers(token, &ctx); token = declarator(token, &ctx); apply_modifiers(token->pos, &ctx); sym->ctype = ctx.ctype; sym->endpos = token->pos; class = ctx.storage_class; if (forced) { *forced = 0; if (class == SForced) { *forced = 1; class = 0; } } if (class) warning(sym->pos, "storage class in typename (%s %s)", storage_class[class], show_typename(sym)); return token; } static struct token *expression_statement(struct token *token, struct expression **tree) { token = parse_expression(token, tree); return expect(token, ';', "at end of statement"); } static struct token *parse_asm_operands(struct token *token, struct statement *stmt, struct 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); 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 && s->op->asm_modifier) s->op->asm_modifier(token, &mods); 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_asm_declarator(struct token *token, struct decl_state *ctx) { struct expression *expr; token = expect(token, '(', "after asm"); token = string_expression(token, &expr, "inline asm"); token = expect(token, ')', "after asm"); return token; } static struct token *parse_static_assert(struct token *token, struct symbol_list **unused) { struct expression *cond = NULL, *message = NULL; token = expect(token->next, '(', "after _Static_assert"); token = constant_expression(token, &cond); if (!cond) sparse_error(token->pos, "Expected constant expression"); token = expect(token, ',', "after conditional expression in _Static_assert"); token = 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) sparse_error(cond->pos, "static assertion failed: %s", show_string(message->string)); return token; } /* Make a statement out of an expression */ static struct statement *make_statement(struct expression *expr) { struct statement *stmt; if (!expr) return NULL; stmt = alloc_statement(expr->pos, STMT_EXPRESSION); stmt->expression = expr; return stmt; } /* * All iterators have two symbols associated with them: * the "continue" and "break" symbols, which are targets * for continue and break statements respectively. * * They are in a special name-space, but they follow * all the normal visibility rules, so nested iterators * automatically work right. */ static void start_iterator(struct statement *stmt) { struct symbol *cont, *brk; start_symbol_scope(); cont = alloc_symbol(stmt->pos, SYM_NODE); bind_symbol(cont, &continue_ident, NS_ITERATOR); brk = alloc_symbol(stmt->pos, SYM_NODE); bind_symbol(brk, &break_ident, NS_ITERATOR); stmt->type = STMT_ITERATOR; stmt->iterator_break = brk; stmt->iterator_continue = cont; fn_local_symbol(brk); fn_local_symbol(cont); } static void end_iterator(struct statement *stmt) { end_symbol_scope(); } static struct statement *start_function(struct symbol *sym) { struct symbol *ret; struct statement *stmt = alloc_statement(sym->pos, STMT_COMPOUND); start_function_scope(); ret = alloc_symbol(sym->pos, SYM_NODE); ret->ctype = sym->ctype.base_type->ctype; ret->ctype.modifiers &= ~(MOD_STORAGE | MOD_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_symbol_scope(); brk = alloc_symbol(stmt->pos, SYM_NODE); bind_symbol(brk, &break_ident, NS_ITERATOR); switch_case = alloc_symbol(stmt->pos, SYM_NODE); bind_symbol(switch_case, &case_ident, NS_ITERATOR); switch_case->stmt = stmt; stmt->type = STMT_SWITCH; stmt->switch_break = brk; stmt->switch_case = switch_case; fn_local_symbol(brk); fn_local_symbol(switch_case); } static void end_switch(struct statement *stmt) { if (!stmt->switch_case->symbol_list) warning(stmt->pos, "switch with no cases"); end_symbol_scope(); } static void add_case_statement(struct statement *stmt) { struct symbol *target = lookup_symbol(&case_ident, NS_ITERATOR); struct symbol *sym; if (!target) { sparse_error(stmt->pos, "not in switch scope"); stmt->type = STMT_NONE; return; } sym = alloc_symbol(stmt->pos, SYM_NODE); add_symbol(&target->symbol_list, sym); sym->stmt = stmt; stmt->case_label = sym; fn_local_symbol(sym); } static struct token *parse_return_statement(struct token *token, struct statement *stmt) { struct symbol *target = lookup_symbol(&return_ident, NS_ITERATOR); if (!target) error_die(token->pos, "internal error: return without a function target"); stmt->type = STMT_RETURN; stmt->ret_target = target; return expression_statement(token->next, &stmt->ret_value); } static void validate_for_loop_decl(struct symbol *sym) { unsigned long storage = sym->ctype.modifiers & MOD_STORAGE; if (storage & ~(MOD_AUTO | MOD_REGISTER)) { const char *name = show_ident(sym->ident); sparse_error(sym->pos, "non-local var '%s' in for-loop initializer", name); sym->ctype.modifiers &= ~MOD_STORAGE; } } static struct token *parse_for_statement(struct token *token, struct statement *stmt) { struct symbol_list *syms; struct expression *e1, *e2, *e3; struct statement *iterator; start_iterator(stmt); token = expect(token->next, '(', "after 'for'"); syms = NULL; e1 = NULL; /* C99 variable declaration? */ if (lookup_type(token)) { token = external_declaration(token, &syms, validate_for_loop_decl); } else { token = parse_expression(token, &e1); token = expect(token, ';', "in 'for'"); } token = parse_expression(token, &e2); token = expect(token, ';', "in 'for'"); token = parse_expression(token, &e3); token = expect(token, ')', "in 'for'"); token = statement(token, &iterator); stmt->iterator_syms = syms; stmt->iterator_pre_statement = make_statement(e1); stmt->iterator_pre_condition = e2; stmt->iterator_post_statement = make_statement(e3); stmt->iterator_post_condition = NULL; stmt->iterator_statement = iterator; end_iterator(stmt); return token; } static struct token *parse_while_statement(struct token *token, struct statement *stmt) { struct expression *expr; struct statement *iterator; start_iterator(stmt); token = parens_expression(token->next, &expr, "after 'while'"); token = statement(token, &iterator); stmt->iterator_pre_condition = expr; stmt->iterator_post_condition = NULL; stmt->iterator_statement = iterator; end_iterator(stmt); return token; } static struct token *parse_do_statement(struct token *token, struct statement *stmt) { struct expression *expr; struct statement *iterator; start_iterator(stmt); token = statement(token->next, &iterator); if (token_type(token) == TOKEN_IDENT && token->ident == &while_ident) token = token->next; else sparse_error(token->pos, "expected 'while' after 'do'"); token = parens_expression(token, &expr, "after 'do-while'"); stmt->iterator_post_condition = expr; stmt->iterator_statement = iterator; end_iterator(stmt); if (iterator && iterator->type != STMT_COMPOUND && Wdo_while) warning(iterator->pos, "do-while statement is not a compound statement"); return expect(token, ';', "after statement"); } static struct token *parse_if_statement(struct token *token, struct statement *stmt) { stmt->type = STMT_IF; token = parens_expression(token->next, &stmt->if_conditional, "after if"); token = statement(token, &stmt->if_true); if (token_type(token) != TOKEN_IDENT) return token; if (token->ident != &else_ident) return token; return statement(token->next, &stmt->if_false); } static inline struct token *case_statement(struct token *token, struct statement *stmt) { stmt->type = STMT_CASE; token = expect(token, ':', "after default/case"); add_case_statement(stmt); return statement(token, &stmt->case_statement); } static struct token *parse_case_statement(struct token *token, struct statement *stmt) { token = parse_expression(token->next, &stmt->case_expression); if (match_op(token, SPECIAL_ELLIPSIS)) token = parse_expression(token->next, &stmt->case_to); return case_statement(token, stmt); } static struct token *parse_default_statement(struct token *token, struct statement *stmt) { return case_statement(token->next, stmt); } static struct token *parse_loop_iterator(struct token *token, struct statement *stmt) { struct symbol *target = lookup_symbol(token->ident, NS_ITERATOR); stmt->type = STMT_GOTO; stmt->goto_label = target; if (!target) sparse_error(stmt->pos, "break/continue not in iterator scope"); return expect(token->next, ';', "at end of statement"); } static struct token *parse_switch_statement(struct token *token, struct statement *stmt) { stmt->type = STMT_SWITCH; start_switch(stmt); token = parens_expression(token->next, &stmt->switch_expression, "after 'switch'"); token = statement(token, &stmt->switch_statement); end_switch(stmt); return token; } static struct token *parse_goto_statement(struct token *token, struct statement *stmt) { stmt->type = STMT_GOTO; token = token->next; if (match_op(token, '*')) { token = parse_expression(token->next, &stmt->goto_expression); add_statement(&function_computed_goto_list, stmt); } else if (token_type(token) == TOKEN_IDENT) { stmt->goto_label = label_symbol(token); token = token->next; } else { sparse_error(token->pos, "Expected identifier or goto expression"); } return expect(token, ';', "at end of statement"); } static struct token *parse_context_statement(struct token *token, struct statement *stmt) { stmt->type = STMT_CONTEXT; token = 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 *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); token = skip_attributes(token->next->next); 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; s->stmt = stmt; return statement(token, &stmt->label_statement); } } if (match_op(token, '{')) { stmt->type = STMT_COMPOUND; start_symbol_scope(); token = compound_statement(token->next, stmt); end_symbol_scope(); return expect(token, '}', "at end of compound statement"); } stmt->type = STMT_EXPRESSION; return expression_statement(token, &stmt->expression); } /* gcc extension - __label__ ident-list; in the beginning of compound stmt */ static struct token *label_statement(struct token *token) { while (token_type(token) == TOKEN_IDENT) { struct symbol *sym = alloc_symbol(token->pos, SYM_LABEL); /* it's block-scope, but we want label namespace */ bind_symbol(sym, token->ident, NS_SYMBOL); sym->namespace = NS_LABEL; fn_local_symbol(sym); token = token->next; if (!match_op(token, ',')) break; token = token->next; } return expect(token, ';', "at end of label declaration"); } static struct token * statement_list(struct token *token, struct statement_list **list) { int seen_statement = 0; while (token_type(token) == TOKEN_IDENT && token->ident == &__label___ident) token = label_statement(token->next); for (;;) { struct statement * stmt; if (eof_token(token)) break; if (match_op(token, '}')) break; if (match_ident(token, &_Static_assert_ident)) { token = parse_static_assert(token, NULL); continue; } if (lookup_type(token)) { if (seen_statement) { warning(token->pos, "mixing declarations and code"); seen_statement = 0; } stmt = alloc_statement(token->pos, STMT_DECLARATION); token = external_declaration(token, &stmt->declaration, NULL); } else { seen_statement = Wdeclarationafterstatement; token = statement(token, &stmt); } add_statement(list, stmt); } return token; } static struct token *identifier_list(struct token *token, struct symbol *fn) { struct symbol_list **list = &fn->arguments; for (;;) { struct symbol *sym = alloc_symbol(token->pos, SYM_NODE); sym->ident = token->ident; token = token->next; sym->endpos = token->pos; sym->ctype.base_type = &incomplete_ctype; add_symbol(list, sym); if (!match_op(token, ',') || token_type(token->next) != TOKEN_IDENT || lookup_type(token->next)) break; token = token->next; } return token; } static struct token *parameter_type_list(struct token *token, struct symbol *fn) { struct symbol_list **list = &fn->arguments; for (;;) { struct symbol *sym; if (match_op(token, SPECIAL_ELLIPSIS)) { fn->variadic = 1; token = token->next; break; } sym = alloc_symbol(token->pos, SYM_NODE); token = parameter_declaration(token, sym); if (sym->ctype.base_type == &void_ctype) { /* Special case: (void) */ if (!*list && !sym->ident) break; warning(token->pos, "void parameter"); } add_symbol(list, sym); if (!match_op(token, ',')) break; token = token->next; } return token; } struct token *compound_statement(struct token *token, struct statement *stmt) { token = statement_list(token, &stmt->stmts); return token; } static struct expression *identifier_expression(struct token *token) { struct expression *expr = alloc_expression(token->pos, EXPR_IDENTIFIER); expr->expr_ident = token->ident; return expr; } static struct expression *index_expression(struct expression *from, struct expression *to) { int idx_from, idx_to; struct expression *expr = alloc_expression(from->pos, EXPR_INDEX); idx_from = const_expression_value(from); idx_to = idx_from; if (to) { idx_to = const_expression_value(to); if (idx_to < idx_from || idx_from < 0) warning(from->pos, "nonsense array initializer index range"); } expr->idx_from = idx_from; expr->idx_to = idx_to; return expr; } static struct token *single_initializer(struct expression **ep, struct token *token) { int expect_equal = 0; struct token *next = token->next; struct expression **tail = ep; int nested; *ep = NULL; if ((token_type(token) == TOKEN_IDENT) && match_op(next, ':')) { struct expression *expr = identifier_expression(token); if (Wold_initializer) warning(token->pos, "obsolete struct initializer, use C99 syntax"); token = initializer(&expr->ident_expression, next->next); if (expr->ident_expression) *ep = expr; return token; } for (tail = ep, nested = 0; ; nested++, next = token->next) { if (match_op(token, '.') && (token_type(next) == TOKEN_IDENT)) { struct expression *expr = identifier_expression(next); *tail = expr; tail = &expr->ident_expression; expect_equal = 1; token = next->next; } else if (match_op(token, '[')) { struct expression *from = NULL, *to = NULL, *expr; token = constant_expression(token->next, &from); if (!from) { sparse_error(token->pos, "Expected constant expression"); break; } if (match_op(token, SPECIAL_ELLIPSIS)) token = constant_expression(token->next, &to); expr = index_expression(from, to); *tail = expr; tail = &expr->idx_expression; token = expect(token, ']', "at end of initializer index"); if (nested) expect_equal = 1; } else { break; } } if (nested && !expect_equal) { if (!match_op(token, '=')) warning(token->pos, "obsolete array initializer, use C99 syntax"); else expect_equal = 1; } if (expect_equal) token = expect(token, '=', "at end of initializer index"); token = initializer(tail, token); if (!*tail) *ep = NULL; return token; } static struct token *initializer_list(struct expression_list **list, struct token *token) { struct expression *expr; for (;;) { token = single_initializer(&expr, token); if (!expr) break; add_expression(list, expr); if (!match_op(token, ',')) break; token = token->next; } return token; } struct token *initializer(struct expression **tree, struct token *token) { if (match_op(token, '{')) { struct expression *expr = alloc_expression(token->pos, EXPR_INITIALIZER); *tree = expr; token = initializer_list(&expr->expr_list, token->next); return expect(token, '}', "at end of initializer"); } return assignment_expression(token, tree); } static void declare_argument(struct symbol *sym, struct symbol *fn) { if (!sym->ident) { sparse_error(sym->pos, "no identifier for function argument"); return; } bind_symbol(sym, sym->ident, NS_SYMBOL); } static struct token *parse_function_body(struct token *token, struct symbol *decl, struct symbol_list **list) { struct symbol_list **old_symbol_list; struct symbol *base_type = decl->ctype.base_type; struct statement *stmt, **p; struct symbol *prev; struct symbol *arg; old_symbol_list = function_symbol_list; if (decl->ctype.modifiers & MOD_INLINE) { function_symbol_list = &decl->inline_symbol_list; p = &base_type->inline_stmt; } else { function_symbol_list = &decl->symbol_list; p = &base_type->stmt; } function_computed_target_list = NULL; function_computed_goto_list = NULL; if (decl->ctype.modifiers & MOD_EXTERN) { if (!(decl->ctype.modifiers & MOD_INLINE)) warning(decl->pos, "function '%s' with external linkage has definition", show_ident(decl->ident)); } if (!(decl->ctype.modifiers & MOD_STATIC)) decl->ctype.modifiers |= MOD_EXTERN; stmt = start_function(decl); *p = stmt; FOR_EACH_PTR (base_type->arguments, arg) { declare_argument(arg, base_type); } END_FOR_EACH_PTR(arg); token = compound_statement(token->next, stmt); end_function(decl); if (!(decl->ctype.modifiers & MOD_INLINE)) add_symbol(list, decl); check_declaration(decl); decl->definition = decl; prev = decl->same_symbol; if (prev && prev->definition) { warning(decl->pos, "multiple definitions for function '%s'", show_ident(decl->ident)); info(prev->definition->pos, " the previous one is here"); } else { while (prev) { rebind_scope(prev, decl->scope); prev->definition = decl; prev = prev->same_symbol; } } function_symbol_list = old_symbol_list; if (function_computed_goto_list) { if (!function_computed_target_list) warning(decl->pos, "function '%s' has computed goto but no targets?", show_ident(decl->ident)); else { FOR_EACH_PTR(function_computed_goto_list, stmt) { stmt->target_list = function_computed_target_list; } END_FOR_EACH_PTR(stmt); } } return expect(token, '}', "at end of function"); } static void promote_k_r_types(struct symbol *arg) { struct symbol *base = arg->ctype.base_type; if (base && base->ctype.base_type == &int_type && (base->ctype.modifiers & (MOD_CHAR | MOD_SHORT))) { arg->ctype.base_type = &int_ctype; } } static void apply_k_r_types(struct symbol_list *argtypes, struct symbol *fn) { struct symbol_list *real_args = fn->ctype.base_type->arguments; struct symbol *arg; FOR_EACH_PTR(real_args, arg) { struct symbol *type; /* This is quadratic in the number of arguments. We _really_ don't care */ FOR_EACH_PTR(argtypes, type) { if (type->ident == arg->ident) goto match; } END_FOR_EACH_PTR(type); if (Wimplicit_int) { sparse_error(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 = storage_modifiers(&ctx); decl = alloc_symbol(token->pos, SYM_NODE); /* Just a type declaration? */ if (match_op(token, ';')) { apply_modifiers(token->pos, &ctx); return token->next; } saved = ctx.ctype; token = declarator(token, &ctx); token = handle_attributes(token, &ctx, KW_ATTRIBUTE | KW_ASM); apply_modifiers(token->pos, &ctx); decl->ctype = ctx.ctype; decl->ctype.modifiers |= mod; decl->endpos = token->pos; /* Just a type declaration? */ if (!ident) { warning(token->pos, "missing identifier in declaration"); return expect(token, ';', "at the end of type declaration"); } /* type define declaration? */ is_typedef = ctx.storage_class == STypedef; /* Typedefs don't have meaningful storage */ if (is_typedef) decl->ctype.modifiers |= MOD_USERTYPE; bind_symbol(decl, ident, is_typedef ? NS_TYPEDEF: NS_SYMBOL); base_type = decl->ctype.base_type; if (is_typedef) { if (base_type && !base_type->ident) { switch (base_type->type) { case SYM_STRUCT: case SYM_UNION: case SYM_ENUM: case SYM_RESTRICT: base_type->ident = ident; break; default: break; } } } else if (base_type && base_type->type == SYM_FN) { if (base_type->ctype.base_type == &incomplete_ctype) { warning(decl->pos, "'%s()' has implicit return type", show_ident(decl->ident)); base_type->ctype.base_type = &int_ctype; } /* K&R argument declaration? */ if (lookup_type(token)) return parse_k_r_arguments(token, decl, list); if (match_op(token, '{')) return parse_function_body(token, decl, list); if (!(decl->ctype.modifiers & MOD_STATIC)) decl->ctype.modifiers |= MOD_EXTERN; } else if (base_type == &void_ctype && !(decl->ctype.modifiers & MOD_EXTERN)) { sparse_error(token->pos, "void declaration"); } if (base_type == &incomplete_ctype) { warning(decl->pos, "'%s' has implicit type", show_ident(decl->ident)); decl->ctype.base_type = &int_ctype;; } for (;;) { if (!is_typedef && match_op(token, '=')) { token = initializer(&decl->initializer, token->next); } if (!is_typedef) { if (validate_decl) validate_decl(decl); if (decl->initializer && decl->ctype.modifiers & MOD_EXTERN) { warning(decl->pos, "symbol with external linkage has initializer"); decl->ctype.modifiers &= ~MOD_EXTERN; } if (!(decl->ctype.modifiers & (MOD_EXTERN | MOD_INLINE))) { add_symbol(list, decl); fn_local_symbol(decl); } } check_declaration(decl); if (decl->same_symbol) { decl->definition = decl->same_symbol->definition; decl->op = decl->same_symbol->op; if (is_typedef) { // TODO: handle -std=c89 --pedantic check_duplicates(decl); } } if (!match_op(token, ',')) break; token = token->next; ident = NULL; decl = alloc_symbol(token->pos, SYM_NODE); ctx.ctype = saved; token = handle_attributes(token, &ctx, KW_ATTRIBUTE); token = declarator(token, &ctx); token = handle_attributes(token, &ctx, KW_ATTRIBUTE | KW_ASM); apply_modifiers(token->pos, &ctx); decl->ctype = ctx.ctype; decl->ctype.modifiers |= mod; decl->endpos = token->pos; if (!ident) { sparse_error(token->pos, "expected identifier name in type definition"); return token; } if (is_typedef) decl->ctype.modifiers |= MOD_USERTYPE; bind_symbol(decl, ident, is_typedef ? NS_TYPEDEF: NS_SYMBOL); /* Function declarations are automatically extern unless specifically static */ base_type = decl->ctype.base_type; if (!is_typedef && base_type && base_type->type == SYM_FN) { if (!(decl->ctype.modifiers & MOD_STATIC)) decl->ctype.modifiers |= MOD_EXTERN; } } return expect(token, ';', "at end of declaration"); } sparse-0.6.1/parse.dtd000066400000000000000000000031471355073133200146310ustar00rootroot00000000000000 sparse-0.6.1/parse.h000066400000000000000000000103001355073133200142720ustar00rootroot00000000000000#ifndef PARSE_H #define PARSE_H /* * Basic parsing data structures. Statements and symbols. * * Copyright (C) 2003 Transmeta Corp. * 2003 Linus Torvalds * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include "symbol.h" enum statement_type { STMT_NONE, STMT_DECLARATION, STMT_EXPRESSION, STMT_COMPOUND, STMT_IF, STMT_RETURN, STMT_CASE, STMT_SWITCH, STMT_ITERATOR, STMT_LABEL, STMT_GOTO, STMT_ASM, STMT_CONTEXT, STMT_RANGE, }; struct statement { enum statement_type type; struct position pos; union { struct /* declaration */ { struct symbol_list *declaration; }; struct /* context */ { struct expression *expression; struct expression *context; }; struct /* return_statement */ { struct expression *ret_value; struct symbol *ret_target; }; struct /* if_statement */ { struct expression *if_conditional; struct statement *if_true; struct statement *if_false; }; struct /* compound_struct */ { struct statement_list *stmts; struct symbol *ret; struct symbol *inline_fn; struct statement *args; }; struct /* labeled_struct */ { struct symbol *label_identifier; struct statement *label_statement; }; struct /* case_struct */ { struct expression *case_expression; struct expression *case_to; struct statement *case_statement; struct symbol *case_label; }; struct /* switch_struct */ { struct expression *switch_expression; struct statement *switch_statement; struct symbol *switch_break, *switch_case; }; struct /* iterator_struct */ { struct symbol *iterator_break; struct symbol *iterator_continue; struct symbol_list *iterator_syms; struct statement *iterator_pre_statement; struct expression *iterator_pre_condition; struct statement *iterator_statement; struct statement *iterator_post_statement; struct expression *iterator_post_condition; }; struct /* goto_struct */ { struct symbol *goto_label; /* computed gotos have these: */ struct expression *goto_expression; struct symbol_list *target_list; }; struct /* asm */ { struct expression *asm_string; struct 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); 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); #endif /* PARSE_H */ sparse-0.6.1/pre-process.c000066400000000000000000001516521355073133200154350ustar00rootroot00000000000000/* * 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; #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 replace_with_has_builtin(struct token *token) { struct symbol *sym = lookup_symbol(token->ident, NS_SYMBOL); replace_with_bool(token, sym && sym->builtin); } static void replace_with_has_attribute(struct token *token) { struct symbol *sym = lookup_symbol(token->ident, NS_KEYWORD); replace_with_bool(token, sym && sym->op && sym->op->attribute); } 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->expander) { sym->expander(token); return 1; } else { sym->used_in = file_scope; return expand(list, sym); } } static inline struct token *scan_next(struct token **where) { struct token *token = *where; if (token_type(token) != TOKEN_UNTAINT) return token; do { token->ident->tainted = 0; token = token->next; } while (token_type(token) == TOKEN_UNTAINT); *where = token; return token; } static void expand_list(struct token **list) { struct token *next; while (!eof_token(next = scan_next(list))) { if (token_type(next) != TOKEN_IDENT || expand_one_symbol(list)) list = &next->next; } } static void preprocessor_line(struct stream *stream, struct token **line); static struct token *collect_arg(struct token *prev, int vararg, struct position *pos, int count) { struct stream *stream = input_streams + prev->pos.stream; struct token **p = &prev->next; struct token *next; int nesting = 0; while (!eof_token(next = scan_next(p))) { if (next->pos.newline && match_op(next, '#')) { if (!next->pos.noexpand) { sparse_error(next->pos, "directive in macro's argument list"); preprocessor_line(stream, p); __free_token(next); /* Free the '#' token */ continue; } } switch (token_type(next)) { case TOKEN_STREAMEND: case TOKEN_STREAMBEGIN: *p = &eof_token_entry; return next; case TOKEN_STRING: case TOKEN_WIDE_STRING: if (count > 1) next->string->immutable = 1; break; } if (false_nesting) { *p = next->next; __free_token(next); continue; } if (match_op(next, '(')) { nesting++; } else if (match_op(next, ')')) { if (!nesting--) break; } else if (match_op(next, ',') && !nesting && !vararg) { break; } next->pos.stream = pos->stream; next->pos.line = pos->line; next->pos.pos = pos->pos; p = &next->next; } *p = &eof_token_entry; return next; } /* * We store arglist as [arg1] ... eof */ struct arg { struct token *arg; struct token *expanded; struct token *str; int n_normal; int n_quoted; int n_str; }; static int collect_arguments(struct token *start, struct token *arglist, struct arg *args, struct token *what) { int wanted = arglist->count.normal; struct token *next = NULL; int count = 0; arglist = arglist->next; /* skip counter */ if (!wanted) { next = collect_arg(start, 0, &what->pos, 0); if (eof_token(next)) goto Eclosing; if (!eof_token(start->next) || !match_op(next, ')')) { count++; goto Emany; } } else { for (count = 0; count < wanted; count++) { struct argcount *p = &arglist->next->count; next = collect_arg(start, p->vararg, &what->pos, p->normal); 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; int nargs = sym->arglist ? sym->arglist->count.normal : 0; struct arg args[nargs]; if (expanding->tainted) { token->pos.noexpand = 1; return 1; } if (sym->arglist) { if (!match_op(scan_next(&token->next), '(')) return 1; if (!collect_arguments(token->next, sym->arglist, args, token)) return 1; expand_arguments(nargs, args); } expanding->tainted = 1; last = token->next; tail = substitute(list, sym->expansion, args); /* * Note that it won't be eof - at least TOKEN_UNTAINT will be there. * We still can lose the newline flag if the sucker expands to nothing, * but the price of dealing with that is probably too high (we'd need * to collect the flags during scan_next()) */ (*list)->pos.newline = token->pos.newline; (*list)->pos.whitespace = token->pos.whitespace; *tail = last; return 0; } static const char *token_name_sequence(struct token *token, int endop, struct token *start) { static char buffer[256]; char *ptr = buffer; while (!eof_token(token) && !match_op(token, endop)) { int len; const char *val = token->string->data; if (token_type(token) != TOKEN_STRING) val = show_token(token); len = strlen(val); memcpy(ptr, val, len); ptr += len; token = token->next; } *ptr = 0; if (endop && !match_op(token, endop)) sparse_error(start->pos, "expected '>' at end of filename"); return buffer; } static int already_tokenized(const char *path) { int stream, next; for (stream = *hash_stream(path); stream >= 0 ; stream = next) { struct stream *s = input_streams + stream; next = s->next_stream; if (s->once) { if (strcmp(path, s->name)) continue; return 1; } if (s->constant != CONSTANT_FILE_YES) continue; if (strcmp(path, s->name)) continue; if (s->protect && !lookup_macro(s->protect)) continue; return 1; } return 0; } /* Handle include of header files. * The relevant options are made compatible with gcc. The only options that * are not supported is -withprefix and friends. * * Three set of include paths are known: * quote_includepath: Path to search when using #include "file.h" * angle_includepath: Paths to search when using #include * isys_includepath: Paths specified with -isystem, come before the * built-in system include paths. Gcc would suppress * warnings from system headers. Here we separate * them from the angle_ ones to keep search ordering. * * sys_includepath: Built-in include paths. * dirafter_includepath Paths added with -dirafter. * * The above is implemented as one array with pointers * +--------------+ * quote_includepath ---> | | * +--------------+ * | | * +--------------+ * angle_includepath ---> | | * +--------------+ * isys_includepath ---> | | * +--------------+ * sys_includepath ---> | | * +--------------+ * dirafter_includepath -> | | * +--------------+ * * -I dir insert dir just before isys_includepath and move the rest * -I- makes all dirs specified with -I before to quote dirs only and * angle_includepath is set equal to isys_includepath. * -nostdinc removes all sys dirs by storing NULL in entry pointed * to by * sys_includepath. Note that this will reset all dirs built-in * and added before -nostdinc by -isystem and -idirafter. * -isystem dir adds dir where isys_includepath points adding this dir as * first systemdir * -idirafter dir adds dir to the end of the list */ static void set_stream_include_path(struct stream *stream) { const char *path = stream->path; if (!path) { const char *p = strrchr(stream->name, '/'); path = ""; if (p) { int len = p - stream->name + 1; char *m = malloc(len+1); /* This includes the final "/" */ memcpy(m, stream->name, len); m[len] = 0; path = m; } stream->path = path; } includepath[0] = path; } #ifndef PATH_MAX #define PATH_MAX 4096 // for Hurd where it's not defined #endif static int try_include(const char *path, const char *filename, int flen, struct token **where, const char **next_path) { int fd; int plen = strlen(path); static char fullname[PATH_MAX]; memcpy(fullname, path, plen); if (plen && path[plen-1] != '/') { fullname[plen] = '/'; plen++; } memcpy(fullname+plen, filename, flen); if (already_tokenized(fullname)) return 1; fd = open(fullname, O_RDONLY); if (fd >= 0) { char *streamname = xmemdup(fullname, plen + flen); *where = tokenize(streamname, fd, *where, next_path); close(fd); return 1; } return 0; } static int do_include_path(const char **pptr, struct token **list, struct token *token, const char *filename, int flen) { const char *path; while ((path = *pptr++) != NULL) { if (!try_include(path, filename, flen, list, pptr)) continue; return 1; } return 0; } static int free_preprocessor_line(struct token *token) { while (token_type(token) != TOKEN_EOF) { struct token *free = token; token = token->next; __free_token(free); }; return 1; } static int handle_include_path(struct stream *stream, struct token **list, struct token *token, int how) { const char *filename; struct token *next; const char **path; int expect; int flen; next = token->next; expect = '>'; if (!match_op(next, '<')) { expand_list(&token->next); expect = 0; next = token; if (match_op(token->next, '<')) { next = token->next; expect = '>'; } } token = next->next; filename = token_name_sequence(token, expect, token); flen = strlen(filename) + 1; /* Absolute path? */ if (filename[0] == '/') { if (try_include("", filename, flen, list, includepath)) return 0; goto out; } switch (how) { case 1: path = stream->next_path; break; case 2: includepath[0] = ""; path = includepath; break; default: /* Dir of input file is first dir to search for quoted includes */ set_stream_include_path(stream); path = expect ? angle_includepath : quote_includepath; break; } /* Check the standard include paths.. */ if (do_include_path(path, list, token, filename, flen)) return 0; out: error_die(token->pos, "unable to open '%s'", filename); } static int handle_include(struct stream *stream, struct token **list, struct token *token) { return handle_include_path(stream, list, token, 0); } static int handle_include_next(struct stream *stream, struct token **list, struct token *token) { return handle_include_path(stream, list, token, 1); } static int handle_argv_include(struct stream *stream, struct token **list, struct token *token) { return handle_include_path(stream, list, token, 2); } static int token_different(struct token *t1, struct token *t2) { int different; if (token_type(t1) != token_type(t2)) return 1; switch (token_type(t1)) { case TOKEN_IDENT: different = t1->ident != t2->ident; break; case TOKEN_ARG_COUNT: case TOKEN_UNTAINT: case TOKEN_CONCAT: case TOKEN_GNU_KLUDGE: different = 0; break; case TOKEN_NUMBER: different = strcmp(t1->number, t2->number); break; case TOKEN_SPECIAL: different = t1->special != t2->special; break; case TOKEN_MACRO_ARGUMENT: case TOKEN_QUOTED_ARGUMENT: case TOKEN_STR_ARGUMENT: different = t1->argnum != t2->argnum; break; case TOKEN_CHAR_EMBEDDED_0 ... TOKEN_CHAR_EMBEDDED_3: case TOKEN_WIDE_CHAR_EMBEDDED_0 ... TOKEN_WIDE_CHAR_EMBEDDED_3: different = memcmp(t1->embedded, t2->embedded, 4); break; case TOKEN_CHAR: case TOKEN_WIDE_CHAR: case TOKEN_STRING: case TOKEN_WIDE_STRING: { struct string *s1, *s2; s1 = t1->string; s2 = t2->string; different = 1; if (s1->length != s2->length) break; different = memcmp(s1->data, s2->data, s1->length); break; } default: different = 1; break; } return different; } static int token_list_different(struct token *list1, struct token *list2) { for (;;) { if (list1 == list2) return 0; if (!list1 || !list2) return 1; if (token_different(list1, list2)) return 1; list1 = list1->next; list2 = list2->next; } } static inline void set_arg_count(struct token *token) { token_type(token) = TOKEN_ARG_COUNT; token->count.normal = token->count.quoted = token->count.str = token->count.vararg = 0; } static struct token *parse_arguments(struct token *list) { struct token *arg = list->next, *next = list; struct argcount *count = &list->count; set_arg_count(list); if (match_op(arg, ')')) { next = arg->next; list->next = &eof_token_entry; return next; } while (token_type(arg) == TOKEN_IDENT) { if (arg->ident == &__VA_ARGS___ident) goto Eva_args; if (!++count->normal) goto Eargs; next = arg->next; if (match_op(next, ',')) { set_arg_count(next); arg = next->next; continue; } if (match_op(next, ')')) { set_arg_count(next); next = next->next; arg->next->next = &eof_token_entry; return next; } /* normal cases are finished here */ if (match_op(next, SPECIAL_ELLIPSIS)) { if (match_op(next->next, ')')) { set_arg_count(next); next->count.vararg = 1; next = next->next; arg->next->next = &eof_token_entry; return next->next; } arg = next; goto Enotclosed; } if (eof_token(next)) { goto Enotclosed; } else { arg = next; goto Ebadstuff; } } if (match_op(arg, SPECIAL_ELLIPSIS)) { next = arg->next; token_type(arg) = TOKEN_IDENT; arg->ident = &__VA_ARGS___ident; if (!match_op(next, ')')) goto Enotclosed; if (!++count->normal) goto Eargs; set_arg_count(next); next->count.vararg = 1; next = next->next; arg->next->next = &eof_token_entry; return next; } if (eof_token(arg)) { arg = next; goto Enotclosed; } if (match_op(arg, ',')) goto Emissing; else goto Ebadstuff; Emissing: sparse_error(arg->pos, "parameter name missing"); return NULL; Ebadstuff: sparse_error(arg->pos, "\"%s\" may not appear in macro parameter list", show_token(arg)); return NULL; Enotclosed: sparse_error(arg->pos, "missing ')' in macro parameter list"); return NULL; Eva_args: sparse_error(arg->pos, "__VA_ARGS__ can only appear in the expansion of a C99 variadic macro"); return NULL; Eargs: sparse_error(arg->pos, "too many arguments in macro definition"); return NULL; } static int try_arg(struct token *token, enum token_type type, struct token *arglist) { struct ident *ident = token->ident; int nr; if (!arglist || token_type(token) != TOKEN_IDENT) return 0; arglist = arglist->next; for (nr = 0; !eof_token(arglist); nr++, arglist = arglist->next->next) { if (arglist->ident == ident) { struct argcount *count = &arglist->next->count; int n; token->argnum = nr; token_type(token) = type; switch (type) { case TOKEN_MACRO_ARGUMENT: n = ++count->normal; break; case TOKEN_QUOTED_ARGUMENT: n = ++count->quoted; break; default: n = ++count->str; } if (n) return count->vararg ? 2 : 1; /* * XXX - need saner handling of that * (>= 1024 instances of argument) */ token_type(token) = TOKEN_ERROR; return -1; } } return 0; } static struct token *handle_hash(struct token **p, struct token *arglist) { struct token *token = *p; if (arglist) { struct token *next = token->next; if (!try_arg(next, TOKEN_STR_ARGUMENT, arglist)) goto Equote; next->pos.whitespace = token->pos.whitespace; __free_token(token); token = *p = next; } else { token->pos.noexpand = 1; } return token; Equote: sparse_error(token->pos, "'#' is not followed by a macro parameter"); return NULL; } /* token->next is ## */ static struct token *handle_hashhash(struct token *token, struct token *arglist) { struct token *last = token; struct token *concat; int state = match_op(token, ','); try_arg(token, TOKEN_QUOTED_ARGUMENT, arglist); while (1) { struct token *t; int is_arg; /* eat duplicate ## */ concat = token->next; while (match_op(t = concat->next, SPECIAL_HASHHASH)) { token->next = t; __free_token(concat); concat = t; } token_type(concat) = TOKEN_CONCAT; if (eof_token(t)) goto Econcat; if (match_op(t, '#')) { t = handle_hash(&concat->next, arglist); if (!t) return NULL; } is_arg = try_arg(t, TOKEN_QUOTED_ARGUMENT, arglist); if (state == 1 && is_arg) { state = is_arg; } else { last = t; state = match_op(t, ','); } token = t; if (!match_op(token->next, SPECIAL_HASHHASH)) break; } /* handle GNU ,##__VA_ARGS__ kludge, in all its weirdness */ if (state == 2) token_type(last) = TOKEN_GNU_KLUDGE; return token; Econcat: sparse_error(concat->pos, "'##' cannot appear at the ends of macro expansion"); return NULL; } static struct token *parse_expansion(struct token *expansion, struct token *arglist, struct ident *name) { struct token *token = expansion; struct token **p; if (match_op(token, SPECIAL_HASHHASH)) goto Econcat; for (p = &expansion; !eof_token(token); p = &token->next, token = *p) { if (match_op(token, '#')) { token = handle_hash(p, arglist); if (!token) return NULL; } if (match_op(token->next, SPECIAL_HASHHASH)) { token = handle_hashhash(token, arglist); if (!token) return NULL; } else { try_arg(token, TOKEN_MACRO_ARGUMENT, arglist); } switch (token_type(token)) { case TOKEN_ERROR: goto Earg; case TOKEN_STRING: case TOKEN_WIDE_STRING: token->string->immutable = 1; break; } } token = alloc_token(&expansion->pos); token_type(token) = TOKEN_UNTAINT; token->ident = name; token->next = *p; *p = token; return expansion; Econcat: sparse_error(token->pos, "'##' cannot appear at the ends of macro expansion"); return NULL; Earg: sparse_error(token->pos, "too many instances of argument in body"); return NULL; } static int do_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(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); } 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); } static const char *show_token_sequence(struct token *token, int quote); /* * Expression handling for #if and #elif; it differs from normal expansion * due to special treatment of "defined". */ static int expression_value(struct token **where) { struct expression *expr; struct token *p; struct token **list = where, **beginning = NULL; long long value; int state = 0; while (!eof_token(p = scan_next(list))) { switch (state) { case 0: if (token_type(p) != TOKEN_IDENT) break; if (p->ident == &defined_ident) { state = 1; beginning = list; break; } else if (p->ident == &__has_builtin_ident) { state = 4; beginning = list; break; } else if (p->ident == &__has_attribute_ident) { state = 6; 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; // __has_builtin(x) or __has_attribute(x) case 4: case 6: if (match_op(p, '(')) { state++; } else { sparse_error(p->pos, "missing '(' after \"__has_%s\"", state == 4 ? "builtin" : "attribute"); state = 0; } *beginning = p; break; case 5: case 7: if (token_type(p) != TOKEN_IDENT) { sparse_error(p->pos, "identifier expected"); state = 0; break; } if (!match_op(p->next, ')')) sparse_error(p->pos, "missing ')' after \"__has_%s\"", state == 5 ? "builtin" : "attribute"); if (state == 5) replace_with_has_builtin(p); else replace_with_has_attribute(p); state = 8; *beginning = p; break; case 8: state = 0; *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 void init_preprocessor(void) { int i; int stream = init_stream("preprocessor", -1, includepath); static struct { const char *name; int (*handler)(struct stream *, struct token **, struct token *); } normal[] = { { "define", handle_define }, { "weak_define", handle_weak_define }, { "strong_define", handle_strong_define }, { "undef", handle_undef }, { "strong_undef", handle_strong_undef }, { "warning", handle_warning }, { "error", handle_error }, { "include", handle_include }, { "include_next", handle_include_next }, { "pragma", handle_pragma }, { "line", handle_line }, { "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 (*expander)(struct token *); } 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 }, }; 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->expander = dynamic[i].expander; } counter_macro = 0; } static void handle_preprocessor_line(struct stream *stream, struct token **line, struct token *start) { int (*handler)(struct stream *, struct token **, struct token *); struct token *token = start->next; int is_normal = 1; if (eof_token(token)) return; if (token_type(token) == TOKEN_IDENT) { struct symbol *sym = lookup_symbol(token->ident, NS_PREPROCESSOR); if (sym) { handler = sym->handler; is_normal = sym->normal; } else { handler = handle_nondirective; } } else if (token_type(token) == TOKEN_NUMBER) { handler = handle_line; } else { handler = handle_nondirective; } if (is_normal) { dirty_stream(stream); if (false_nesting) goto out; } if (!handler(stream, line, token)) /* all set */ return; out: free_preprocessor_line(token); } static void preprocessor_line(struct stream *stream, struct token **line) { struct token *start = *line, *next; struct token **tp = &start->next; for (;;) { next = *tp; if (next->pos.newline) break; tp = &next->next; } *line = next; *tp = &eof_token_entry; handle_preprocessor_line(stream, line, start); } static void do_preprocess(struct token **list) { struct token *next; while (!eof_token(next = scan_next(list))) { struct stream *stream = input_streams + next->pos.stream; if (next->pos.newline && match_op(next, '#')) { if (!next->pos.noexpand) { preprocessor_line(stream, list); __free_token(next); /* Free the '#' token */ continue; } } switch (token_type(next)) { case TOKEN_STREAMEND: if (stream->top_if) { nesting_error(stream); sparse_error(stream->top_if->pos, "unterminated preprocessor conditional"); stream->top_if = NULL; false_nesting = 0; } if (!stream->dirty) stream->constant = CONSTANT_FILE_YES; *list = next->next; 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.1/ptrlist.c000066400000000000000000000246741355073133200146770ustar00rootroot00000000000000/* * ptrlist.c * * (C) Copyright Linus Torvalds 2003-2005 */ /// // Pointer list manipulation // ------------------------- #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 linearized. // // Linearize the entries of a list up to a total of @max, // and return the nr of entries linearized. // // 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; if (i > max) i = max; memcpy(arr, list->list, i*sizeof(void *)); arr += i; nr += i; max -= i; if (!max) break; } while ((list = list->next) != head); } return nr; } /// // 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 splitted // // 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 elemant 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 elemant 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.1/ptrlist.h000066400000000000000000000213511355073133200146710ustar00rootroot00000000000000#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 TYPEOF(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);\ }) 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) //////////////////////////////////////////////////////////////////////// // 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, \ 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, PTR_ENTRY_NOTAG) #define FOR_EACH_PTR_TAG(head, ptr) \ DO_FOR_EACH(head, ptr, __head##ptr, __list##ptr, __nr##ptr, PTR_ENTRY_UNTAG) #define END_FOR_EACH_PTR(ptr) \ DO_END_FOR_EACH(ptr, __head##ptr, __list##ptr, __nr##ptr) #define FOR_EACH_PTR_REVERSE(head, ptr) \ DO_FOR_EACH_REVERSE(head, ptr, __head##ptr, __list##ptr, __nr##ptr, PTR_ENTRY_NOTAG) #define FOR_EACH_PTR_REVERSE_TAG(head, ptr) \ DO_FOR_EACH_REVERSE(head, ptr, __head##ptr, __list##ptr, __nr##ptr, PTR_ENTRY_UNTAG) #define END_FOR_EACH_PTR_REVERSE(ptr) \ DO_END_FOR_EACH_REVERSE(ptr, __head##ptr, __list##ptr, __nr##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, PTR_ENTRY) do { \ __typeof__(head) __head = (head); \ __typeof__(head) __list = __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) \ } \ } while ((__list = __list->next) != __head); \ } while (0) #define DO_FOR_EACH_REVERSE(head, ptr, __head, __list, __nr, PTR_ENTRY) do { \ __typeof__(head) __head = (head); \ __typeof__(head) __list = __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) \ } \ } while (__list != __head); \ } while (0) #define DO_REVERSE(ptr, __head, __list, __nr, new, __newhead, \ __newlist, __newnr, PTR_ENTRY) do { \ __typeof__(__head) __newhead = __head; \ __typeof__(__head) __newlist = __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 { \ TYPEOF(__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 { \ TYPEOF(__head) __this = __list->list + __nr; \ TYPEOF(__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.1/ptrmap.c000066400000000000000000000051501355073133200144650ustar00rootroot00000000000000// 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.1/ptrmap.h000066400000000000000000000015411355073133200144720ustar00rootroot00000000000000#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.1/scope.c000066400000000000000000000067261355073133200143050ustar00rootroot00000000000000/* * Symbol scoping. * * This is pretty trivial. * * Copyright (C) 2003 Transmeta Corp. * 2003-2004 Linus Torvalds * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include #include "lib.h" #include "allocate.h" #include "symbol.h" #include "scope.h" static struct scope builtin_scope = { .next = &builtin_scope }; struct scope *block_scope = &builtin_scope, // regular automatic variables etc *function_scope = &builtin_scope, // labels, arguments etc *file_scope = &builtin_scope, // static *global_scope = &builtin_scope; // externally visible void bind_scope(struct symbol *sym, struct scope *scope) { sym->scope = scope; add_symbol(&scope->symbols, sym); } void rebind_scope(struct symbol *sym, struct scope *new) { struct scope *old = sym->scope; if (old == new) return; if (old) delete_ptr_list_entry((struct ptr_list**) &old->symbols, sym, 1); bind_scope(sym, new); } static void start_scope(struct scope **s) { struct scope *scope = __alloc_scope(0); memset(scope, 0, sizeof(*scope)); scope->next = *s; *s = scope; } void start_file_scope(void) { struct scope *scope = __alloc_scope(0); memset(scope, 0, sizeof(*scope)); scope->next = &builtin_scope; file_scope = scope; /* top-level stuff defaults to file scope, "extern" etc will choose global scope */ function_scope = scope; block_scope = scope; } void start_symbol_scope(void) { start_scope(&block_scope); } void start_function_scope(void) { start_scope(&function_scope); start_scope(&block_scope); } static void remove_symbol_scope(struct symbol *sym) { struct symbol **ptr = &sym->ident->symbols; while (*ptr != sym) ptr = &(*ptr)->next_id; *ptr = sym->next_id; } static void end_scope(struct scope **s) { struct scope *scope = *s; struct symbol_list *symbols = scope->symbols; struct symbol *sym; *s = scope->next; scope->symbols = NULL; FOR_EACH_PTR(symbols, sym) { remove_symbol_scope(sym); } END_FOR_EACH_PTR(sym); } void end_file_scope(void) { end_scope(&file_scope); } void new_file_scope(void) { if (file_scope != &builtin_scope) end_file_scope(); start_file_scope(); } void end_symbol_scope(void) { end_scope(&block_scope); } void end_function_scope(void) { end_scope(&block_scope); end_scope(&function_scope); } int is_outer_scope(struct scope *scope) { if (scope == block_scope) return 0; if (scope == &builtin_scope && block_scope->next == &builtin_scope) return 0; return 1; } sparse-0.6.1/scope.h000066400000000000000000000036641355073133200143100ustar00rootroot00000000000000#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, *function_scope, *file_scope, *global_scope; static inline int toplevel(struct scope *scope) { return scope == file_scope || scope == global_scope; } extern void start_file_scope(void); extern void end_file_scope(void); extern void new_file_scope(void); extern void start_symbol_scope(void); extern void end_symbol_scope(void); extern void start_function_scope(void); extern void end_function_scope(void); extern void bind_scope(struct symbol *, struct scope *); extern void rebind_scope(struct symbol *, struct scope *); extern int is_outer_scope(struct scope *); #endif sparse-0.6.1/show-parse.c000066400000000000000000000676101355073133200152630ustar00rootroot00000000000000/* * sparse/show-parse.c * * Copyright (C) 2003 Transmeta Corp. * 2003-2004 Linus Torvalds * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * * Print out results of parsing for debugging and testing. */ #include #include #include #include #include #include #include #include "lib.h" #include "allocate.h" #include "token.h" #include "parse.h" #include "symbol.h" #include "scope.h" #include "expression.h" #include "target.h" static int show_symbol_expr(struct symbol *sym); static int show_string_expr(struct expression *expr); static void do_debug_symbol(struct symbol *sym, int indent) { static const char indent_string[] = " "; static const char *typestr[] = { [SYM_UNINITIALIZED] = "none", [SYM_PREPROCESSOR] = "cpp.", [SYM_BASETYPE] = "base", [SYM_NODE] = "node", [SYM_PTR] = "ptr.", [SYM_FN] = "fn..", [SYM_ARRAY] = "arry", [SYM_STRUCT] = "strt", [SYM_UNION] = "unin", [SYM_ENUM] = "enum", [SYM_TYPEDEF] = "tdef", [SYM_TYPEOF] = "tpof", [SYM_MEMBER] = "memb", [SYM_BITFIELD] = "bitf", [SYM_LABEL] = "labl", [SYM_RESTRICT] = "rstr", [SYM_FOULED] = "foul", [SYM_BAD] = "bad.", }; struct context *context; int i; if (!sym) return; fprintf(stderr, "%.*s%s%3d:%lu %s %s (as: %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. */ const char *modifier_string(unsigned long mod) { static char buffer[100]; int len = 0; int i; struct mod_name { unsigned long mod; const char *name; } *m; static struct mod_name mod_names[] = { {MOD_AUTO, "auto"}, {MOD_REGISTER, "register"}, {MOD_STATIC, "static"}, {MOD_EXTERN, "extern"}, {MOD_CONST, "const"}, {MOD_VOLATILE, "volatile"}, {MOD_RESTRICT, "restrict"}, {MOD_ATOMIC, "[atomic]"}, {MOD_SIGNED, "[signed]"}, {MOD_UNSIGNED, "[unsigned]"}, {MOD_CHAR, "[char]"}, {MOD_SHORT, "[short]"}, {MOD_LONG, "[long]"}, {MOD_LONGLONG, "[long long]"}, {MOD_LONGLONGLONG, "[long long long]"}, {MOD_TLS, "[tls]"}, {MOD_INLINE, "inline"}, {MOD_ADDRESSABLE, "[addressable]"}, {MOD_NOCAST, "[nocast]"}, {MOD_NODEREF, "[noderef]"}, {MOD_TOPLEVEL, "[toplevel]"}, {MOD_ASSIGNED, "[assigned]"}, {MOD_TYPE, "[type]"}, {MOD_SAFE, "[safe]"}, {MOD_USERTYPE, "[usertype]"}, {MOD_NORETURN, "[noreturn]"}, {MOD_EXPLICITLY_SIGNED, "[explicitly-signed]"}, {MOD_BITWISE, "[bitwise]"}, {MOD_PURE, "[pure]"}, }; for (i = 0; i < ARRAY_SIZE(mod_names); i++) { m = mod_names + i; if (mod & m->mod) { char c; const char *name = m->name; while ((c = *name++) != '\0' && len + 2 < sizeof buffer) buffer[len++] = c; buffer[len++] = ' '; } } buffer[len] = 0; return buffer; } static void show_struct_member(struct symbol *sym) { printf("\t%s:%d:%ld at offset %ld.%d", show_ident(sym->ident), sym->bit_size, sym->ctype.alignment, sym->offset, sym->bit_offset); printf("\n"); } void show_symbol_list(struct symbol_list *list, const char *sep) { struct symbol *sym; const char *prepend = ""; FOR_EACH_PTR(list, sym) { puts(prepend); prepend = ", "; show_symbol(sym); } END_FOR_EACH_PTR(sym); } 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" }, { & lllong_ctype, "long long long", "LLL" }, { &slllong_ctype, "signed long long long", "LLL" }, { &ulllong_ctype, "unsigned long long long", "ULLL" }, { &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->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; 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->r_nrbits, target, new, expr->r_bitpos); return new; } static int show_regular_preop(struct expression *expr) { int target = show_expression(expr->unop); int new = new_pseudo(); static const char *name[] = { ['!'] = "nonzero", ['-'] = "neg", ['~'] = "not", }; unsigned int op = expr->op; const char *opname; opname = show_special(op); if (op < ARRAY_SIZE(name)) opname = name[op]; printf("\t%s.%d\t\tv%d,v%d\n", opname, expr->ctype->bit_size, new, target); return new; } /* * FIXME! Not all accesses are memory loads. We should * check what kind of symbol is behind the dereference. */ static int show_address_gen(struct expression *expr) { return show_expression(expr->unop); } static int show_load_gen(int bits, struct expression *expr, int addr) { int new = new_pseudo(); printf("\tld.%d\t\tv%d,[v%d]\n", bits, new, addr); return new; } static void show_store_gen(int bits, int value, struct expression *expr, int addr) { /* FIXME!!! Bitfield store! */ printf("\tst.%d\t\tv%d,[v%d]\n", bits, value, addr); } static int show_assignment(struct expression *expr) { struct expression *target = expr->left; int val, addr, bits; if (!expr->ctype) return 0; bits = expr->ctype->bit_size; val = show_expression(expr->right); addr = show_address_gen(target); show_store_gen(bits, val, target, addr); return val; } static int show_return_stmt(struct statement *stmt) { struct expression *expr = stmt->ret_value; struct symbol *target = stmt->ret_target; if (expr && expr->ctype) { int val = show_expression(expr); int bits = expr->ctype->bit_size; int addr = show_symbol_expr(target); show_store_gen(bits, val, NULL, addr); } printf("\tret\t\t(%p)\n", target); return 0; } static int show_initialization(struct symbol *sym, struct expression *expr) { int val, addr, bits; if (!expr->ctype) return 0; bits = expr->ctype->bit_size; val = show_expression(expr); addr = show_symbol_expr(sym); // FIXME! The "target" expression is for bitfield store information. // Leave it NULL, which works fine. show_store_gen(bits, val, NULL, addr); return 0; } static int show_access(struct expression *expr) { int addr = show_address_gen(expr); return show_load_gen(expr->ctype->bit_size, expr, addr); } static int show_inc_dec(struct expression *expr, int postop) { int addr = show_address_gen(expr->unop); int retval, new; const char *opname = expr->op == SPECIAL_INCREMENT ? "add" : "sub"; int bits = expr->ctype->bit_size; retval = show_load_gen(bits, expr->unop, addr); new = retval; if (postop) new = new_pseudo(); printf("\t%s.%d\t\tv%d,v%d,$1\n", opname, bits, new, retval); show_store_gen(bits, new, expr->unop, addr); return retval; } static int show_preop(struct expression *expr) { /* * '*' is an lvalue access, and is fundamentally different * from an arithmetic operation. Maybe it should have an * expression type of its own.. */ if (expr->op == '*') return show_access(expr); if (expr->op == SPECIAL_INCREMENT || expr->op == SPECIAL_DECREMENT) return show_inc_dec(expr, 0); return show_regular_preop(expr); } static int show_postop(struct expression *expr) { return show_inc_dec(expr, 1); } static int show_symbol_expr(struct symbol *sym) { int new = new_pseudo(); if (sym->initializer && sym->initializer->type == EXPR_STRING) return show_string_expr(sym->initializer); if (sym->ctype.modifiers & (MOD_TOPLEVEL | MOD_EXTERN | MOD_STATIC)) { printf("\tmovi.%d\t\tv%d,$%s\n", bits_in_pointer, new, show_ident(sym->ident)); return new; } if (sym->ctype.modifiers & MOD_ADDRESSABLE) { printf("\taddi.%d\t\tv%d,vFP,$%lld\n", bits_in_pointer, new, 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; } return 0; } sparse-0.6.1/simplify.c000066400000000000000000001216121355073133200150200ustar00rootroot00000000000000/* * 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 "flow.h" #include "symbol.h" /// // Utilities // ^^^^^^^^^ /// // 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 a function like linearize_ptr_list() // because any VOIDs in the phi-list must be ignored here // as in this context they mean 'entry has been removed'. static int get_phisources(struct instruction *sources[], int nbr, struct instruction *insn) { pseudo_t phi; int i = 0; assert(insn->opcode == OP_PHI); FOR_EACH_PTR(insn->phi_list, phi) { struct instruction *def; if (phi == VOID) continue; if (i >= nbr) return 1; def = phi->def; assert(def->opcode == OP_PHISOURCE); sources[i++] = def; } END_FOR_EACH_PTR(phi); return i - nbr; } static int if_convert_phi(struct instruction *insn) { struct instruction *array[2]; struct basic_block *parents[3]; struct basic_block *bb, *bb1, *bb2, *source; struct instruction *br; pseudo_t p1, p2; bb = insn->bb; if (get_phisources(array, 2, insn)) return 0; if (linearize_ptr_list((struct ptr_list *)bb->parents, (void **)parents, 3) != 2) return 0; p1 = array[0]->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 (!pseudo) { pseudo = src; continue; } if (src == pseudo) continue; if (src == target) 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)) { if (p->type == PSEUDO_SYM) repeat_phase |= REPEAT_SYMBOL_CLEANUP; delete_pseudo_user_list_entry(&p->users, usep, 1); if (kill && !p->users) 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); } /// // 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_SETVAL: case OP_SLICE: kill_use(&insn->src1); break; case OP_PHI: kill_use_list(insn->phi_list); break; case OP_PHISOURCE: kill_use(&insn->phi_src); break; case OP_SYMADDR: kill_use(&insn->src); repeat_phase |= REPEAT_SYMBOL_CLEANUP; break; case OP_CBR: case OP_SWITCH: case OP_COMPUTEDGOTO: kill_use(&insn->cond); break; case OP_CALL: if (!force) { /* a "pure" function can be killed too */ if (!(insn->func->type == PSEUDO_SYM)) return 0; if (!(insn->func->sym->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_ENTRY: /* ignore */ return 0; case OP_BR: case OP_SETFVAL: default: break; } insn->bb = NULL; return repeat_phase |= REPEAT_CSE; } /// // kill trivially dead instructions static int dead_insn(struct instruction *insn, pseudo_t *src1, pseudo_t *src2, pseudo_t *src3) { if (has_users(insn->target)) return 0; insn->bb = NULL; kill_use(src1); kill_use(src2); kill_use(src3); return 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; } /// // 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; } static int replace_with_pseudo(struct instruction *insn, pseudo_t pseudo) { convert_instruction_target(insn, pseudo); switch (insn->opcode) { case OP_SEL: case OP_RANGE: kill_use(&insn->src3); case OP_BINARY ... OP_BINCMP_END: kill_use(&insn->src2); case OP_UNOP ... OP_UNOP_END: case OP_SYMADDR: kill_use(&insn->src1); break; default: assert(0); } insn->bb = NULL; 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_insn(struct instruction *insn) { /* FIXME! Verify signs and sizes!! */ unsigned int size = insn->size; long long left = insn->src1->value; long long right = insn->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 (insn->opcode) { 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; } res &= bits; return value_pseudo(res); undef: return NULL; } /// // 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 (multi_users(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 && !multi_users(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 && !multi_users(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 (!multi_users(sh->target)) return simplify_mask_shift_or(sh, inner, mask); break; } return 0; } static long long check_shift_count(struct instruction *insn, unsigned long long uval) { unsigned int size = insn->size; long long sval = uval; if (uval < size) return uval; sval = sign_extend_safe(sval, size); sval = sign_extend_safe(sval, bits_in_int); if (sval < 0) insn->src2 = value_pseudo(sval); if (insn->tainted) return sval; if (sval < 0 && Wshift_count_negative) warning(insn->pos, "shift count is negative (%lld)", sval); if (sval > 0 && Wshift_count_overflow) { struct symbol *ctype = insn->type; const char *tname; if (ctype->type == SYM_NODE) ctype = ctype->ctype.base_type; tname = show_typename(ctype); warning(insn->pos, "shift too big (%llu) for type %s", sval, tname); } insn->tainted = 1; return sval; } 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_pseudo(insn, value_pseudo(0)); if (nmask == mask) return replace_pseudo(insn, &insn->src1, def->src1); if (nbr_users(pseudo) > 1) 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_pseudo(insn, value_pseudo(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_pseudo(insn, value_pseudo(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; 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 return replace_pseudo(insn, &insn->src1, def->src); case OP_TRUNC: if (multi_users(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_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_constant_rightside(struct instruction *insn) { long long value = insn->src2->value; long long sbit = 1ULL << (insn->size - 1); long long bits = sbit | (sbit - 1); 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; } goto case_neutral_zero; case OP_SUB: if (value) { insn->opcode = OP_ADD; insn->src2 = value_pseudo(-value); return REPEAT_CSE; } /* Fall through */ case OP_ADD: case_neutral_zero: if (!value) return replace_with_pseudo(insn, insn->src1); return 0; 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_pseudo(insn, value_pseudo(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: return simplify_seteq_setne(insn, value); } return 0; } static int simplify_constant_leftside(struct instruction *insn) { long long value = insn->src1->value; switch (insn->opcode) { case OP_ADD: case OP_OR: case OP_XOR: if (!value) return replace_with_pseudo(insn, insn->src2); return 0; case OP_SHL: case OP_LSR: case OP_ASR: case OP_AND: case OP_MUL: if (!value) return replace_with_pseudo(insn, insn->src1); return 0; } return 0; } static int simplify_constant_binop(struct instruction *insn) { pseudo_t res = eval_insn(insn); if (!res) return 0; replace_with_pseudo(insn, res); return REPEAT_CSE; } static int simplify_binop_same_args(struct instruction *insn, pseudo_t arg) { switch (insn->opcode) { case OP_SET_NE: case OP_SET_LT: case OP_SET_GT: case OP_SET_B: case OP_SET_A: if (Wtautological_compare) warning(insn->pos, "self-comparison always evaluates to false"); case OP_SUB: case OP_XOR: return replace_with_pseudo(insn, value_pseudo(0)); case OP_SET_EQ: case OP_SET_LE: case OP_SET_GE: case OP_SET_BE: case OP_SET_AE: if (Wtautological_compare) warning(insn->pos, "self-comparison always evaluates to true"); return replace_with_pseudo(insn, value_pseudo(1)); case OP_AND: case OP_OR: return replace_with_pseudo(insn, arg); default: break; } return 0; } static int simplify_binop(struct instruction *insn) { if (dead_insn(insn, &insn->src1, &insn->src2, NULL)) return REPEAT_CSE; if (constant(insn->src1)) { if (constant(insn->src2)) return simplify_constant_binop(insn); return simplify_constant_leftside(insn); } if (constant(insn->src2)) return simplify_constant_rightside(insn); if (insn->src1 == insn->src2) return simplify_binop_same_args(insn, insn->src1); return 0; } static void switch_pseudo(struct instruction *insn1, pseudo_t *pp1, struct instruction *insn2, pseudo_t *pp2) { pseudo_t p1 = *pp1, p2 = *pp2; use_pseudo(insn1, p2, pp1); use_pseudo(insn2, p1, pp2); remove_usage(p1, pp1); remove_usage(p2, pp2); } static int canonical_order(pseudo_t p1, pseudo_t p2) { /* symbol/constants on the right */ if (p1->type == PSEUDO_VAL) return p2->type == PSEUDO_VAL; if (p1->type == PSEUDO_SYM) return p2->type == PSEUDO_SYM || p2->type == PSEUDO_VAL; return 1; } static int 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; } 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 (multi_users(def->target)) return 0; switch_pseudo(def, &def->src1, insn, &insn->src2); return REPEAT_CSE; } static int simplify_constant_unop(struct instruction *insn) { long long val = insn->src1->value; long long res, mask; switch (insn->opcode) { case OP_NOT: res = ~val; break; case OP_NEG: res = -val; break; 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); replace_with_pseudo(insn, value_pseudo(res)); return REPEAT_CSE; } static int simplify_unop(struct instruction *insn) { if (dead_insn(insn, &insn->src1, NULL, NULL)) return REPEAT_CSE; if (constant(insn->src1)) return simplify_constant_unop(insn); switch (insn->opcode) { struct instruction *def; case OP_NOT: def = insn->src->def; if (def && def->opcode == OP_NOT) return replace_with_pseudo(insn, def->src); break; case OP_NEG: def = insn->src->def; if (def && def->opcode == OP_NEG) return replace_with_pseudo(insn, def->src); break; default: return 0; } return 0; } static int simplify_one_memop(struct instruction *insn, pseudo_t orig) { pseudo_t addr = insn->src; pseudo_t new, off; if (addr->type == PSEUDO_REG) { struct instruction *def = addr->def; if (def->opcode == OP_SYMADDR && def->src) { kill_use(&insn->src); use_pseudo(insn, def->src, &insn->src); return REPEAT_CSE | REPEAT_SYMBOL_CLEANUP; } if (def->opcode == OP_ADD) { new = def->src1; off = def->src2; if (constant(off)) goto offset; new = off; off = def->src1; if (constant(off)) goto offset; return 0; } } return 0; offset: /* Invalid code */ if (new == orig || 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 | REPEAT_SYMBOL_CLEANUP; } /// // 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; pseudo_t src; pseudo_t val; int osize; int size; if (dead_insn(insn, &insn->src, NULL, NULL)) return REPEAT_CSE; src = insn->src; /* 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 (multi_users(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 (multi_users(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; use_pseudo(insn, def->src2, &insn->src2); return replace_pseudo(insn, &insn->src1, def->src1); } 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_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; if (dead_insn(insn, &insn->src1, &insn->src2, &insn->src3)) return REPEAT_CSE; cond = insn->src1; src1 = insn->src2; src2 = insn->src3; if (constant(cond) || src1 == src2) { pseudo_t *kill, take; kill_use(&insn->src1); take = cond->value ? src1 : src2; kill = cond->value ? &insn->src3 : &insn->src2; kill_use(kill); replace_with_pseudo(insn, take); return REPEAT_CSE; } if (constant(src1) && constant(src2)) { long long val1 = src1->value; long long val2 = src2->value; /* The pair 0/1 is special - replace with SETNE/SETEQ */ if ((val1 | val2) == 1) { int opcode = OP_SET_EQ; if (val1) { src1 = src2; opcode = OP_SET_NE; } insn->opcode = opcode; /* insn->src1 is already cond */ insn->src2 = src1; /* Zero */ return REPEAT_CSE; } } if (cond == src2 && is_zero(src1)) { kill_use(&insn->src1); kill_use(&insn->src3); replace_with_pseudo(insn, value_pseudo(0)); return REPEAT_CSE; } return 0; } static int is_in_range(pseudo_t src, long long low, long long high) { long long value; switch (src->type) { case PSEUDO_VAL: value = src->value; return value >= low && value <= high; default: return 0; } } static int simplify_range(struct instruction *insn) { pseudo_t src1, src2, src3; src1 = insn->src1; src2 = insn->src2; src3 = insn->src3; if (src2->type != PSEUDO_VAL || src3->type != PSEUDO_VAL) return 0; if (is_in_range(src1, src2->value, src3->value)) { kill_instruction(insn); return REPEAT_CSE; } return 0; } /// // simplify SET_NE/EQ $0 + BR static int simplify_cond_branch(struct instruction *br, 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)) { insert_branch(insn->bb, insn, cond->value ? insn->bb_true : insn->bb_false); return REPEAT_CSE; } /* Same target? */ if (insn->bb_true == insn->bb_false) { struct basic_block *bb = insn->bb; struct basic_block *target = insn->bb_false; remove_bb_from_list(&target->parents, bb, 1); remove_bb_from_list(&bb->children, target, 1); insn->bb_false = NULL; kill_use(&insn->cond); insn->cond = NULL; insn->opcode = OP_BR; return REPEAT_CSE; } /* Conditional on a SETNE $0 or SETEQ $0 */ if (cond->type == PSEUDO_REG) { struct instruction *def = cond->def; if (def->opcode == OP_SET_NE || def->opcode == OP_SET_EQ) { if (constant(def->src1) && !def->src1->value) return simplify_cond_branch(insn, 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) { insert_branch(insn->bb, insn, insn->bb_false); return REPEAT_CSE; } if (val1 && val2) { insert_branch(insn->bb, insn, insn->bb_true); return REPEAT_CSE; } if (val2) { struct basic_block *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: insert_branch(insn->bb, insn, jmp->target); return REPEAT_CSE; } int simplify_instruction(struct instruction *insn) { if (!insn->bb) return 0; switch (insn->opcode) { case OP_ADD: case OP_MUL: case OP_AND: case OP_OR: case OP_XOR: canonicalize_commutative(insn); if (simplify_binop(insn)) return REPEAT_CSE; return simplify_associative_binop(insn); case OP_SET_EQ: case OP_SET_NE: canonicalize_commutative(insn); return simplify_binop(insn); 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: canonicalize_compare(insn); /* fall through */ case OP_SUB: case OP_DIVU: case OP_DIVS: case OP_MODU: case OP_MODS: case OP_SHL: case OP_LSR: case OP_ASR: return simplify_binop(insn); case OP_NOT: case OP_NEG: case OP_FNEG: return simplify_unop(insn); case OP_LOAD: if (!has_users(insn->target)) return kill_instruction(insn); /* fall-through */ case OP_STORE: return simplify_memop(insn); case OP_SYMADDR: if (dead_insn(insn, &insn->src, NULL, NULL)) return REPEAT_CSE | REPEAT_SYMBOL_CLEANUP; return replace_with_pseudo(insn, insn->src); case OP_SEXT: case OP_ZEXT: case OP_TRUNC: return simplify_cast(insn); case OP_FCVTU: case OP_FCVTS: case OP_UCVTF: case OP_SCVTF: case OP_FCVTF: case OP_PTRCAST: if (dead_insn(insn, &insn->src, NULL, NULL)) return REPEAT_CSE; break; case OP_UTPTR: case OP_PTRTU: return replace_with_pseudo(insn, insn->src); case OP_SLICE: if (dead_insn(insn, &insn->src, NULL, NULL)) return REPEAT_CSE; break; case OP_SETVAL: case OP_SETFVAL: if (dead_insn(insn, NULL, NULL, NULL)) return REPEAT_CSE; break; case OP_PHI: if (dead_insn(insn, NULL, NULL, NULL)) { kill_use_list(insn->phi_list); return REPEAT_CSE; } return clean_up_phi(insn); case OP_PHISOURCE: if (dead_insn(insn, &insn->phi_src, NULL, NULL)) return REPEAT_CSE; break; case OP_SEL: return simplify_select(insn); case OP_CBR: return simplify_branch(insn); case OP_SWITCH: return simplify_switch(insn); case OP_RANGE: return simplify_range(insn); case OP_FADD: case OP_FSUB: case OP_FMUL: case OP_FDIV: if (dead_insn(insn, &insn->src1, &insn->src2, NULL)) return REPEAT_CSE; break; } return 0; } sparse-0.6.1/sort.c000066400000000000000000000131331355073133200141510ustar00rootroot00000000000000/* * 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.1/sparse-llvm-dis000077500000000000000000000004171355073133200157670ustar00rootroot00000000000000#!/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.1/sparse-llvm.c000066400000000000000000000771721355073133200154440ustar00rootroot00000000000000/* * 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) { LLVMValueRef v; struct instruction *phi; assert(insn->target->priv == NULL); /* target = src */ v = get_operand(fn, insn->type, insn->phi_src); FOR_EACH_PTR(insn->phi_users, phi) { LLVMValueRef load, ptr; assert(phi->opcode == OP_PHI); /* phi must be load from alloca */ load = phi->target->priv; assert(LLVMGetInstructionOpcode(load) == LLVMLoad); ptr = LLVMGetOperand(load, 0); /* store v to alloca */ LLVMBuildStore(fn->builder, v, ptr); } END_FOR_EACH_PTR(phi); } static void output_op_phi(struct function *fn, struct instruction *insn) { LLVMValueRef load = insn->target->priv; /* forward load */ assert(LLVMGetInstructionOpcode(load) == LLVMLoad); /* forward load has no parent block */ assert(!LLVMGetInstructionParent(load)); /* finalize load in current block */ LLVMInsertIntoBuilder(fn->builder, load); } static void output_op_ptrcast(struct function *fn, struct instruction *insn) { LLVMValueRef src, target; 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_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_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]; struct instruction *insn; sprintf(bbname, "L%d", nr_bb++); bbr = LLVMAppendBasicBlock(function.fn, bbname); bb->priv = bbr; /* allocate alloca for each phi */ FOR_EACH_PTR(bb->insns, insn) { LLVMBasicBlockRef entrybbr; LLVMTypeRef phi_type; LLVMValueRef ptr; if (!insn->bb || insn->opcode != OP_PHI) continue; /* insert alloca into entry block */ entrybbr = LLVMGetEntryBasicBlock(function.fn); LLVMPositionBuilderAtEnd(function.builder, entrybbr); phi_type = insn_symbol_type(insn); ptr = LLVMBuildAlloca(function.builder, phi_type, ""); /* emit forward load for phi */ LLVMClearInsertionPosition(function.builder); insn->target->priv = LLVMBuildLoad(function.builder, ptr, "phi"); } END_FOR_EACH_PTR(insn); } END_FOR_EACH_PTR(bb); FOR_EACH_PTR(ep->bbs, bb) { LLVMPositionBuilderAtEnd(function.builder, bb->priv); output_bb(&function, bb); } 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); /* need ->phi_users */ dbg_dead = 1; 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.1/sparse.1000066400000000000000000000411601355073133200143760ustar00rootroot00000000000000.\" 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-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 \-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 \-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 \-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. . .SH MISC OPTIONS .TP .B \-gcc-base-dir \fIdir\fR Look for compiler-provided system headers in \fIdir\fR/include/ and \fIdir\fR/include-fixed/. . .TP .B \-multiarch-dir \fIdir\fR Look for system headers in the multiarch subdirectory \fIdir\fR. The \fIdir\fR name would normally take the form of the target's normalized GNU triplet. (e.g. i386-linux-gnu). . .SH DEBUG OPTIONS .TP .B \-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-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 http://www.kernel.org/pub/software/devel/sparse/ . .SH MAILING LIST linux-sparse@vger.kernel.org . .SH CONTRIBUTINGS 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 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.1/sparse.c000066400000000000000000000204551355073133200144640ustar00rootroot00000000000000/* * 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 pseudo_t argument(struct instruction *call, unsigned int argno) { pseudo_t args[8]; struct ptr_list *arg_list = (struct ptr_list *) call->arguments; argno--; if (linearize_ptr_list(arg_list, (void *)args, 8) > argno) return args[argno]; return NULL; } static void check_memset(struct instruction *insn) { check_byte_count(insn, argument(insn, 3)); } #define check_memcpy check_memset #define check_ctu check_memset #define check_cfu check_memset struct checkfn { struct ident *id; void (*check)(struct instruction *insn); }; static void check_call_instruction(struct instruction *insn) { pseudo_t fn = insn->func; struct ident *ident; static const struct checkfn check_fn[] = { { &memset_ident, check_memset }, { &memcpy_ident, check_memcpy }, { ©_to_user_ident, check_ctu }, { ©_from_user_ident, check_cfu }, }; int i; if (fn->type != PSEUDO_SYM) return; ident = fn->sym->ident; if (!ident) return; for (i = 0; i < ARRAY_SIZE(check_fn); i++) { if (check_fn[i].id != ident) continue; check_fn[i].check(insn); break; } } static void check_one_instruction(struct instruction *insn) { switch (insn->opcode) { case OP_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.1/sparsec000077500000000000000000000016761355073133200144150ustar00rootroot00000000000000#!/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.1/sparsei000077500000000000000000000006241355073133200144130ustar00rootroot00000000000000#!/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.1/ssa.c000066400000000000000000000212271355073133200137530ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // SSA conversion // Copyright (C) 2005 Luc Van Oostenryck // #include #include "ssa.h" #include "lib.h" #include "sset.h" #include "dominate.h" #include "flowgraph.h" #include "linearize.h" #include "flow.h" // for convert_load_instruction() // 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 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 bool insn_before(struct instruction *a, struct instruction *b) { struct basic_block *bb = a->bb; struct instruction *insn; assert(b->bb == bb); FOR_EACH_PTR(bb->insns, insn) { if (insn == a) return true; if (insn == b) return false; } END_FOR_EACH_PTR(insn); assert(0); } static void kill_store(struct instruction *insn) { remove_use(&insn->src); remove_use(&insn->target); insn->bb = NULL; } static void rewrite_local_var(struct basic_block *bb, pseudo_t addr, int nbr_stores, int nbr_uses) { struct instruction *insn; pseudo_t val = NULL; if (!bb) return; FOR_EACH_PTR(bb->insns, insn) { if (!insn->bb || insn->src != addr) continue; switch (insn->opcode) { case OP_LOAD: if (!val) val = undef_pseudo(); convert_load_instruction(insn, val); break; case OP_STORE: val = insn->target; // can't use kill_instruction() unless // we add a fake user to val kill_store(insn); break; } } END_FOR_EACH_PTR(insn); } static bool rewrite_single_store(struct instruction *store) { pseudo_t addr = store->src; struct pseudo_user *pu; FOR_EACH_PTR(addr->users, pu) { struct instruction *insn = pu->insn; if (insn->opcode != OP_LOAD) continue; // Let's try to replace the value of the load // by the value from the store. This is only valid // if the store dominate the load. if (insn->bb == store->bb) { // the load and the store are in the same BB // we can convert if the load is after the store. if (!insn_before(store, insn)) continue; } else if (!domtree_dominates(store->bb, insn->bb)) { // we can't convert this load continue; } // OK, we can rewrite this load // undefs ? convert_load_instruction(insn, store->target); } END_FOR_EACH_PTR(pu); // is there some unconverted loads? if (pseudo_user_list_size(addr->users) > 1) return false; kill_store(store); return true; } static struct sset *processed; // 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) { struct basic_block_list *alpha = NULL; struct basic_block_list *idf = NULL; struct basic_block *samebb = NULL; struct instruction *store = 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 sset_reset(processed); 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++; store = insn; if (!sset_testset(processed, bb->nr)) 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 (nbr_stores == 1) { if (rewrite_single_store(store)) return; } // 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 pseudo_t lookup_var(struct basic_block *bb, struct symbol *var) { do { pseudo_t val = phi_map_lookup(bb->phi_map, var); if (val) return val; } while ((bb = bb->idom)); return undef_pseudo(); } static struct instruction_list *phis_all; static struct instruction_list *phis_used; static void ssa_rename_insn(struct basic_block *bb, struct instruction *insn) { 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->target); kill_store(insn); break; case OP_LOAD: addr = insn->src; if (addr->type != PSEUDO_SYM) break; var = addr->sym; if (!var || !var->torename) break; val = lookup_var(bb, var); convert_load_instruction(insn, val); break; case OP_PHI: var = insn->type; if (!var || !var->torename) break; phi_map_update(&bb->phi_map, var, insn->target); 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 *term = delete_last_instruction(&par->insns); pseudo_t val = lookup_var(par, var); pseudo_t phi = alloc_phi(par, val, var); phi->ident = var->ident; add_instruction(&par->insns, term); use_pseudo(insn, phi, add_pseudo(&insn->phi_list, 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); } 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; } END_FOR_EACH_PTR(bb); processed = sset_init(first, last); // try to promote memory accesses to pseudos 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); } sparse-0.6.1/ssa.h000066400000000000000000000001421355073133200137510ustar00rootroot00000000000000#ifndef SSA_H #define SSA_H struct entrypoint; void ssa_convert(struct entrypoint *ep); #endif sparse-0.6.1/sset.c000066400000000000000000000011021355073133200141310ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // // sset.c - an all O(1) implementation of sparse sets as presented in: // "An Efficient Representation for Sparse Sets" // by Preston Briggs and Linda Torczon // // Copyright (C) 2017 - Luc Van Oostenryck #include "sset.h" #include "lib.h" #include struct sset *sset_init(unsigned int first, unsigned int last) { unsigned int size = last - first + 1; struct sset *s = malloc(sizeof(*s) + size * 2 * sizeof(s->sets[0])); s->size = size; s->off = first; s->nbr = 0; return s; } void sset_free(struct sset *s) { free(s); } sparse-0.6.1/sset.h000066400000000000000000000021151355073133200141430ustar00rootroot00000000000000// SPDX-License-Identifier: MIT #ifndef SSET_H #define SSET_H /* * sset.h - an all O(1) implementation of sparse sets as presented in: * "An Efficient Representation for Sparse Sets" * by Preston Briggs and Linda Torczon * * Copyright (C) 2017 - Luc Van Oostenryck */ #include struct sset { unsigned int nbr; unsigned int off; unsigned int size; unsigned int sets[0]; }; extern struct sset *sset_init(unsigned int size, unsigned int off); extern void sset_free(struct sset *s); static inline void sset_reset(struct sset *s) { s->nbr = 0; } static inline void sset_add(struct sset *s, unsigned int idx) { unsigned int __idx = idx - s->off; unsigned int n = s->nbr++; s->sets[__idx] = n; s->sets[s->size + n] = __idx; } static inline bool sset_test(struct sset *s, unsigned int idx) { unsigned int __idx = idx - s->off; unsigned int n = s->sets[__idx]; return (n < s->nbr) && (s->sets[s->size + n] == __idx); } static inline bool sset_testset(struct sset *s, unsigned int idx) { if (sset_test(s, idx)) return true; sset_add(s, idx); return false; } #endif sparse-0.6.1/stats.c000066400000000000000000000032621355073133200143220ustar00rootroot00000000000000#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.1/storage.c000066400000000000000000000166501355073133200146350ustar00rootroot00000000000000/* * Storage - associate pseudos with "storage" that keeps them alive * between basic blocks. The aim is to be able to turn as much of * the global storage allocation problem as possible into a local * per-basic-block one. * * Copyright (C) 2004 Linus Torvalds */ #include #include #include #include "symbol.h" #include "expression.h" #include "linearize.h" #include "storage.h" ALLOCATOR(storage, "storages"); ALLOCATOR(storage_hash, "storage hash"); #define MAX_STORAGE_HASH 64 static struct storage_hash_list *storage_hash_table[MAX_STORAGE_HASH]; static inline unsigned int storage_hash(struct basic_block *bb, pseudo_t pseudo, enum inout_enum inout) { unsigned hash = hashval(bb) + hashval(pseudo) + hashval(inout); hash += hash / MAX_STORAGE_HASH; return hash & (MAX_STORAGE_HASH-1); } static int hash_list_cmp(const void *_a, const void *_b) { const struct storage_hash *a = _a; const struct storage_hash *b = _b; if (a->pseudo != b->pseudo) return a->pseudo < b->pseudo ? -1 : 1; return 0; } static void sort_hash_list(struct storage_hash_list **listp) { sort_list((struct ptr_list **)listp, hash_list_cmp); } struct storage_hash_list *gather_storage(struct basic_block *bb, enum inout_enum inout) { int i; struct storage_hash *entry, *prev; struct storage_hash_list *list = NULL; for (i = 0; i < MAX_STORAGE_HASH; i++) { struct storage_hash *hash; FOR_EACH_PTR(storage_hash_table[i], hash) { if (hash->bb == bb && hash->inout == inout) add_ptr_list(&list, hash); } END_FOR_EACH_PTR(hash); } sort_hash_list(&list); prev = NULL; FOR_EACH_PTR(list, entry) { if (prev && entry->pseudo == prev->pseudo) { assert(entry == prev); DELETE_CURRENT_PTR(entry); } prev = entry; } END_FOR_EACH_PTR(entry); PACK_PTR_LIST(&list); return list; } static void name_storage(void) { int i; int name = 0; for (i = 0; i < MAX_STORAGE_HASH; i++) { struct storage_hash *hash; FOR_EACH_PTR(storage_hash_table[i], hash) { struct storage *storage = hash->storage; if (storage->name) continue; storage->name = ++name; } END_FOR_EACH_PTR(hash); } } struct storage *lookup_storage(struct basic_block *bb, pseudo_t pseudo, enum inout_enum inout) { struct storage_hash_list *list = storage_hash_table[storage_hash(bb,pseudo,inout)]; struct storage_hash *hash; FOR_EACH_PTR(list, hash) { if (hash->bb == bb && hash->pseudo == pseudo && hash->inout == inout) return hash->storage; } END_FOR_EACH_PTR(hash); return NULL; } void add_storage(struct storage *storage, struct basic_block *bb, pseudo_t pseudo, enum inout_enum inout) { struct storage_hash_list **listp = storage_hash_table + storage_hash(bb,pseudo,inout); struct storage_hash *hash = alloc_storage_hash(storage); hash->bb = bb; hash->pseudo = pseudo; hash->inout = inout; add_ptr_list(listp, hash); } static int storage_hash_cmp(const void *_a, const void *_b) { const struct storage_hash *a = _a; const struct storage_hash *b = _b; struct storage *aa = a->storage; struct storage *bb = b->storage; if (a->bb != b->bb) return a->bb < b->bb ? -1 : 1; if (a->inout != b->inout) return a->inout < b->inout ? -1 : 1; if (aa->type != bb->type) return aa->type < bb->type ? -1 : 1; if (aa->regno != bb->regno) return aa->regno < bb->regno ? -1 : 1; return 0; } static void vrfy_storage(struct storage_hash_list **listp) { struct storage_hash *entry, *last; sort_list((struct ptr_list **)listp, storage_hash_cmp); last = NULL; FOR_EACH_PTR(*listp, entry) { if (last) { struct storage *a = last->storage; struct storage *b = entry->storage; if (a == b) continue; if (last->bb == entry->bb && last->inout == entry->inout && a->type != REG_UDEF && a->type == b->type && a->regno == b->regno) { printf("\t BAD: same storage as %s in %p: %s (%s and %s)\n", last->inout == STOR_IN ? "input" : "output", last->bb, show_storage(a), show_pseudo(last->pseudo), show_pseudo(entry->pseudo)); } } last = entry; } END_FOR_EACH_PTR(entry); } void free_storage(void) { int i; for (i = 0; i < MAX_STORAGE_HASH; i++) { vrfy_storage(storage_hash_table + i); free_ptr_list(storage_hash_table + i); } } const char *show_storage(struct storage *s) { static char buffer[1024]; if (!s) return "none"; switch (s->type) { case REG_REG: sprintf(buffer, "reg%d (%d)", s->regno, s->name); break; case REG_STACK: sprintf(buffer, "%d(SP) (%d)", s->offset, s->name); break; case REG_ARG: sprintf(buffer, "ARG%d (%d)", s->regno, s->name); break; default: sprintf(buffer, "%d:%d (%d)", s->type, s->regno, s->name); break; } return buffer; } /* * Combine two storage allocations into one. * * We just randomly pick one over the other, and replace * the other uses. */ static struct storage * combine_storage(struct storage *src, struct storage *dst) { struct storage **usep; /* Remove uses of "src_storage", replace with "dst" */ FOR_EACH_PTR(src->users, usep) { assert(*usep == src); *usep = dst; add_ptr_list(&dst->users, usep); } END_FOR_EACH_PTR(usep); /* Mark it unused */ src->type = REG_BAD; src->users = NULL; return dst; } static void set_up_bb_storage(struct basic_block *bb) { struct basic_block *child; FOR_EACH_PTR(bb->children, child) { pseudo_t pseudo; FOR_EACH_PTR(child->needs, pseudo) { struct storage *child_in, *parent_out; parent_out = lookup_storage(bb, pseudo, STOR_OUT); child_in = lookup_storage(child, pseudo, STOR_IN); if (parent_out) { if (!child_in) { add_storage(parent_out, child, pseudo, STOR_IN); continue; } if (parent_out == child_in) continue; combine_storage(parent_out, child_in); continue; } if (child_in) { add_storage(child_in, bb, pseudo, STOR_OUT); continue; } parent_out = alloc_storage(); add_storage(parent_out, bb, pseudo, STOR_OUT); add_storage(parent_out, child, pseudo, STOR_IN); } END_FOR_EACH_PTR(pseudo); } END_FOR_EACH_PTR(child); } static void set_up_argument_storage(struct entrypoint *ep, struct basic_block *bb) { pseudo_t arg; FOR_EACH_PTR(bb->needs, arg) { struct storage *storage = alloc_storage(); /* FIXME! Totally made-up argument passing conventions */ if (arg->type == PSEUDO_ARG) { storage->type = REG_ARG; storage->regno = arg->nr; } add_storage(storage, bb, arg, STOR_IN); } END_FOR_EACH_PTR(arg); } /* * One phi-source may feed multiple phi nodes. If so, combine * the storage output for this bb into one entry to reduce * storage pressure. */ static void combine_phi_storage(struct basic_block *bb) { struct instruction *insn; FOR_EACH_PTR(bb->insns, insn) { struct instruction *phi; struct storage *last; if (!insn->bb || insn->opcode != OP_PHISOURCE) continue; last = NULL; FOR_EACH_PTR(insn->phi_users, phi) { struct storage *storage = lookup_storage(bb, phi->target, STOR_OUT); if (!storage) { DELETE_CURRENT_PTR(phi); continue; } if (last && storage != last) storage = combine_storage(storage, last); last = storage; } END_FOR_EACH_PTR(phi); PACK_PTR_LIST(&insn->phi_users); } END_FOR_EACH_PTR(insn); } void set_up_storage(struct entrypoint *ep) { struct basic_block *bb; /* First set up storage for the incoming arguments */ set_up_argument_storage(ep, ep->entry->bb); /* Then do a list of all the inter-bb storage */ FOR_EACH_PTR(ep->bbs, bb) { set_up_bb_storage(bb); combine_phi_storage(bb); } END_FOR_EACH_PTR(bb); name_storage(); } sparse-0.6.1/storage.h000066400000000000000000000033251355073133200146350ustar00rootroot00000000000000#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.1/symbol.c000066400000000000000000000607331355073133200144770ustar00rootroot00000000000000/* * Symbol lookup and handling. * * Copyright (C) 2003 Transmeta Corp. * 2003-2004 Linus Torvalds * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include #include "lib.h" #include "allocate.h" #include "token.h" #include "parse.h" #include "symbol.h" #include "scope.h" #include "expression.h" #include "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; }; /* * Unions are fairly easy to lay out ;) */ static void lay_out_union(struct symbol *sym, struct struct_union_info *info) { examine_symbol_type(sym); // Unnamed bitfields do not affect alignment. if (sym->ident || !is_bitfield_type(sym)) { if (sym->ctype.alignment > info->max_align) info->max_align = sym->ctype.alignment; } if (sym->bit_size > info->bit_size) info->bit_size = sym->bit_size; sym->offset = 0; } static int bitfield_base_size(struct symbol *sym) { if (sym->type == SYM_NODE) sym = sym->ctype.base_type; if (sym->type == SYM_BITFIELD) sym = sym->ctype.base_type; return sym->bit_size; } /* * Structures are a bit more interesting to lay out */ static void lay_out_struct(struct symbol *sym, struct struct_union_info *info) { unsigned long bit_size, align_bit_mask; int base_size; examine_symbol_type(sym); // Unnamed bitfields do not affect alignment. if (sym->ident || !is_bitfield_type(sym)) { if (sym->ctype.alignment > info->max_align) info->max_align = sym->ctype.alignment; } bit_size = info->bit_size; base_size = sym->bit_size; /* * Unsized arrays cause us to not align the resulting * structure size */ if (base_size < 0) { info->align_size = 0; base_size = 0; } align_bit_mask = bytes_to_bits(sym->ctype.alignment) - 1; /* * Bitfields have some very special rules.. */ if (is_bitfield_type (sym)) { unsigned long bit_offset = bit_size & align_bit_mask; int room = bitfield_base_size(sym) - bit_offset; // Zero-width fields just fill up the unit. int width = base_size ? : (bit_offset ? room : 0); if (width > room) { bit_size = (bit_size + align_bit_mask) & ~align_bit_mask; bit_offset = 0; } sym->offset = bits_to_bytes(bit_size - bit_offset); sym->bit_offset = bit_offset; sym->ctype.base_type->bit_offset = bit_offset; info->bit_size = bit_size + width; // warning (sym->pos, "bitfield: offset=%d:%d size=:%d", sym->offset, sym->bit_offset, width); return; } /* * Otherwise, just align it right and add it up.. */ bit_size = (bit_size + align_bit_mask) & ~align_bit_mask; sym->offset = bits_to_bytes(bit_size); info->bit_size = bit_size + base_size; // warning (sym->pos, "regular: offset=%d", sym->offset); } static struct symbol * examine_struct_union_type(struct symbol *sym, int advance) { struct struct_union_info info = { .max_align = 1, .bit_size = 0, .align_size = 1 }; unsigned long bit_size, bit_align; void (*fn)(struct symbol *, struct struct_union_info *); struct symbol *member; fn = advance ? lay_out_struct : lay_out_union; FOR_EACH_PTR(sym->symbol_list, member) { fn(member, &info); } END_FOR_EACH_PTR(member); if (!sym->ctype.alignment) sym->ctype.alignment = info.max_align; bit_size = info.bit_size; if (info.align_size) { bit_align = bytes_to_bits(sym->ctype.alignment)-1; bit_size = (bit_size + bit_align) & ~bit_align; } sym->bit_size = bit_size; return sym; } static struct symbol *examine_base_type(struct symbol *sym) { struct symbol *base_type; /* Check the base type */ base_type = examine_symbol_type(sym->ctype.base_type); if (!base_type || base_type->type == SYM_PTR) return base_type; 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; } return base_type; } static struct symbol * examine_array_type(struct symbol *sym) { struct symbol *base_type = examine_base_type(sym); unsigned long bit_size = -1, alignment; struct expression *array_size = sym->array_size; if (!base_type) return sym; if (array_size) { bit_size = array_element_offset(base_type->bit_size, get_expression_value_silent(array_size)); if (array_size->type != EXPR_VALUE) { if (Wvla) warning(array_size->pos, "Variable length array is used."); bit_size = -1; } } alignment = base_type->ctype.alignment; if (!sym->ctype.alignment) sym->ctype.alignment = alignment; sym->bit_size = bit_size; return sym; } static struct symbol *examine_bitfield_type(struct symbol *sym) { struct symbol *base_type = examine_base_type(sym); unsigned long bit_size, alignment, modifiers; if (!base_type) return sym; bit_size = base_type->bit_size; if (sym->bit_size > bit_size) warning(sym->pos, "impossible field-width, %d, for this type", sym->bit_size); alignment = base_type->ctype.alignment; if (!sym->ctype.alignment) sym->ctype.alignment = alignment; modifiers = base_type->ctype.modifiers; /* Bitfields are unsigned, unless the base type was explicitly signed */ if (!(modifiers & MOD_EXPLICITLY_SIGNED)) modifiers = (modifiers & ~MOD_SIGNED) | MOD_UNSIGNED; sym->ctype.modifiers |= modifiers & MOD_SIGNEDNESS; return sym; } /* * "typeof" will have to merge the types together */ void merge_type(struct symbol *sym, struct symbol *base_type) { 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 int count_array_initializer(struct symbol *t, struct expression *expr) { int nr = 0; int is_char = 0; /* * Arrays of character types are special; they can be initialized by * string literal _or_ by string literal in braces. The latter means * that with T x[] = {} number of elements in x depends * on T - if it's a character type, we get the length of string literal * (including NUL), otherwise we have one element here. */ if (t->ctype.base_type == &int_type && t->ctype.modifiers & MOD_CHAR) is_char = 1; switch (expr->type) { case EXPR_INITIALIZER: { struct expression *entry; int count = 0; int str_len = 0; FOR_EACH_PTR(expr->expr_list, entry) { count++; switch (entry->type) { case EXPR_INDEX: if (entry->idx_to >= nr) nr = entry->idx_to+1; break; case EXPR_PREOP: { struct expression *e = entry; if (is_char) { while (e && e->type == EXPR_PREOP && e->op == '(') e = e->unop; if (e && e->type == EXPR_STRING) { entry = e; case EXPR_STRING: if (is_char) str_len = entry->string->length; } } } default: nr++; } } END_FOR_EACH_PTR(entry); if (count == 1 && str_len) nr = str_len; break; } case EXPR_PREOP: if (is_char) { struct expression *e = expr; while (e && e->type == EXPR_PREOP && e->op == '(') e = e->unop; if (e && e->type == EXPR_STRING) { expr = e; case EXPR_STRING: if (is_char) nr = expr->string->length; } } break; default: break; } return nr; } static struct expression *get_symbol_initializer(struct symbol *sym) { do { if (sym->initializer) return sym->initializer; } while ((sym = sym->same_symbol) != NULL); return NULL; } static 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; return sym; } static struct symbol *examine_enum_type(struct symbol *sym) { struct symbol *base_type = examine_base_type(sym); sym->ctype.modifiers |= (base_type->ctype.modifiers & MOD_SIGNEDNESS); sym->bit_size = bits_in_enum; if (base_type->bit_size > sym->bit_size) sym->bit_size = base_type->bit_size; sym->ctype.alignment = enum_alignment; if (base_type->ctype.alignment > sym->ctype.alignment) sym->ctype.alignment = base_type->ctype.alignment; return sym; } static struct symbol *examine_pointer_type(struct symbol *sym) { /* * We need to set the pointer size first, and * examine the thing we point to only afterwards. * That's because this pointer type may end up * being needed for the base type size evaluation. */ if (!sym->bit_size) sym->bit_size = bits_in_pointer; if (!sym->ctype.alignment) sym->ctype.alignment = pointer_alignment; return sym; } /* * Fill in type size and alignment information for * regular SYM_TYPE things. */ struct symbol *examine_symbol_type(struct symbol * sym) { if (!sym) return sym; /* Already done? */ if (sym->examined) return sym; sym->examined = 1; switch (sym->type) { case SYM_FN: case SYM_NODE: return examine_node_type(sym); case SYM_ARRAY: return examine_array_type(sym); case SYM_STRUCT: return examine_struct_union_type(sym, 1); case SYM_UNION: return examine_struct_union_type(sym, 0); case SYM_PTR: return examine_pointer_type(sym); case SYM_ENUM: return examine_enum_type(sym); case SYM_BITFIELD: return examine_bitfield_type(sym); case SYM_BASETYPE: /* Size and alignment had better already be set up */ return sym; case SYM_TYPEOF: { struct symbol *base = evaluate_expression(sym->initializer); if (base) { unsigned long mod = 0; if (is_bitfield_type(base)) warning(base->pos, "typeof applied to bitfield type"); if (base->type == SYM_NODE) { mod |= base->ctype.modifiers & MOD_TYPEOF; base = base->ctype.base_type; } sym->type = SYM_NODE; sym->ctype.modifiers = mod; sym->ctype.base_type = base; return examine_node_type(sym); } sym->type = SYM_NODE; sym->ctype.base_type = &bad_ctype; return 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_TYPEDEF] = "typedef", [SYM_TYPEOF] = "typeof", [SYM_MEMBER] = "member", [SYM_BITFIELD] = "bitfield", [SYM_LABEL] = "label", [SYM_RESTRICT] = "restrict", [SYM_FOULED] = "fouled", [SYM_KEYWORD] = "keyword", [SYM_BAD] = "bad"}; if (type <= SYM_BAD) return type_lookup[type]; else return NULL; } struct symbol *examine_pointer_target(struct symbol *sym) { return examine_base_type(sym); } static struct symbol_list *restr, *fouled; void create_fouled(struct symbol *type) { if (type->bit_size < bits_in_int) { struct symbol *new = alloc_symbol(type->pos, type->type); *new = *type; new->bit_size = bits_in_int; new->type = SYM_FOULED; new->ctype.base_type = type; add_symbol(&restr, type); add_symbol(&fouled, new); } } struct symbol *befoul(struct symbol *type) { struct symbol *t1, *t2; while (type->type == SYM_NODE) type = type->ctype.base_type; PREPARE_PTR_LIST(restr, t1); PREPARE_PTR_LIST(fouled, t2); for (;;) { if (t1 == type) return t2; if (!t1) break; NEXT_PTR_LIST(t1); NEXT_PTR_LIST(t2); } FINISH_PTR_LIST(t2); FINISH_PTR_LIST(t1); return NULL; } void check_declaration(struct symbol *sym) { int warned = 0; struct symbol *next = sym; while ((next = next->next_id) != NULL) { if (next->namespace != sym->namespace) continue; if (sym->scope == next->scope) { sym->same_symbol = next; return; } /* Extern in block level matches a TOPLEVEL non-static symbol */ if (sym->ctype.modifiers & MOD_EXTERN) { if ((next->ctype.modifiers & (MOD_TOPLEVEL|MOD_STATIC)) == MOD_TOPLEVEL) { sym->same_symbol = next; return; } } if (!Wshadow || warned) continue; if (get_sym_type(next) == SYM_FN) continue; warned = 1; warning(sym->pos, "symbol '%s' shadows an earlier one", show_ident(sym->ident)); info(next->pos, "originally declared here"); } } void bind_symbol(struct symbol *sym, struct ident *ident, enum namespace ns) { struct scope *scope; if (sym->bound) { sparse_error(sym->pos, "internal error: symbol type already bound"); return; } if (ident->reserved && (ns & (NS_TYPEDEF | NS_STRUCT | NS_LABEL | NS_SYMBOL))) { sparse_error(sym->pos, "Trying to use reserved word '%s' as identifier", show_ident(ident)); return; } sym->namespace = ns; sym->next_id = ident->symbols; ident->symbols = sym; if (sym->ident && sym->ident != ident) warning(sym->pos, "Symbol '%s' already bound", show_ident(sym->ident)); sym->ident = ident; sym->bound = 1; scope = block_scope; if (ns == NS_SYMBOL && toplevel(scope)) { unsigned mod = MOD_ADDRESSABLE | MOD_TOPLEVEL; scope = global_scope; if (sym->ctype.modifiers & MOD_STATIC || is_extern_inline(sym)) { scope = file_scope; mod = MOD_TOPLEVEL; } sym->ctype.modifiers |= mod; } if (ns == NS_MACRO) scope = file_scope; if (ns == NS_LABEL) scope = function_scope; bind_scope(sym, scope); } struct symbol *create_symbol(int stream, const char *name, int type, int namespace) { struct ident *ident = built_in_ident(name); struct symbol *sym = lookup_symbol(ident, namespace); if (sym && sym->type != type) die("symbol %s created with different types: %d old %d", name, type, sym->type); if (!sym) { struct token *token = built_in_token(stream, ident); sym = alloc_symbol(token->pos, type); bind_symbol(sym, token->ident, namespace); } return sym; } /* * Abstract types */ struct symbol int_type, fp_type; /* * C types (i.e. actual instances that the abstract types * can map onto) */ struct symbol bool_ctype, void_ctype, type_ctype, char_ctype, schar_ctype, uchar_ctype, short_ctype, sshort_ctype, ushort_ctype, int_ctype, sint_ctype, uint_ctype, long_ctype, slong_ctype, ulong_ctype, llong_ctype, sllong_ctype, ullong_ctype, lllong_ctype, slllong_ctype, ulllong_ctype, float_ctype, double_ctype, ldouble_ctype, string_ctype, ptr_ctype, lazy_ptr_ctype, incomplete_ctype, label_ctype, bad_ctype, null_ctype; struct symbol int_ptr_ctype, uint_ptr_ctype; struct symbol long_ptr_ctype, ulong_ptr_ctype; struct symbol llong_ptr_ctype, ullong_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 zero_int; #define __INIT_IDENT(str, res) { .len = sizeof(str)-1, .name = str, .reserved = res } #define __IDENT(n,str,res) \ struct ident n = __INIT_IDENT(str,res) #include "ident-list.h" void init_symbols(void) { int stream = init_stream("builtin", -1, includepath); #define __IDENT(n,str,res) \ hash_ident(&n) #include "ident-list.h" init_parser(stream); init_builtins(stream); } // For fix-sized types static int bits_in_type32 = 32; static int bits_in_type64 = 64; static int bits_in_type128 = 128; #define MOD_ESIGNED (MOD_SIGNED | MOD_EXPLICITLY_SIGNED) #define MOD_LL (MOD_LONG | MOD_LONGLONG) #define MOD_LLL MOD_LONGLONGLONG static const struct ctype_declare { struct symbol *ptr; enum type type; unsigned long modifiers; int *bit_size; int *maxalign; struct symbol *base_type; } ctype_declaration[] = { { &bool_ctype, SYM_BASETYPE, MOD_UNSIGNED, &bits_in_bool, &max_int_alignment, &int_type }, { &void_ctype, SYM_BASETYPE, 0, NULL, NULL, NULL }, { &type_ctype, SYM_BASETYPE, MOD_TYPE, NULL, NULL, NULL }, { &incomplete_ctype,SYM_BASETYPE, 0, NULL, NULL, NULL }, { &bad_ctype, SYM_BASETYPE, 0, NULL, NULL, NULL }, { &char_ctype, SYM_BASETYPE, MOD_SIGNED | MOD_CHAR, &bits_in_char, &max_int_alignment, &int_type }, { &schar_ctype, SYM_BASETYPE, MOD_ESIGNED | MOD_CHAR, &bits_in_char, &max_int_alignment, &int_type }, { &uchar_ctype, SYM_BASETYPE, MOD_UNSIGNED | MOD_CHAR, &bits_in_char, &max_int_alignment, &int_type }, { &short_ctype, SYM_BASETYPE, MOD_SIGNED | MOD_SHORT, &bits_in_short, &max_int_alignment, &int_type }, { &sshort_ctype, SYM_BASETYPE, MOD_ESIGNED | MOD_SHORT, &bits_in_short, &max_int_alignment, &int_type }, { &ushort_ctype, SYM_BASETYPE, MOD_UNSIGNED | MOD_SHORT, &bits_in_short, &max_int_alignment, &int_type }, { &int_ctype, SYM_BASETYPE, MOD_SIGNED, &bits_in_int, &max_int_alignment, &int_type }, { &sint_ctype, SYM_BASETYPE, MOD_ESIGNED, &bits_in_int, &max_int_alignment, &int_type }, { &uint_ctype, SYM_BASETYPE, MOD_UNSIGNED, &bits_in_int, &max_int_alignment, &int_type }, { &long_ctype, SYM_BASETYPE, MOD_SIGNED | MOD_LONG, &bits_in_long, &max_int_alignment, &int_type }, { &slong_ctype, SYM_BASETYPE, MOD_ESIGNED | MOD_LONG, &bits_in_long, &max_int_alignment, &int_type }, { &ulong_ctype, SYM_BASETYPE, MOD_UNSIGNED | MOD_LONG, &bits_in_long, &max_int_alignment, &int_type }, { &llong_ctype, SYM_BASETYPE, MOD_SIGNED | MOD_LL, &bits_in_longlong, &max_int_alignment, &int_type }, { &sllong_ctype, SYM_BASETYPE, MOD_ESIGNED | MOD_LL, &bits_in_longlong, &max_int_alignment, &int_type }, { &ullong_ctype, SYM_BASETYPE, MOD_UNSIGNED | MOD_LL, &bits_in_longlong, &max_int_alignment, &int_type }, { &lllong_ctype, SYM_BASETYPE, MOD_SIGNED | MOD_LLL, &bits_in_longlonglong, &max_int_alignment, &int_type }, { &slllong_ctype, SYM_BASETYPE, MOD_ESIGNED | MOD_LLL, &bits_in_longlonglong, &max_int_alignment, &int_type }, { &ulllong_ctype, SYM_BASETYPE, MOD_UNSIGNED | MOD_LLL, &bits_in_longlonglong, &max_int_alignment, &int_type }, { &float_ctype, SYM_BASETYPE, 0, &bits_in_float, &max_fp_alignment, &fp_type }, { &double_ctype, SYM_BASETYPE, MOD_LONG, &bits_in_double, &max_fp_alignment, &fp_type }, { &ldouble_ctype, SYM_BASETYPE, MOD_LONG | MOD_LONGLONG, &bits_in_longdouble, &max_fp_alignment, &fp_type }, { &float32_ctype, SYM_BASETYPE, 0, &bits_in_type32, &max_fp_alignment, &fp_type }, { &float32x_ctype, SYM_BASETYPE, MOD_LONG, &bits_in_double, &max_fp_alignment, &fp_type }, { &float64_ctype, SYM_BASETYPE, 0, &bits_in_type64, &max_fp_alignment, &fp_type }, { &float64x_ctype, SYM_BASETYPE, MOD_LONG | MOD_LONGLONG, &bits_in_longdouble, &max_fp_alignment, &fp_type }, { &float128_ctype, SYM_BASETYPE, 0, &bits_in_type128, &max_alignment, &fp_type }, { &string_ctype, SYM_PTR, 0, &bits_in_pointer, &pointer_alignment, &char_ctype }, { &ptr_ctype, SYM_PTR, 0, &bits_in_pointer, &pointer_alignment, &void_ctype }, { &null_ctype, SYM_PTR, 0, &bits_in_pointer, &pointer_alignment, &void_ctype }, { &label_ctype, SYM_PTR, 0, &bits_in_pointer, &pointer_alignment, &void_ctype }, { &lazy_ptr_ctype, SYM_PTR, 0, &bits_in_pointer, &pointer_alignment, &void_ctype }, { &int_ptr_ctype, SYM_PTR, 0, &bits_in_pointer, &pointer_alignment, &int_ctype }, { &uint_ptr_ctype, SYM_PTR, 0, &bits_in_pointer, &pointer_alignment, &uint_ctype }, { &long_ptr_ctype, SYM_PTR, 0, &bits_in_pointer, &pointer_alignment, &long_ctype }, { &ulong_ptr_ctype, SYM_PTR, 0, &bits_in_pointer, &pointer_alignment, &ulong_ctype }, { &llong_ptr_ctype, SYM_PTR, 0, &bits_in_pointer, &pointer_alignment, &llong_ctype }, { &ullong_ptr_ctype,SYM_PTR, 0, &bits_in_pointer, &pointer_alignment, &ullong_ctype }, { &const_void_ctype, SYM_NODE, MOD_CONST, NULL, NULL, &void_ctype }, { &const_char_ctype, SYM_NODE, MOD_CONST, &bits_in_char, &max_int_alignment, &char_ctype }, { &const_ptr_ctype, SYM_PTR, 0, &bits_in_pointer, &pointer_alignment, &const_void_ctype }, { &const_string_ctype,SYM_PTR, 0, &bits_in_pointer, &pointer_alignment, &const_char_ctype }, { NULL, } }; #undef MOD_LLL #undef MOD_LL #undef MOD_ESIGNED void init_ctype(void) { const struct ctype_declare *ctype; for (ctype = ctype_declaration ; ctype->ptr; ctype++) { struct symbol *sym = ctype->ptr; unsigned long bit_size = ctype->bit_size ? *ctype->bit_size : -1; unsigned long maxalign = ctype->maxalign ? *ctype->maxalign : 0; unsigned long alignment = bits_to_bytes(bit_size); if (alignment > maxalign) alignment = maxalign; sym->type = ctype->type; sym->bit_size = bit_size; sym->ctype.alignment = alignment; sym->ctype.base_type = ctype->base_type; sym->ctype.modifiers = ctype->modifiers; } // and now some adjustments if (funsigned_char) { char_ctype.ctype.modifiers |= MOD_UNSIGNED; char_ctype.ctype.modifiers &= ~MOD_SIGNED; } } sparse-0.6.1/symbol.h000066400000000000000000000347101355073133200145000ustar00rootroot00000000000000#ifndef SYMBOL_H #define SYMBOL_H /* * Basic symbol and namespace definitions. * * Copyright (C) 2003 Transmeta Corp. * 2003 Linus Torvalds * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include "token.h" #include "target.h" /* * An identifier with semantic meaning is a "symbol". * * There's a 1:n relationship: each symbol is always * associated with one identifier, while each identifier * can have one or more semantic meanings due to C scope * rules. * * The progression is symbol -> token -> identifier. The * token contains the information on where the symbol was * declared. */ enum namespace { NS_NONE = 0, NS_MACRO = 1, NS_TYPEDEF = 2, NS_STRUCT = 4, // Also used for unions and enums. NS_LABEL = 8, NS_SYMBOL = 16, NS_ITERATOR = 32, NS_PREPROCESSOR = 64, NS_UNDEF = 128, NS_KEYWORD = 256, }; enum type { SYM_UNINITIALIZED, SYM_PREPROCESSOR, SYM_BASETYPE, SYM_NODE, SYM_PTR, SYM_FN, SYM_ARRAY, SYM_STRUCT, SYM_UNION, SYM_ENUM, SYM_TYPEDEF, SYM_TYPEOF, SYM_MEMBER, SYM_BITFIELD, SYM_LABEL, SYM_RESTRICT, SYM_FOULED, SYM_KEYWORD, SYM_BAD, }; enum keyword { KW_SPECIFIER = 1 << 0, KW_MODIFIER = 1 << 1, KW_QUALIFIER = 1 << 2, KW_ATTRIBUTE = 1 << 3, KW_STATEMENT = 1 << 4, KW_ASM = 1 << 5, KW_MODE = 1 << 6, KW_SHORT = 1 << 7, KW_LONG = 1 << 8, KW_EXACT = 1 << 9, }; struct context { struct expression *context; unsigned int in, out; }; extern struct context *alloc_context(void); DECLARE_PTR_LIST(context_list, struct context); struct ctype { unsigned long modifiers; unsigned long alignment; struct context_list *contexts; struct ident *as; struct symbol *base_type; }; struct decl_state { struct ctype ctype; struct ident **ident; struct symbol_op *mode; unsigned char prefer_abstract, is_inline, storage_class, is_tls; unsigned char is_ext_visible; }; struct symbol_op { enum keyword type; int (*evaluate)(struct expression *); int (*expand)(struct expression *, int); int (*args)(struct expression *); /* keywords */ struct token *(*declarator)(struct token *token, struct decl_state *ctx); struct token *(*statement)(struct token *token, struct statement *stmt); struct token *(*toplevel)(struct token *token, struct symbol_list **list); struct token *(*attribute)(struct token *token, struct symbol *attr, struct decl_state *ctx); struct symbol *(*to_mode)(struct symbol *); void (*asm_modifier)(struct token *token, unsigned long *mods); 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 (*expander)(struct token *); }; struct /* NS_PREPROCESSOR */ { int (*handler)(struct stream *, struct token **, struct token *); int normal; }; struct /* NS_SYMBOL */ { unsigned long offset; int bit_size; unsigned int bit_offset:8, variadic:1, initialized:1, examined:1, expanding:1, evaluated:1, string:1, designated_init:1, forced_arg:1, accessed:1, builtin:1, torename:1, transparent_union:1; struct expression *array_size; struct ctype ctype; struct symbol_list *arguments; struct statement *stmt; struct symbol_list *symbol_list; struct statement *inline_stmt; struct symbol_list *inline_symbol_list; struct expression *initializer; struct entrypoint *ep; struct symbol *definition; }; }; union /* backend */ { struct basic_block *bb_target; /* label */ void *aux; /* Auxiliary info, e.g. backend information */ struct { /* sparse ctags */ char kind; unsigned char visited:1; }; }; pseudo_t pseudo; }; /* Modifiers */ #define MOD_AUTO 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_TYPE 0x00010000 #define MOD_USERTYPE 0x00020000 #define MOD_CHAR 0x00040000 #define MOD_SHORT 0x00080000 #define MOD_LONG 0x00100000 #define MOD_LONGLONG 0x00200000 #define MOD_LONGLONGLONG 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_SIGNEDNESS (MOD_SIGNED | MOD_UNSIGNED | MOD_EXPLICITLY_SIGNED) #define MOD_LONG_ALL (MOD_LONG | MOD_LONGLONG | MOD_LONGLONGLONG) #define MOD_SPECIFIER (MOD_CHAR | MOD_SHORT | MOD_LONG_ALL | MOD_SIGNEDNESS) #define MOD_SIZE (MOD_CHAR | MOD_SHORT | MOD_LONG_ALL) #define MOD_IGNORE (MOD_STORAGE | MOD_ACCESS | MOD_USERTYPE | MOD_EXPLICITLY_SIGNED | MOD_EXT_VISIBLE) #define MOD_QUALIFIER (MOD_CONST | MOD_VOLATILE | MOD_RESTRICT | MOD_ATOMIC) #define MOD_PTRINHERIT (MOD_QUALIFIER | MOD_NODEREF | MOD_NORETURN | MOD_NOCAST) /* modifiers preserved by typeof() operator */ #define MOD_TYPEOF (MOD_QUALIFIER | MOD_NOCAST | MOD_SPECIFIER) /* Current parsing/evaluation function */ extern struct symbol *current_fn; /* Abstract types */ extern struct symbol int_type, fp_type; /* C types */ extern struct symbol bool_ctype, void_ctype, type_ctype, char_ctype, schar_ctype, uchar_ctype, short_ctype, sshort_ctype, ushort_ctype, int_ctype, sint_ctype, uint_ctype, long_ctype, slong_ctype, ulong_ctype, llong_ctype, sllong_ctype, ullong_ctype, lllong_ctype, slllong_ctype, ulllong_ctype, float_ctype, double_ctype, ldouble_ctype, string_ctype, ptr_ctype, lazy_ptr_ctype, incomplete_ctype, label_ctype, bad_ctype, null_ctype; 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 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; #define uintptr_ctype size_t_ctype #define intptr_ctype ssize_t_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 declare_builtins(void); extern void init_ctype(void); extern void init_target(void); extern struct symbol *alloc_symbol(struct position, int type); extern void show_type(struct symbol *); extern const char *modifier_string(unsigned long mod); extern void show_symbol(struct symbol *); extern int show_symbol_expr_init(struct symbol *sym); extern void show_type_list(struct symbol *); extern void show_symbol_list(struct symbol_list *, const char *); extern void add_symbol(struct symbol_list **, struct symbol *); extern void bind_symbol(struct symbol *, struct ident *, enum namespace); extern struct symbol *examine_symbol_type(struct symbol *); extern struct symbol *examine_pointer_target(struct symbol *); extern 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->ctype.modifiers & MOD_TYPE) != 0; } static inline int is_ptr_type(struct symbol *type) { if (type->type == SYM_NODE) type = type->ctype.base_type; return type->type == SYM_PTR || type->type == SYM_ARRAY || type->type == SYM_FN; } static inline int is_func_type(struct symbol *type) { if (type->type == SYM_NODE) type = type->ctype.base_type; return type->type == SYM_FN; } static inline int is_array_type(struct symbol *type) { if (type->type == SYM_NODE) type = type->ctype.base_type; return type->type == SYM_ARRAY; } static inline int is_float_type(struct symbol *type) { if (type->type == SYM_NODE) type = type->ctype.base_type; return type->ctype.base_type == &fp_type; } static inline int is_byte_type(struct symbol *type) { return type->bit_size == bits_in_char && type->type != SYM_BITFIELD; } static inline int is_void_type(struct symbol *type) { if (type->type == SYM_NODE) type = type->ctype.base_type; return type == &void_ctype; } static inline int is_bool_type(struct symbol *type) { if (type->type == SYM_NODE) type = type->ctype.base_type; return type == &bool_ctype; } static inline int is_scalar_type(struct symbol *type) { if (type->type == SYM_NODE) type = type->ctype.base_type; switch (type->type) { case SYM_ENUM: case SYM_BITFIELD: case SYM_PTR: case SYM_RESTRICT: // OK, always integer types return 1; default: break; } if (type->ctype.base_type == &int_type) return 1; if (type->ctype.base_type == &fp_type) return 1; return 0; } /// 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 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 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.1/target.c000066400000000000000000000061511355073133200144520ustar00rootroot00000000000000#include #include "symbol.h" #include "target.h" #include "machine.h" struct symbol *size_t_ctype = &uint_ctype; struct symbol *ssize_t_ctype = &int_ctype; struct symbol *intmax_ctype = &llong_ctype; struct symbol *uintmax_ctype = &ullong_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; /* * For "__attribute__((aligned))" */ int max_alignment = 16; /* * Integer data types */ int bits_in_bool = 1; int bits_in_char = 8; int bits_in_short = 16; int bits_in_int = 32; int bits_in_long = 32; int bits_in_longlong = 64; int bits_in_longlonglong = 128; int max_int_alignment = 4; /* * Floating point data types */ int bits_in_float = 32; int bits_in_double = 64; int bits_in_longdouble = 128; int max_fp_alignment = 16; /* * Pointer data type */ int bits_in_pointer = 32; int pointer_alignment = 4; /* * Enum data types */ int bits_in_enum = 32; int enum_alignment = 4; void init_target(void) { switch (arch_mach) { case MACH_X86_64: if (arch_m64 == ARCH_LP64) break; /* fall through */ case MACH_I386: case MACH_M68K: case MACH_SPARC32: case MACH_PPC32: wchar_ctype = &long_ctype; break; case MACH_ARM: case MACH_ARM64: wchar_ctype = &uint_ctype; break; default: break; } switch (arch_mach) { case MACH_MIPS64: if (arch_m64 == ARCH_LP64) break; /* fall through */ case MACH_M68K: case MACH_SPARC32: case MACH_PPC32: case MACH_MIPS32: case MACH_RISCV32: arch_m64 = ARCH_LP32; int32_ctype = &long_ctype; uint32_ctype = &ulong_ctype; break; default: break; } switch (arch_mach) { case MACH_ARM: case MACH_MIPS32: case MACH_S390X: case MACH_SPARC32: bits_in_longdouble = 64; max_fp_alignment = 8; break; case MACH_X86_64: if (arch_m64 == ARCH_LP64 || arch_m64 == ARCH_X32) break; /* fall through */ case MACH_I386: case MACH_M68K: bits_in_longdouble = 96; max_fp_alignment = 4; break; default: break; } switch (arch_m64) { case ARCH_X32: max_int_alignment = 8; int64_ctype = &llong_ctype; uint64_ctype = &ullong_ctype; break; case ARCH_LP32: /* default values */ int64_ctype = &llong_ctype; uint64_ctype = &ullong_ctype; intmax_ctype = &llong_ctype; uintmax_ctype = &ullong_ctype; break; case ARCH_LP64: bits_in_long = 64; max_int_alignment = 8; size_t_ctype = &ulong_ctype; ssize_t_ctype = &long_ctype; intmax_ctype = &long_ctype; uintmax_ctype = &ulong_ctype; goto case_64bit_common; case ARCH_LLP64: bits_in_long = 32; max_int_alignment = 8; size_t_ctype = &ullong_ctype; ssize_t_ctype = &llong_ctype; int64_ctype = &llong_ctype; uint64_ctype = &ullong_ctype; goto case_64bit_common; case_64bit_common: bits_in_pointer = 64; pointer_alignment = 8; break; } #if defined(__CYGWIN__) wchar_ctype = &ushort_ctype; #endif #if defined(__FreeBSD__) || defined(__APPLE__) wint_ctype = &int_ctype; #endif #if defined(__APPLE__) int64_ctype = &llong_ctype; uint64_ctype = &ullong_ctype; #endif } sparse-0.6.1/target.h000066400000000000000000000027441355073133200144630ustar00rootroot00000000000000#ifndef TARGET_H #define TARGET_H extern struct symbol *size_t_ctype; extern struct symbol *ssize_t_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; /* * For "__attribute__((aligned))" */ extern int max_alignment; /* * Integer data types */ extern int bits_in_bool; extern int bits_in_char; extern int bits_in_short; extern int bits_in_int; extern int bits_in_long; extern int bits_in_longlong; extern int bits_in_longlonglong; extern int max_int_alignment; /* * Floating point data types */ extern int bits_in_float; extern int bits_in_double; extern int bits_in_longdouble; extern int max_fp_alignment; /* * Pointer data type */ extern int bits_in_pointer; extern int pointer_alignment; /* * Enum data types */ extern int bits_in_enum; extern int enum_alignment; /* * Helper functions for converting bits to bytes and vice versa. */ static inline int bits_to_bytes(int bits) { return bits >= 0 ? (bits + bits_in_char - 1) / 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.1/test-dissect.c000066400000000000000000000041151355073133200155750ustar00rootroot00000000000000#include "dissect.h" static unsigned dotc_stream; static inline char storage(struct symbol *sym) { int t = sym->type; unsigned m = sym->ctype.modifiers; if (m & MOD_INLINE || t == SYM_STRUCT || t == SYM_UNION /*|| t == SYM_ENUM*/) return sym->pos.stream == dotc_stream ? 's' : 'g'; return (m & MOD_STATIC) ? 's' : (m & MOD_NONLOCAL) ? 'g' : 'l'; } static inline const char *show_mode(unsigned mode) { static char str[3]; if (mode == -1) return "def"; #define U(u_r) "-rwm"[(mode / u_r) & 3] str[0] = U(U_R_AOF); str[1] = U(U_R_VAL); str[2] = U(U_R_PTR); #undef U return str; } static void print_usage(struct position *pos, struct symbol *sym, unsigned mode) { static unsigned curr_stream = -1; if (curr_stream != pos->stream) { curr_stream = pos->stream; printf("\nFILE: %s\n\n", stream_name(curr_stream)); } printf("%4d:%-3d %c %-5.3s", pos->line, pos->pos, storage(sym), show_mode(mode)); } static void r_symbol(unsigned mode, struct position *pos, struct symbol *sym) { print_usage(pos, sym, mode); if (!sym->ident) sym->ident = built_in_ident("__asm__"); printf("%-32.*s %s\n", sym->ident->len, sym->ident->name, show_typename(sym->ctype.base_type)); } static void r_member(unsigned mode, struct position *pos, struct symbol *sym, struct symbol *mem) { struct ident *ni, *si, *mi; print_usage(pos, sym, mode); ni = built_in_ident("?"); si = sym->ident ?: ni; /* mem == NULL means entire struct accessed */ mi = mem ? (mem->ident ?: ni) : built_in_ident("*"); printf("%.*s.%-*.*s %s\n", si->len, si->name, 32-1 - si->len, mi->len, mi->name, show_typename(mem ? mem->ctype.base_type : sym)); } static void r_symdef(struct symbol *sym) { r_symbol(-1, &sym->pos, sym); } int main(int argc, char **argv) { static struct reporter reporter = { .r_symdef = r_symdef, .r_symbol = r_symbol, .r_member = r_member, }; struct string_list *filelist = NULL; char *file; sparse_initialize(argc, argv, &filelist); FOR_EACH_PTR(filelist, file) { dotc_stream = input_stream_nr; dissect(__sparse(file), &reporter); } END_FOR_EACH_PTR(file); return 0; } sparse-0.6.1/test-inspect.c000066400000000000000000000015261355073133200156070ustar00rootroot00000000000000 #include #include #include #include #include #include #include #include "lib.h" #include "allocate.h" #include "token.h" #include "parse.h" #include "symbol.h" #include "expression.h" #include "ast-view.h" static void expand_symbols(struct symbol_list *list) { struct symbol *sym; FOR_EACH_PTR(list, sym) { expand_symbol(sym); } END_FOR_EACH_PTR(sym); } int main(int argc, char **argv) { struct string_list *filelist = NULL; char *file; struct symbol_list *view_syms = NULL; gtk_init(&argc,&argv); expand_symbols(sparse_initialize(argc, argv, &filelist)); FOR_EACH_PTR(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.1/test-lexing.c000066400000000000000000000032641355073133200154310ustar00rootroot00000000000000/* * 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.1/test-linearize.c000066400000000000000000000040031355073133200161150ustar00rootroot00000000000000/* * 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.1/test-parsing.c000066400000000000000000000046061355073133200156070ustar00rootroot00000000000000/* * Example trivial client program that uses the sparse library * to tokenize, preprocess and parse a C file, and prints out * the results. * * Copyright (C) 2003 Transmeta Corp. * 2003 Linus Torvalds * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include #include #include #include #include #include "lib.h" #include "allocate.h" #include "token.h" #include "parse.h" #include "symbol.h" #include "expression.h" static void clean_up_symbols(struct symbol_list *list) { struct symbol *sym; FOR_EACH_PTR(list, sym) { expand_symbol(sym); } END_FOR_EACH_PTR(sym); } int main(int argc, char **argv) { struct symbol_list * list; struct string_list * filelist = NULL; char *file; list = sparse_initialize(argc, argv, &filelist); // Simplification clean_up_symbols(list); #if 1 show_symbol_list(list, "\n\n"); printf("\n\n"); #endif FOR_EACH_PTR(filelist, file) { list = sparse(file); // Simplification clean_up_symbols(list); #if 1 // Show the end result. show_symbol_list(list, "\n\n"); printf("\n\n"); #endif } END_FOR_EACH_PTR(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.1/test-sort.c000066400000000000000000000015241355073133200151270ustar00rootroot00000000000000#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.1/test-unssa.c000066400000000000000000000032351355073133200152720ustar00rootroot00000000000000#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.1/token.h000066400000000000000000000143431355073133200143130ustar00rootroot00000000000000#ifndef TOKEN_H #define TOKEN_H /* * Basic tokenization structures. NOTE! Those tokens had better * be pretty small, since we're going to keep them all in memory * indefinitely. * * Copyright (C) 2003 Transmeta Corp. * 2003 Linus Torvalds * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include "lib.h" /* * This describes the pure lexical elements (tokens), with * no semantic meaning. In other words, an identifier doesn't * have a type or meaning, it is only a specific string in * the input stream. * * Semantic meaning is handled elsewhere. */ enum constantfile { CONSTANT_FILE_MAYBE, // To be determined, not inside any #ifs in this file CONSTANT_FILE_IFNDEF, // To be determined, currently inside #ifndef CONSTANT_FILE_NOPE, // No CONSTANT_FILE_YES // Yes }; extern const char *includepath[]; struct stream { int fd; const char *name; const char *path; // input-file path - see set_stream_include_path() const char **next_path; /* Use these to check for "already parsed" */ enum constantfile constant; int dirty, next_stream, once; struct ident *protect; struct token *ifndef; struct token *top_if; }; extern int input_stream_nr; extern struct stream *input_streams; extern unsigned int tabstop; extern int *hash_stream(const char *name); struct ident { struct ident *next; /* Hash chain of identifiers */ struct symbol *symbols; /* Pointer to semantic meaning list */ unsigned char len; /* Length of identifier name */ unsigned char tainted:1, reserved:1, keyword:1; char name[]; /* Actual identifier */ }; enum token_type { TOKEN_EOF, TOKEN_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 char *, int fd, const char **next_path); extern const char *stream_name(int stream); extern struct ident *hash_ident(struct ident *); extern struct ident *built_in_ident(const char *); extern struct token *built_in_token(int, struct ident *); extern const char *show_special(int); extern const char *show_ident(const struct ident *); extern const char *show_string(const struct string *string); extern const char *show_token(const struct token *); extern const char *quote_token(const struct token *); extern struct token * tokenize(const char *, int, struct token *, const char **next_path); extern struct token * tokenize_buffer(void *, unsigned long, struct token **); extern void show_identifier_stats(void); extern struct token *preprocess(struct token *); static inline int match_op(struct token *token, unsigned int op) { return token->pos.type == TOKEN_SPECIAL && token->special == op; } static inline int match_ident(struct token *token, struct ident *id) { return token->pos.type == TOKEN_IDENT && token->ident == id; } #endif sparse-0.6.1/tokenize.c000066400000000000000000000557361355073133200150310ustar00rootroot00000000000000/* * This is a really stupid C tokenizer. It doesn't do any include * files or anything complex at all. That's the preprocessor. * * Copyright (C) 2003 Transmeta Corp. * 2003 Linus Torvalds * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include #include #include #include #include #include #include "lib.h" #include "allocate.h" #include "token.h" #include "symbol.h" #define EOF (-1) int input_stream_nr = 0; struct stream *input_streams; static int input_streams_allocated; unsigned int tabstop = 8; #define BUFSIZE (8192) typedef struct { int fd, offset, size; int pos, line, nr; int newline, whitespace; struct token **tokenlist; struct token *token; unsigned char *buffer; } stream_t; const char *stream_name(int stream) { if (stream < 0 || stream > input_stream_nr) return ""; return input_streams[stream].name; } static struct position stream_pos(stream_t *stream) { struct position pos; pos.type = 0; pos.stream = stream->nr; pos.newline = stream->newline; pos.whitespace = stream->whitespace; pos.pos = stream->pos; pos.line = stream->line; pos.noexpand = 0; return pos; } const char *show_special(int val) { static char buffer[4]; buffer[0] = val; buffer[1] = 0; if (val >= SPECIAL_BASE) strcpy(buffer, (char *) combinations[val - SPECIAL_BASE]); return buffer; } const char *show_ident(const struct ident *ident) { static char 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 char *name, int fd, const char **next_path) { int stream = input_stream_nr, *hash; struct stream *current; if (stream >= input_streams_allocated) { int newalloc = stream * 4 / 3 + 10; input_streams = realloc(input_streams, newalloc * sizeof(struct stream)); if (!input_streams) die("Unable to allocate more streams space"); input_streams_allocated = newalloc; } current = input_streams + stream; memset(current, 0, sizeof(*current)); current->name = name; current->fd = fd; current->next_path = next_path; current->path = NULL; current->constant = CONSTANT_FILE_MAYBE; input_stream_nr = stream+1; hash = hash_stream(name); current->next_stream = *hash; *hash = stream; return stream; } static struct token * alloc_token(stream_t *stream) { struct token *token = __alloc_token(0); token->pos = stream_pos(stream); return token; } /* * Argh... That was surprisingly messy - handling '\r' complicates the * things a _lot_. */ static int nextchar_slow(stream_t *stream) { int offset = stream->offset; int size = stream->size; int c; int spliced = 0, had_cr, had_backslash; restart: had_cr = had_backslash = 0; repeat: if (offset >= size) { if (stream->fd < 0) goto got_eof; size = read(stream->fd, stream->buffer, BUFSIZE); if (size <= 0) goto got_eof; stream->size = size; stream->offset = offset = 0; } c = stream->buffer[offset++]; if (had_cr) goto check_lf; if (c == '\r') { had_cr = 1; goto repeat; } norm: if (!had_backslash) { switch (c) { case '\t': stream->pos += tabstop - stream->pos % tabstop; break; case '\n': stream->line++; stream->pos = 0; stream->newline = 1; break; case '\\': had_backslash = 1; stream->pos++; goto repeat; default: stream->pos++; } } else { if (c == '\n') { stream->line++; stream->pos = 0; spliced = 1; goto restart; } offset--; c = '\\'; } out: stream->offset = offset; return c; check_lf: if (c != '\n') offset--; c = '\n'; goto norm; got_eof: if (had_backslash) { c = '\\'; goto out; } if (stream->pos) warning(stream_pos(stream), "no newline at end of file"); else if (spliced) warning(stream_pos(stream), "backslash-newline at end of file"); return EOF; } /* * We want that as light as possible while covering all normal cases. * Slow path (including the logics with line-splicing and EOF sanity * checks) is in nextchar_slow(). */ static inline int nextchar(stream_t *stream) { int offset = stream->offset; if (offset < stream->size) { int c = stream->buffer[offset++]; static const char special[256] = { ['\t'] = 1, ['\r'] = 1, ['\n'] = 1, ['\\'] = 1 }; if (!special[c]) { stream->offset = offset; stream->pos++; return c; } } return nextchar_slow(stream); } struct token eof_token_entry; static struct token *mark_eof(stream_t *stream) { struct token *end; end = alloc_token(stream); 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 <= 4) { if (len == 0) { sparse_error(stream_pos(stream), "empty character constant"); return nextchar(stream); } token_type(token) = type + len; memset(buffer + len, '\0', 4 - len); memcpy(token->embedded, buffer, 4); } else { token_type(token) = type; string = __alloc_string(len+1); memcpy(string->data, buffer, len); string->data[len] = '\0'; string->length = len+1; token->string = string; } /* Pass it on.. */ token = stream->token; add_token(stream); return nextchar(stream); } static int drop_stream_eoln(stream_t *stream) { drop_token(stream); for (;;) { switch (nextchar(stream)) { case EOF: return EOF; case '\n': return nextchar(stream); } } } static int drop_stream_comment(stream_t *stream) { int newline; int next; drop_token(stream); newline = stream->newline; next = nextchar(stream); for (;;) { int curr = next; if (curr == EOF) { warning(stream_pos(stream), "End of file in the middle of a comment"); return curr; } next = nextchar(stream); if (curr == '*' && next == '/') break; } stream->newline = newline; return nextchar(stream); } unsigned char combinations[][4] = COMBINATION_STRINGS; #define NR_COMBINATIONS (SPECIAL_ARG_SEPARATOR - SPECIAL_BASE) /* hash function for two-character punctuators - all give unique values */ #define special_hash(c0, c1) (((c0*8+c1*2)+((c0*8+c1*2)>>5))&31) /* * note that we won't get false positives - special_hash(0,0) is 0 and * entry 0 is filled (by +=), so all the missing ones are OK. */ static unsigned char hash_results[32][2] = { #define RES(c0, c1) [special_hash(c0, c1)] = {c0, c1} RES('+', '='), /* 00 */ RES('/', '='), /* 01 */ RES('^', '='), /* 05 */ RES('&', '&'), /* 07 */ RES('#', '#'), /* 08 */ RES('<', '<'), /* 0a */ RES('<', '='), /* 0c */ RES('!', '='), /* 0e */ RES('%', '='), /* 0f */ RES('-', '-'), /* 10 */ RES('-', '='), /* 11 */ RES('-', '>'), /* 13 */ RES('=', '='), /* 15 */ RES('&', '='), /* 17 */ RES('*', '='), /* 18 */ RES('.', '.'), /* 1a */ RES('+', '+'), /* 1b */ RES('|', '='), /* 1c */ RES('>', '='), /* 1d */ RES('|', '|'), /* 1e */ RES('>', '>') /* 1f */ #undef RES }; static int code[32] = { #define CODE(c0, c1, value) [special_hash(c0, c1)] = value CODE('+', '=', SPECIAL_ADD_ASSIGN), /* 00 */ CODE('/', '=', SPECIAL_DIV_ASSIGN), /* 01 */ CODE('^', '=', SPECIAL_XOR_ASSIGN), /* 05 */ CODE('&', '&', SPECIAL_LOGICAL_AND), /* 07 */ CODE('#', '#', SPECIAL_HASHHASH), /* 08 */ CODE('<', '<', SPECIAL_LEFTSHIFT), /* 0a */ CODE('<', '=', SPECIAL_LTE), /* 0c */ CODE('!', '=', SPECIAL_NOTEQUAL), /* 0e */ CODE('%', '=', SPECIAL_MOD_ASSIGN), /* 0f */ CODE('-', '-', SPECIAL_DECREMENT), /* 10 */ CODE('-', '=', SPECIAL_SUB_ASSIGN), /* 11 */ CODE('-', '>', SPECIAL_DEREFERENCE), /* 13 */ CODE('=', '=', SPECIAL_EQUAL), /* 15 */ CODE('&', '=', SPECIAL_AND_ASSIGN), /* 17 */ CODE('*', '=', SPECIAL_MUL_ASSIGN), /* 18 */ CODE('.', '.', SPECIAL_DOTDOT), /* 1a */ CODE('+', '+', SPECIAL_INCREMENT), /* 1b */ CODE('|', '=', SPECIAL_OR_ASSIGN), /* 1c */ CODE('>', '=', SPECIAL_GTE), /* 1d */ CODE('|', '|', SPECIAL_LOGICAL_OR), /* 1e */ CODE('>', '>', SPECIAL_RIGHTSHIFT) /* 1f */ #undef CODE }; static int get_one_special(int c, stream_t *stream) { struct token *token; int next, value, i; next = nextchar(stream); /* * Check for numbers, strings, character constants, and comments */ switch (c) { case '.': if (next >= '0' && next <= '9') return get_one_number(c, next, stream); break; case '"': return eat_string(next, stream, TOKEN_STRING); case '\'': return eat_string(next, stream, TOKEN_CHAR); case '/': if (next == '/') return drop_stream_eoln(stream); if (next == '*') return drop_stream_comment(stream); } /* * Check for combinations */ value = c; if (cclass[next + 1] & ValidSecond) { i = special_hash(c, next); if (hash_results[i][0] == c && hash_results[i][1] == next) { value = code[i]; next = nextchar(stream); if (value >= SPECIAL_LEFTSHIFT && next == "==."[value - SPECIAL_LEFTSHIFT]) { value += 3; next = nextchar(stream); } } } /* Pass it on.. */ token = stream->token; token_type(token) = TOKEN_SPECIAL; token->special = value; add_token(stream); return next; } #define IDENT_HASH_BITS (13) #define IDENT_HASH_SIZE (1<> IDENT_HASH_BITS) + (hash)) & IDENT_HASH_MASK) static struct ident *hash_table[IDENT_HASH_SIZE]; static int ident_hit, ident_miss, idents; void show_identifier_stats(void) { int i; int distribution[100]; fprintf(stderr, "identifiers: %d hits, %d misses\n", ident_hit, ident_miss); for (i = 0; i < 100; i++) distribution[i] = 0; for (i = 0; i < IDENT_HASH_SIZE; i++) { struct ident * ident = hash_table[i]; int count = 0; while (ident) { count++; ident = ident->next; } if (count > 99) count = 99; distribution[count]++; } for (i = 0; i < 100; i++) { if (distribution[i]) fprintf(stderr, "%2d: %d buckets\n", i, distribution[i]); } } static struct ident *alloc_ident(const char *name, int len) { struct ident *ident = __alloc_ident(len); ident->symbols = NULL; ident->len = len; ident->tainted = 0; memcpy(ident->name, name, len); return ident; } static struct ident * insert_hash(struct ident *ident, unsigned long hash) { ident->next = hash_table[hash]; hash_table[hash] = ident; ident_miss++; return ident; } static struct ident *create_hashed_ident(const char *name, int len, unsigned long hash) { struct ident *ident; struct ident **p; p = &hash_table[hash]; while ((ident = *p) != NULL) { if (ident->len == (unsigned char) len) { if (strncmp(name, ident->name, len) != 0) goto next; ident_hit++; return ident; } next: //misses++; p = &ident->next; } ident = alloc_ident(name, len); *p = ident; ident->next = NULL; ident_miss++; idents++; return ident; } static unsigned long hash_name(const char *name, int len) { unsigned long hash; const unsigned char *p = (const unsigned char *)name; hash = ident_hash_init(*p++); while (--len) { unsigned int i = *p++; hash = ident_hash_add(hash, i); } return ident_hash_end(hash); } struct ident *hash_ident(struct ident *ident) { return insert_hash(ident, hash_name(ident->name, ident->len)); } struct ident *built_in_ident(const char *name) { int len = strlen(name); return create_hashed_ident(name, len, hash_name(name, len)); } struct token *built_in_token(int stream, struct ident *ident) { struct token *token; token = __alloc_token(0); token->pos.stream = stream; token_type(token) = TOKEN_IDENT; token->ident = ident; return token; } static int get_one_identifier(int c, stream_t *stream) { struct token *token; struct ident *ident; unsigned long hash; char buf[256]; int len = 1; int next; hash = ident_hash_init(c); buf[0] = c; for (;;) { next = nextchar(stream); if (!(cclass[next + 1] & (Letter | Digit))) break; if (len >= sizeof(buf)) break; hash = ident_hash_add(hash, next); buf[len] = next; len++; }; if (cclass[next + 1] & Quote) { if (len == 1 && buf[0] == 'L') { if (next == '\'') return eat_string(nextchar(stream), stream, TOKEN_WIDE_CHAR); else return eat_string(nextchar(stream), stream, TOKEN_WIDE_STRING); } } hash = ident_hash_end(hash); ident = create_hashed_ident(buf, len, hash); /* Pass it on.. */ token = stream->token; token_type(token) = TOKEN_IDENT; token->ident = ident; add_token(stream); return next; } static int get_one_token(int c, stream_t *stream) { long class = cclass[c + 1]; if (class & Digit) return get_one_number(c, nextchar(stream), stream); if (class & Letter) return get_one_identifier(c, stream); return get_one_special(c, stream); } static struct token *setup_stream(stream_t *stream, int idx, int fd, unsigned char *buf, unsigned int buf_size) { struct token *begin; stream->nr = idx; stream->line = 1; stream->newline = 1; stream->whitespace = 0; stream->pos = 0; stream->token = NULL; stream->fd = fd; stream->offset = 0; stream->size = buf_size; stream->buffer = buf; begin = alloc_token(stream); token_type(begin) = TOKEN_STREAMBEGIN; stream->tokenlist = &begin->next; return begin; } static struct token *tokenize_stream(stream_t *stream) { int c = nextchar(stream); while (c != EOF) { if (!isspace(c)) { struct token *token = alloc_token(stream); stream->token = token; stream->newline = 0; stream->whitespace = 0; c = get_one_token(c, stream); continue; } stream->whitespace = 1; c = nextchar(stream); } return mark_eof(stream); } struct token * tokenize_buffer(void *buffer, unsigned long size, struct token **endtoken) { stream_t stream; struct token *begin; begin = setup_stream(&stream, 0, -1, buffer, size); *endtoken = tokenize_stream(&stream); return begin; } struct token * tokenize(const char *name, int fd, struct token *endtoken, const char **next_path) { struct token *begin, *end; stream_t stream; unsigned char buffer[BUFSIZE]; int idx; idx = init_stream(name, fd, next_path); if (idx < 0) { // info(endtoken->pos, "File %s is const", name); return endtoken; } begin = setup_stream(&stream, idx, fd, buffer, 0); end = tokenize_stream(&stream); if (endtoken) end->next = endtoken; return begin; } sparse-0.6.1/unssa.c000066400000000000000000000070131355073133200143130ustar00rootroot00000000000000/* * 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.1/utils.c000066400000000000000000000013131355073133200143170ustar00rootroot00000000000000// SPDX-License-Identifier: MIT // Copyright (C) 2018 Luc Van Oostenryck #include "utils.h" #include "allocate.h" #include #include #include 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.1/utils.h000066400000000000000000000023171355073133200143310ustar00rootroot00000000000000#ifndef UTILS_H #define UTILS_H /// // Miscellaneous utilities // ----------------------- #include #include /// // 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.1/validation/000077500000000000000000000000001355073133200151475ustar00rootroot00000000000000sparse-0.6.1/validation/.gitignore000066400000000000000000000000451355073133200171360ustar00rootroot00000000000000# test-suite *.diff *.got *.expected sparse-0.6.1/validation/Waddress-array.c000066400000000000000000000006601355073133200202050ustar00rootroot00000000000000int 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.1/validation/Waddress-function.c000066400000000000000000000004721355073133200207150ustar00rootroot00000000000000extern 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.1/validation/Waddress-space-all-attr.c000066400000000000000000000047601355073133200217050ustar00rootroot00000000000000/* 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.1/validation/Waddress-space-from.c000066400000000000000000000037221355073133200211250ustar00rootroot00000000000000 #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.1/validation/Waddress-space-strict.c000066400000000000000000000017341355073133200214730ustar00rootroot00000000000000#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.1/validation/Waddress-weak.c000066400000000000000000000011521355073133200200130ustar00rootroot00000000000000extern 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.1/validation/Waddress.c000066400000000000000000000073701355073133200170760ustar00rootroot00000000000000extern 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.1/validation/Wcast-to-as.c000066400000000000000000000013041355073133200174130ustar00rootroot00000000000000#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.1/validation/Woverride-init-def.c000066400000000000000000000004561355073133200207630ustar00rootroot00000000000000static 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.1/validation/Woverride-init-no.c000066400000000000000000000003151355073133200206330ustar00rootroot00000000000000static 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.1/validation/Woverride-init-yes.c000066400000000000000000000004761355073133200210270ustar00rootroot00000000000000static 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.1/validation/Wunknown-attribute-def.c000066400000000000000000000001471355073133200217000ustar00rootroot00000000000000static int foo(void) __attribute__((unknown_attribute)); /* * check-name: warn-unknown-attribute */ sparse-0.6.1/validation/Wunknown-attribute-no.c000066400000000000000000000003131355073133200215510ustar00rootroot00000000000000static 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.1/validation/Wunknown-attribute-yes.c000066400000000000000000000004271355073133200217430ustar00rootroot00000000000000static 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.1/validation/__func__.c000066400000000000000000000005051355073133200170420ustar00rootroot00000000000000static 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.1/validation/abi-integer.c000066400000000000000000000010371355073133200175020ustar00rootroot00000000000000#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.1/validation/abstract-array-declarator-static.c000066400000000000000000000010261355073133200236340ustar00rootroot00000000000000 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.1/validation/address_space.c000066400000000000000000000006751355073133200201230ustar00rootroot00000000000000#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.1/validation/alloc-align.c000066400000000000000000000024301355073133200174740ustar00rootroot00000000000000typedef 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.1/validation/alternate-keywords.c000066400000000000000000000032171355073133200211420ustar00rootroot00000000000000 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.1/validation/anon-union.c000066400000000000000000000002401355073133200173700ustar00rootroot00000000000000struct s { union { int val; }; }; static struct s foo = { .val = 5, }; /* * check-name: test anonymous union initializer */ sparse-0.6.1/validation/array-implicit-size.c000066400000000000000000000007331355073133200212140ustar00rootroot00000000000000static 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.1/validation/as-name.c000066400000000000000000000006001355073133200166300ustar00rootroot00000000000000#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.1/validation/asm-bad0.c000066400000000000000000000015541355073133200167040ustar00rootroot00000000000000extern 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(oid) { 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.1/validation/asm-empty-clobber.c000066400000000000000000000015711355073133200206410ustar00rootroot00000000000000 # 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.1/validation/asm-goto-lables.c000066400000000000000000000012251355073133200203010ustar00rootroot00000000000000static 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.1/validation/asm-inline.c000066400000000000000000000027641355073133200173600ustar00rootroot00000000000000static 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.1/validation/attr-context.c000066400000000000000000000032121355073133200177450ustar00rootroot00000000000000static 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.1/validation/attr-inline.c000066400000000000000000000005741355073133200175470ustar00rootroot00000000000000 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.1/validation/attr-no_sanitize_address.c000066400000000000000000000002511355073133200223100ustar00rootroot00000000000000#define __no_sanitize_address __attribute__((no_sanitize_address)) static void __no_sanitize_address bar(void) { } /* * check-name: attribute no_sanitize_address */ sparse-0.6.1/validation/attr-noclone.c000066400000000000000000000001721355073133200177200ustar00rootroot00000000000000#define noclone __attribute__((__noclone__)) static void noclone bar(void) { } /* * check-name: attribute noclone */ sparse-0.6.1/validation/attr-optimize.c000066400000000000000000000004021355073133200201170ustar00rootroot00000000000000 #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.1/validation/attr-warning.c000066400000000000000000000002621355073133200177300ustar00rootroot00000000000000# define __warndecl(name, msg) \ extern void name (void) __attribute__((__warning__ (msg))) __warndecl (__warn_func, "warn message"); /* * check-name: attribute warning */ sparse-0.6.1/validation/attr_aligned.c000066400000000000000000000002421355073133200177460ustar00rootroot00000000000000void *foo(void) __attribute__((__assume_aligned__(4096))); void *foo(void) __attribute__((assume_aligned(4096))); /* * check-name: attribute assume_aligned */ sparse-0.6.1/validation/attr_in_parameter.c000066400000000000000000000003551355073133200210160ustar00rootroot00000000000000#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.1/validation/attr_vector_size.c000066400000000000000000000001771355073133200207060ustar00rootroot00000000000000typedef unsigned int u32; typedef u32 __attribute__((vector_size(16))) sse128_t; /* * check-name: attribute vector_size */ sparse-0.6.1/validation/backend/000077500000000000000000000000001355073133200165365ustar00rootroot00000000000000sparse-0.6.1/validation/backend/arithmetic-ops.c000066400000000000000000000025431355073133200216360ustar00rootroot00000000000000static 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.1/validation/backend/array.c000066400000000000000000000001611355073133200200160ustar00rootroot00000000000000static char array[128]; /* * check-name: Array code generation * check-command: sparsec -c $file -o tmp.o */ sparse-0.6.1/validation/backend/bitwise-ops.c000066400000000000000000000014741355073133200211550ustar00rootroot00000000000000static 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.1/validation/backend/bool-test.c000066400000000000000000000002171355073133200206120ustar00rootroot00000000000000static _Bool return_false(void) { return 0; } /* * check-name: Boolean type code generation * check-command: sparsec -c $file -o tmp.o */ sparse-0.6.1/validation/backend/call-variadic.c000066400000000000000000000011731355073133200213770ustar00rootroot00000000000000#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.1/validation/backend/cast.c000066400000000000000000000021601355073133200176330ustar00rootroot00000000000000typedef _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.1/validation/backend/cmp-ops.c000066400000000000000000000017761355073133200202730ustar00rootroot00000000000000static 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.1/validation/backend/compare-with-null.c000066400000000000000000000005101355073133200222450ustar00rootroot00000000000000int 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.1/validation/backend/constant-pointer.c000066400000000000000000000006561355073133200222200ustar00rootroot00000000000000extern 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.1/validation/backend/degenerate-ptr.c000066400000000000000000000015421355073133200216120ustar00rootroot00000000000000extern 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.1/validation/backend/extern.c000066400000000000000000000002541355073133200202100ustar00rootroot00000000000000extern 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.1/validation/backend/fn-ref.c000066400000000000000000000006021355073133200200550ustar00rootroot00000000000000extern 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.1/validation/backend/function-ptr-xtype.c000066400000000000000000000014631355073133200225050ustar00rootroot00000000000000typedef 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.1/validation/backend/function-ptr.c000066400000000000000000000046631355073133200213430ustar00rootroot00000000000000extern 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.1/validation/backend/hello.c000066400000000000000000000002761355073133200200120ustar00rootroot00000000000000#include int main(int argc, char *argv[]) { puts("hello, world"); return 0; } /* * check-name: 'hello, world' code generation * check-command: sparsec -c $file -o tmp.o */ sparse-0.6.1/validation/backend/int-cond.c000066400000000000000000000006411355073133200204160ustar00rootroot00000000000000static 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.1/validation/backend/label-as-value.c000066400000000000000000000002561355073133200214770ustar00rootroot00000000000000void *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.1/validation/backend/load-global.c000066400000000000000000000006231355073133200210600ustar00rootroot00000000000000const 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.1/validation/backend/load-type.c000066400000000000000000000003221355073133200205750ustar00rootroot00000000000000extern 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.1/validation/backend/logical-ops.c000066400000000000000000000005651355073133200211210ustar00rootroot00000000000000static 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.1/validation/backend/loop.c000066400000000000000000000003321355073133200176510ustar00rootroot00000000000000 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.1/validation/backend/loop2.c000066400000000000000000000002671355073133200177420ustar00rootroot00000000000000extern 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.1/validation/backend/pointer-add.c000066400000000000000000000042521355073133200211130ustar00rootroot00000000000000char *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.1/validation/backend/pointer-cmp.c000066400000000000000000000006231355073133200211400ustar00rootroot00000000000000int 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.1/validation/backend/pointer-param.c000066400000000000000000000013001355073133200214520ustar00rootroot00000000000000extern 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.1/validation/backend/pointer-sub.c000066400000000000000000000012401355073133200211460ustar00rootroot00000000000000long 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.1/validation/backend/ptrcast.c000066400000000000000000000002501355073133200203570ustar00rootroot00000000000000static 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.1/validation/backend/setval.c000066400000000000000000000002531355073133200202000ustar00rootroot00000000000000double 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.1/validation/backend/shift-special.c000066400000000000000000000003061355073133200214340ustar00rootroot00000000000000long 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.1/validation/backend/store-type.c000066400000000000000000000002621355073133200210150ustar00rootroot00000000000000struct 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.1/validation/backend/store-x2.c000066400000000000000000000005251355073133200203670ustar00rootroot00000000000000void 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.1/validation/backend/string-value.c000066400000000000000000000003631355073133200213240ustar00rootroot00000000000000extern 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.1/validation/backend/struct-access.c000066400000000000000000000005361355073133200214710ustar00rootroot00000000000000struct 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.1/validation/backend/struct.c000066400000000000000000000006001355073133200202220ustar00rootroot00000000000000struct 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.1/validation/backend/sum.c000066400000000000000000000005711355073133200175110ustar00rootroot00000000000000#include #include static int sum(int n) { int i, result = 0; for (i = 1; i <= n; ++i) result += i; return result; } int main(int argc, char **argv) { printf("%d\n", sum(5)); printf("%d\n", sum(100)); return 0; } /* * check-name: sum from 1 to n * check-command: sparsei --no-jit $file * * check-output-start 15 5050 * check-output-end */ sparse-0.6.1/validation/backend/switch.c000066400000000000000000000075531355073133200202150ustar00rootroot00000000000000int 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.1/validation/backend/symaddr.c000066400000000000000000000024771355073133200203570ustar00rootroot00000000000000extern 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.1/validation/backend/type-constant.c000066400000000000000000000012471355073133200215160ustar00rootroot00000000000000char 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.1/validation/backend/union.c000066400000000000000000000002671355073133200200370ustar00rootroot00000000000000union 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.1/validation/backend/void-return-type.c000066400000000000000000000002501355073133200221340ustar00rootroot00000000000000static 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.1/validation/bad-array-designated-initializer.c000066400000000000000000000005571355073133200236120ustar00rootroot00000000000000static 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.1/validation/bad-assignment.c000066400000000000000000000003401355073133200202040ustar00rootroot00000000000000static int foo(int a) { a |=\1; return a; } /* * check-name: bad assignment * * check-error-start bad-assignment.c:3:13: error: Expected ; at end of statement bad-assignment.c:3:13: error: got \ * check-error-end */ sparse-0.6.1/validation/bad-cast.c000066400000000000000000000004261355073133200167730ustar00rootroot00000000000000struct 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.1/validation/bad-return-type.c000066400000000000000000000004621355073133200203370ustar00rootroot00000000000000void 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.1/validation/bad-ternary-cond.c000066400000000000000000000004371355073133200204500ustar00rootroot00000000000000static 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.1/validation/bad-type-twice0.c000066400000000000000000000003451355073133200202130ustar00rootroot00000000000000static int foo(a) { return a ? : 1; } /* * check-name: bad-type-twice0 * * check-error-start bad-type-twice0.c:3:16: error: non-scalar type in conditional: bad-type-twice0.c:3:16: incomplete type a * check-error-end */ sparse-0.6.1/validation/bad-type-twice1.c000066400000000000000000000005161355073133200202140ustar00rootroot00000000000000static 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.1/validation/bad-type-twice2.c000066400000000000000000000006621355073133200202170ustar00rootroot00000000000000extern 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.1/validation/bad-typeof.c000066400000000000000000000003251355073133200173450ustar00rootroot00000000000000static 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.1/validation/badtype1.c000066400000000000000000000002751355073133200170300ustar00rootroot00000000000000static 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.1/validation/badtype2.c000066400000000000000000000012201355073133200170200ustar00rootroot00000000000000//typedef int undef; extern undef bar(void); static undef foo(char *c) { char p = *c; switch (p) { default: return bar(); } } /* * check-name: missing type * check-error-start badtype2.c:2:8: warning: 'undef' has implicit type badtype2.c:2:14: error: Expected ; at end of declaration badtype2.c:2:14: error: got bar badtype2.c:3:14: error: Expected ; at end of declaration badtype2.c:3:14: error: got foo badtype2.c:6:3: error: Trying to use reserved word 'switch' as identifier badtype2.c:7:3: error: not in switch scope badtype2.c:10:1: error: Expected ; at the end of type declaration badtype2.c:10:1: error: got } * check-error-end */ sparse-0.6.1/validation/badtype3.c000066400000000000000000000014761355073133200170360ustar00rootroot00000000000000int foo (int (*func) (undef, void *), void *data) { int err = 0; while (cur) { if ((*func) (cur, data)) break; } return err; } /* * check-name: missing type in argument list * check-error-start badtype3.c:2:18: warning: identifier list not in definition badtype3.c:2:24: error: Expected ) in function declarator badtype3.c:2:24: error: got , badtype3.c:5:3: error: Trying to use reserved word 'while' as identifier badtype3.c:7:7: error: break/continue not in iterator scope badtype3.c:9:3: error: Trying to use reserved word 'return' as identifier badtype3.c:9:10: error: Expected ; at end of declaration badtype3.c:9:10: error: got err badtype3.c:10:1: error: Expected ; at the end of type declaration badtype3.c:10:1: error: got } badtype3.c:6:11: error: undefined identifier 'func' * check-error-end */ sparse-0.6.1/validation/badtype4.c000066400000000000000000000004051355073133200170260ustar00rootroot00000000000000void 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.1/validation/badtype5.c000066400000000000000000000007371355073133200170370ustar00rootroot00000000000000#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.1/validation/binary-constant.c000066400000000000000000000001041355073133200204210ustar00rootroot00000000000000extern int x; int x = 0b11; /* * check-name: binary constant */ sparse-0.6.1/validation/bitfield-bool-layout.c000066400000000000000000000010541355073133200213410ustar00rootroot00000000000000struct 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.1/validation/bitfield-kr.c000066400000000000000000000003311355073133200175040ustar00rootroot00000000000000static 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.1/validation/bitfields.c000066400000000000000000000004641355073133200172640ustar00rootroot00000000000000/* * 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.1/validation/bitwise-cast-ptr.c000066400000000000000000000016201355073133200205130ustar00rootroot00000000000000#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.1/validation/bitwise-cast.c000066400000000000000000000021051355073133200177070ustar00rootroot00000000000000typedef 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.1/validation/bool-array.c000066400000000000000000000014771355073133200173730ustar00rootroot00000000000000static _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.1/validation/bool-cast-bad.c000066400000000000000000000011671355073133200177270ustar00rootroot00000000000000typedef 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.1/validation/bool-cast-restricted.c000066400000000000000000000032001355073133200213370ustar00rootroot00000000000000typedef 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.1/validation/bool-float.c000066400000000000000000000002721355073133200173520ustar00rootroot00000000000000int 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.1/validation/bswap-constant-folding.c000066400000000000000000000011171355073133200216760ustar00rootroot00000000000000typedef 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.1/validation/bug-bad-type.c000066400000000000000000000004641355073133200175770ustar00rootroot00000000000000struct 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.1/validation/bug-crash16.c000066400000000000000000000001601355073133200173320ustar00rootroot00000000000000static void foo(void) { int b[] = { 8 }; int c; for (;;) b[c] = b[0]; } /* * check-name: bug-crash16 */ sparse-0.6.1/validation/bug-expand-union0.c000066400000000000000000000005521355073133200205550ustar00rootroot00000000000000union u { char c; float f; }; static int foo(void) { union u u = { .f = 0.123 }; return u.c; } /* * check-name: bug-expand-union * check description: must not infer the value from the float * check-command: test-linearize $file * check-known-to-fail * * check-output-ignore * check-output-contains: load\\. * check-output-excludes: ret\\..*\\$ */ sparse-0.6.1/validation/bug-expand-union1.c000066400000000000000000000005031355073133200205520ustar00rootroot00000000000000union u { int i; float f; }; static int foo(void) { union u u = { .f = 0.123 }; return u.i; } /* * check-name: bug-expand-union * check description: must not infer the value from the float * check-command: test-linearize $file * check-known-to-fail * * check-output-ignore * check-output-contains: load\\. */ sparse-0.6.1/validation/bug-rshift-ub.c000066400000000000000000000006071355073133200177740ustar00rootroot00000000000000enum 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.1/validation/bug_inline_switch.c000066400000000000000000000005301355073133200210050ustar00rootroot00000000000000 #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.1/validation/builtin-args-checking.c000066400000000000000000000034151355073133200214670ustar00rootroot00000000000000static 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.1/validation/builtin-arith.c000066400000000000000000000025421355073133200200710ustar00rootroot00000000000000 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-known-to-fail * * check-error-start builtin-arith.c:10:xx: error: ... builtin-arith.c:11:xx: error: ... builtin-arith.c:13:xx: error: arithmetics on pointers to functions builtin-arith.c:14:xx: error: arithmetics on pointers to functions builtin-arith.c:15:xx: error: arithmetics on pointers to functions builtin-arith.c:18:xx: error: ... builtin-arith.c:19:xx: error: ... builtin-arith.c:21:xx: error: ... builtin-arith.c:22:xx: error: ... builtin-arith.c:23:xx: error: ... builtin-arith.c:24:xx: error: ... builtin-arith.c:25:xx: error: ... builtin-arith.c:27:24: error: subtraction of functions? Share your drugs builtin-arith.c:28:13: error: subtraction of functions? Share your drugs * check-error-end */ sparse-0.6.1/validation/builtin-bswap-constant.c000066400000000000000000000013351355073133200217240ustar00rootroot00000000000000unsigned 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.1/validation/builtin-bswap-variable.c000066400000000000000000000014161355073133200216600ustar00rootroot00000000000000typedef 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.1/validation/builtin-fp-unop.c000066400000000000000000000054361355073133200203530ustar00rootroot00000000000000static 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.1/validation/builtin-overflow.c000066400000000000000000000266111355073133200206300ustar00rootroot00000000000000enum 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.1/validation/builtin-prototype.c000066400000000000000000000004661355073133200210320ustar00rootroot00000000000000void 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.1/validation/builtin_atomic.c000066400000000000000000000013431355073133200203160ustar00rootroot00000000000000static 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.1/validation/builtin_bswap.c000066400000000000000000000003411355073133200201530ustar00rootroot00000000000000static 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.1/validation/builtin_inf.c000066400000000000000000000010271355073133200176150ustar00rootroot00000000000000static 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.1/validation/builtin_safe1.c000066400000000000000000000020271355073133200200410ustar00rootroot00000000000000#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.1/validation/builtin_unreachable.c000066400000000000000000000003511355073133200213110ustar00rootroot00000000000000/* 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.1/validation/builtin_va_arg_pack.c000066400000000000000000000005301355073133200212740ustar00rootroot00000000000000extern 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.1/validation/c11-alignas.c000066400000000000000000000017261355073133200173210ustar00rootroot00000000000000static _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.1/validation/c11-alignof.c000066400000000000000000000003101355073133200173060ustar00rootroot00000000000000static 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.1/validation/c11-atomic.c000066400000000000000000000054651355073133200171630ustar00rootroot00000000000000void 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-pass */ 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; extern const int *pqo; extern int *puo; pqo = &qo; /* check-should-pass */ pqo = &uo; /* check-should-pass */ pqo = puo; puo = &uo; /* check-should-pass */ puo = &qo; /* check-should-fail */ puo = pqo; /* check-should-fail */ } void bar(void) { extern int _Atomic *pqo; extern int *puo; pqo = &qo; /* check-should-pass */ pqo = &uo; /* check-should-pass */ pqo = puo; puo = &uo; /* check-should-pass */ puo = &qo; /* check-should-fail */ puo = pqo; /* check-should-fail */ } void baz(void) { extern typeof(&qo) pqo; extern typeof(&uo) puo; pqo = &qo; /* check-should-pass */ pqo = &uo; /* check-should-pass */ pqo = puo; 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:11:6: error: symbol 'f02' redeclared with different type (originally declared at c11-atomic.c:3) - incompatible argument 1 (different modifiers) c11-atomic.c:12:6: error: symbol 'f03' redeclared with different type (originally declared at c11-atomic.c:4) - incompatible argument 1 (different modifiers) c11-atomic.c:33:13: warning: incorrect type in assignment (different modifiers) c11-atomic.c:33:13: expected int *extern [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 *extern [assigned] puo c11-atomic.c:34:13: got int const *extern [assigned] pqo c11-atomic.c:48:13: warning: incorrect type in assignment (different modifiers) c11-atomic.c:48:13: expected int *extern [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 *extern [assigned] puo c11-atomic.c:49:13: got int [atomic] *extern [assigned] pqo c11-atomic.c:63:13: warning: incorrect type in assignment (different modifiers) c11-atomic.c:63:13: expected int *extern [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 *extern [assigned] puo c11-atomic.c:64:13: got int [atomic] *extern [assigned] pqo * check-error-end */ sparse-0.6.1/validation/c11-noreturn.c000066400000000000000000000003051355073133200175470ustar00rootroot00000000000000static _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.1/validation/c11-stdc-version.c000066400000000000000000000002321355073133200203120ustar00rootroot00000000000000__STDC_VERSION__ /* * check-name: c11-stdc-version * check-command: sparse -E -std=c11 $file * * check-output-start 201112L * check-output-end */ sparse-0.6.1/validation/c11-thread-local.c000066400000000000000000000002621355073133200202340ustar00rootroot00000000000000static _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.1/validation/c99-for-loop-decl.c000066400000000000000000000017321355073133200203620ustar00rootroot00000000000000static 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.1/validation/c99-for-loop.c000066400000000000000000000005071355073133200174540ustar00rootroot00000000000000int 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.1/validation/call-inlined.c000066400000000000000000000014641355073133200176530ustar00rootroot00000000000000static 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 ret.32 %r5 bar: .L3: ret bas: .L6: add.64 %r16 <- "abc", $1 ret.64 %r16 qus: .L9: add.64 %r21 <- messg, $1 ret.64 %r21 * check-output-end */ sparse-0.6.1/validation/call-variadic.c000066400000000000000000000007311355073133200200070ustar00rootroot00000000000000#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.1/validation/calling-convention-attributes.c000066400000000000000000000014211355073133200232660ustar00rootroot00000000000000extern 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.1/validation/cast-bad-00.c000066400000000000000000000014341355073133200172100ustar00rootroot00000000000000typedef 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.1/validation/cast-bad-01.c000066400000000000000000000003311355073133200172040ustar00rootroot00000000000000extern 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.1/validation/cast-kinds-check.c000066400000000000000000000014061355073133200204270ustar00rootroot00000000000000#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 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.1/validation/cast-weirds.c000066400000000000000000000010731355073133200175410ustar00rootroot00000000000000typedef 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.1/validation/char-signed.c000066400000000000000000000002541355073133200175000ustar00rootroot00000000000000void 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.1/validation/char-unsigned.c000066400000000000000000000003411355073133200200400ustar00rootroot00000000000000#define MASK ((1 << __CHAR_BIT__) - 1) void foo(void) { _Static_assert((char) -1 == (-1 & MASK), "plain char is not unsigned"); } /* * check-name: fsigned-char * check-command: sparse -funsigned-char -Wno-decl $file */ sparse-0.6.1/validation/check_access-multi.c000066400000000000000000000003511355073133200210400ustar00rootroot00000000000000extern 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.1/validation/check_access-store.c000066400000000000000000000005421355073133200210440ustar00rootroot00000000000000extern 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.1/validation/check_byte_count-ice.c000066400000000000000000000012151355073133200213600ustar00rootroot00000000000000extern 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.1/validation/choose_expr.c000066400000000000000000000012361355073133200176330ustar00rootroot00000000000000static 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.1/validation/comma.c000066400000000000000000000004151355073133200164070ustar00rootroot00000000000000static 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.1/validation/compare-null-to-int.c000066400000000000000000000005331355073133200211220ustar00rootroot00000000000000static 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.1/validation/compound-assign-type.c000066400000000000000000000006031355073133200213770ustar00rootroot00000000000000static 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.1/validation/compound-sizes.c000066400000000000000000000034451355073133200203000ustar00rootroot00000000000000// 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.1/validation/cond-address.c000066400000000000000000000005021355073133200176560ustar00rootroot00000000000000extern 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.1/validation/cond-err-expand.c000066400000000000000000000012331355073133200203000ustar00rootroot00000000000000static 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.1/validation/cond_expr.c000066400000000000000000000007341355073133200173000ustar00rootroot00000000000000/* * 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.1/validation/cond_expr2.c000066400000000000000000000015451355073133200173630ustar00rootroot00000000000000extern const int *p; extern volatile void *q; extern volatile int *r; static void f(void) { q = 1 ? p : q; // warn: const volatile void * -> const int * r = 1 ? r : q; // OK: volatile void * -> volatile int * r = 1 ? r : p; // warn: const volatile int * -> volatile int * } /* * check-name: type of conditional expression * check-description: Used to miss qualifier mixing and mishandle void * * * check-error-start cond_expr2.c:6:11: warning: incorrect type in assignment (different modifiers) cond_expr2.c:6:11: expected void volatile *extern [addressable] [toplevel] q cond_expr2.c:6:11: got void const volatile * cond_expr2.c:8:11: warning: incorrect type in assignment (different modifiers) cond_expr2.c:8:11: expected int volatile *extern [addressable] [toplevel] [assigned] r cond_expr2.c:8:11: got int const volatile * * check-error-end */ sparse-0.6.1/validation/cond_expr3.c000066400000000000000000000011561355073133200173620ustar00rootroot00000000000000static 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.1/validation/conditional-type.c000066400000000000000000000036301355073133200205770ustar00rootroot00000000000000extern 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.1/validation/constant-suffix-32.c000066400000000000000000000005761355073133200207000ustar00rootroot00000000000000#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.1/validation/constant-suffix-64.c000066400000000000000000000006171355073133200207010ustar00rootroot00000000000000#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.1/validation/constexpr-addr-of-static-member.c000066400000000000000000000007211355073133200234040ustar00rootroot00000000000000struct 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.1/validation/constexpr-addr-of-static.c000066400000000000000000000015171355073133200221430ustar00rootroot00000000000000static 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.1/validation/constexpr-binop.c000066400000000000000000000023461355073133200204520ustar00rootroot00000000000000static 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.1/validation/constexpr-cast.c000066400000000000000000000012451355073133200202720ustar00rootroot00000000000000static 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.1/validation/constexpr-compound-literal.c000066400000000000000000000010131355073133200226070ustar00rootroot00000000000000static 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.1/validation/constexpr-conditional.c000066400000000000000000000024611355073133200216440ustar00rootroot00000000000000static 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.1/validation/constexpr-constcond.c000066400000000000000000000002151355073133200213260ustar00rootroot00000000000000extern int var; static int a[] = { [0 ? var : 1] = 0, [1 ? 2 : var] = 0, }; /* * check-name: constexprness in constant conditionals */ sparse-0.6.1/validation/constexpr-init.c000066400000000000000000000041261355073133200203040ustar00rootroot00000000000000static 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.1/validation/constexpr-labelref.c000066400000000000000000000003241355073133200211110ustar00rootroot00000000000000static 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.1/validation/constexpr-offsetof.c000066400000000000000000000006521355073133200211540ustar00rootroot00000000000000struct 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.1/validation/constexpr-pointer-arith.c000066400000000000000000000016561355073133200221330ustar00rootroot00000000000000static 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.1/validation/constexpr-pointer-cast.c000066400000000000000000000005161355073133200217500ustar00rootroot00000000000000static 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.1/validation/constexpr-preop.c000066400000000000000000000020641355073133200204650ustar00rootroot00000000000000static 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.1/validation/constexpr-pure-builtin.c000066400000000000000000000007331355073133200217600ustar00rootroot00000000000000// 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.1/validation/constexpr-shift.c000066400000000000000000000004031355073133200204500ustar00rootroot00000000000000#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.1/validation/constexpr-string.c000066400000000000000000000002641355073133200206460ustar00rootroot00000000000000static 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.1/validation/constexpr-types-compatible-p.c000066400000000000000000000002571355073133200230600ustar00rootroot00000000000000static 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.1/validation/context-stmt.c000066400000000000000000000044301355073133200177650ustar00rootroot00000000000000static 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.1/validation/context.c000066400000000000000000000106501355073133200170010ustar00rootroot00000000000000#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.1/validation/crash-add-doms.c000066400000000000000000000003671355073133200201070ustar00rootroot00000000000000char 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.1/validation/crash-bb_target.c000066400000000000000000000002121355073133200203350ustar00rootroot00000000000000a() { &&b /* * check-name: crash bb_target * check-command: test-linearize $file * * check-error-ignore * check-output-ignore */ sparse-0.6.1/validation/crash-ep-active.c000066400000000000000000000002501355073133200202630ustar00rootroot00000000000000int 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.1/validation/crash-ptrlist.c000066400000000000000000000006411355073133200201130ustar00rootroot00000000000000a; 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.1/validation/crash-rewrite-branch.c000066400000000000000000000004421355073133200213250ustar00rootroot00000000000000void 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.1/validation/crash-select.c000066400000000000000000000002471355073133200176730ustar00rootroot00000000000000struct 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.1/validation/crazy02-not-so.c000066400000000000000000000011261355073133200200220ustar00rootroot00000000000000int 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.1/validation/crazy03.c000066400000000000000000000005451355073133200166120ustar00rootroot00000000000000extern 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.1/validation/declaration-after-statement-ansi.c000066400000000000000000000004011355073133200236240ustar00rootroot00000000000000static 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.1/validation/declaration-after-statement-c89.c000066400000000000000000000004021355073133200232760ustar00rootroot00000000000000static 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.1/validation/declaration-after-statement-c99.c000066400000000000000000000002151355073133200233010ustar00rootroot00000000000000static void func (int i) { i; int j = i; } /* * check-name: declaration after statement (C99) * check-command: sparse -std=c99 $file */ sparse-0.6.1/validation/declaration-after-statement-default.c000066400000000000000000000002101355073133200243140ustar00rootroot00000000000000static void func (int i) { i; int j = i; } /* * check-name: declaration after statement (default) * check-command: sparse $file */ sparse-0.6.1/validation/definitions.c000066400000000000000000000002311355073133200176220ustar00rootroot00000000000000static inline int f(void); static int g(void) { return f(); } static inline int f(void) { return 0; } /* * check-name: finding definitions */ sparse-0.6.1/validation/designated-init.c000066400000000000000000000161261355073133200203710ustar00rootroot00000000000000struct 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.1/validation/discarded-label-statement.c000066400000000000000000000005461355073133200223210ustar00rootroot00000000000000/* * Verify that the statement following an unused label * is not discarded with the label. */ static int bad(int a, int b) { int r = 0; start: r += a; r += b; return r; } /* * check-name: discarded-label-statement * check-command: test-linearize $file * * check-output-ignore * check-output-contains: add * check-output-contains: %arg1 */ sparse-0.6.1/validation/div.c000066400000000000000000000016541355073133200161030ustar00rootroot00000000000000#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.1/validation/doc/000077500000000000000000000000001355073133200157145ustar00rootroot00000000000000sparse-0.6.1/validation/doc/cdoc.cdoc000066400000000000000000000061431355073133200174620ustar00rootroot00000000000000/// // 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.1/validation/double-semicolon.c000066400000000000000000000002631355073133200205540ustar00rootroot00000000000000extern 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.1/validation/dubious-bitwise-with-not.c000066400000000000000000000015751355073133200222100ustar00rootroot00000000000000static 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.1/validation/empty-expr.c000066400000000000000000000005451355073133200174310ustar00rootroot00000000000000static 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.1/validation/empty-file000066400000000000000000000000001355073133200171330ustar00rootroot00000000000000sparse-0.6.1/validation/endian-big.c000066400000000000000000000005151355073133200173110ustar00rootroot00000000000000#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.1/validation/endian-little.c000066400000000000000000000005261355073133200200470ustar00rootroot00000000000000#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.1/validation/enum+mode.c000066400000000000000000000006061355073133200172010ustar00rootroot00000000000000enum 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.1/validation/enum-base-type.c000066400000000000000000000006311355073133200201460ustar00rootroot00000000000000enum 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.1/validation/enum-bitwise-bad.c000066400000000000000000000007361355073133200204550ustar00rootroot00000000000000#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.1/validation/enum-bitwise-mixed.c000066400000000000000000000011031355073133200210220ustar00rootroot00000000000000#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.1/validation/enum-bitwise.c000066400000000000000000000006641355073133200177310ustar00rootroot00000000000000#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.1/validation/enum-bounds.c000066400000000000000000000007661355073133200175600ustar00rootroot00000000000000enum 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.1/validation/enum-init-constness.c000066400000000000000000000001411355073133200212310ustar00rootroot00000000000000extern int invalid; enum e { E = 1 ? 1 : invalid }; /* * check-name: enum-init-constness */ sparse-0.6.1/validation/enum-invalid.c000066400000000000000000000003251355073133200177030ustar00rootroot00000000000000enum 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.1/validation/enum-min-size.c000066400000000000000000000013251355073133200200110ustar00rootroot00000000000000enum 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.1/validation/enum-mismatch.c000066400000000000000000000005561355073133200200700ustar00rootroot00000000000000enum 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.1/validation/enum-same-type.c000066400000000000000000000004471355073133200201660ustar00rootroot00000000000000enum 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.1/validation/enum-sign-extend.c000066400000000000000000000003241355073133200205010ustar00rootroot00000000000000enum 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.1/validation/enum-sign-gcc.c000066400000000000000000000036761355073133200177630ustar00rootroot00000000000000// 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.1/validation/enum-typecheck.c000066400000000000000000000017201355073133200202340ustar00rootroot00000000000000enum 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.1/validation/enum_scope.c000066400000000000000000000002301355073133200174430ustar00rootroot00000000000000enum {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.1/validation/error-at-eof.c000066400000000000000000000002771355073133200176230ustar00rootroot00000000000000/* * 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.1/validation/escapes.c000066400000000000000000000023531355073133200167410ustar00rootroot00000000000000static 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.1/validation/eval-bad-assign1.c000066400000000000000000000005201355073133200203260ustar00rootroot00000000000000static 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.1/validation/eval-bad-assign2.c000066400000000000000000000005401355073133200203310ustar00rootroot00000000000000struct 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.1/validation/eval-typeof-vla.c000066400000000000000000000005061355073133200203270ustar00rootroot00000000000000extern 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.1/validation/eval/000077500000000000000000000000001355073133200160765ustar00rootroot00000000000000sparse-0.6.1/validation/eval/asm-degen.c000066400000000000000000000007501355073133200201040ustar00rootroot00000000000000#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.1/validation/eval/asm-memop.c000066400000000000000000000012121355073133200201310ustar00rootroot00000000000000extern 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.1/validation/expand/000077500000000000000000000000001355073133200164265ustar00rootroot00000000000000sparse-0.6.1/validation/expand/asm0.c000066400000000000000000000004641355073133200174360ustar00rootroot00000000000000static 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.1/validation/expand/bad-shift.c000066400000000000000000000017021355073133200204330ustar00rootroot00000000000000#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:18: warning: shift too big (32) for type int expand/bad-shift.c:10:18: warning: shift count is negative (-1) expand/bad-shift.c:15:18: warning: shift too big (32) for type int expand/bad-shift.c:20:18: warning: shift count is negative (-1) * check-error-end */ sparse-0.6.1/validation/expand/builtin-expect.c000066400000000000000000000032621355073133200215310ustar00rootroot00000000000000int 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.1/validation/expand/builtin_fpclassify.c000066400000000000000000000010351355073133200224620ustar00rootroot00000000000000enum { 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.1/validation/expand/builtin_huge_val.c000066400000000000000000000010421355073133200221070ustar00rootroot00000000000000static 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.1/validation/expand/builtin_isinf.c000066400000000000000000000005621355073133200214330ustar00rootroot00000000000000int 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.1/validation/expand/builtin_isnan.c000066400000000000000000000005731355073133200214350ustar00rootroot00000000000000int 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.1/validation/expand/builtin_isnormal.c000066400000000000000000000005321355073133200221440ustar00rootroot00000000000000int 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.1/validation/expand/builtin_nan.c000066400000000000000000000005351355073133200210770ustar00rootroot00000000000000static 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.1/validation/expand/compound-literal.c000066400000000000000000000005371355073133200220550ustar00rootroot00000000000000#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.1/validation/expand/function-pointer.c000066400000000000000000000004571355073133200221030ustar00rootroot00000000000000struct 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.1/validation/extern-array.c000066400000000000000000000003711355073133200177350ustar00rootroot00000000000000extern 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.1/validation/extern-inline.c000066400000000000000000000006071355073133200200770ustar00rootroot00000000000000extern __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.1/validation/fdiag-prefix.c000066400000000000000000000003751355073133200176650ustar00rootroot00000000000000int 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.1/validation/field-overlap.c000066400000000000000000000003351355073133200200450ustar00rootroot00000000000000static 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.1/validation/field-override.c000066400000000000000000000052131355073133200202140ustar00rootroot00000000000000static 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.1/validation/fored_arg.c000066400000000000000000000003731355073133200172460ustar00rootroot00000000000000/* * 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.1/validation/foul-bitwise.c000066400000000000000000000013031355073133200177210ustar00rootroot00000000000000typedef 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.1/validation/fp-ops.c000066400000000000000000000016431355073133200165230ustar00rootroot00000000000000double 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.1/validation/function-pointer-inheritance.c000066400000000000000000000002111355073133200230770ustar00rootroot00000000000000extern int foo(int f(int, void *)); int foo(int (*f)(int, void *)) { return 0; } /* * check-name: Function pointer inheritance */ sparse-0.6.1/validation/function-pointer-type.c000066400000000000000000000006431355073133200216000ustar00rootroot00000000000000extern 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.1/validation/function-redecl.c000066400000000000000000000051501355073133200203750ustar00rootroot00000000000000#define __user __attribute__((address_space(1))) #define NULL ((void*)0) int ret_type(void); void ret_type(void) { } /* check-should-fail */ int ret_const(void); int const ret_const(void) { return 0; } /* check-should-fail */ void *ret_as(void); void __user *ret_as(void) { return NULL; } /* check-should-fail */ void *ret_mod(void); void const *ret_mod(void) { return NULL; } /* check-should-fail */ void arg_type(int a); void arg_type(void *a) { } /* check-should-fail */ void arg_const(int a); void arg_const(const int a) { } /* OK */ void arg_as(void *a); void arg_as(void __user *a) { } /* check-should-fail */ void arg_mod(void *); void arg_mod(void const *a) { } /* check-should-fail */ void arg_more_arg(int a); void arg_more_arg(int a, int b) { } /* check-should-fail */ void arg_less_arg(int a, int b); void arg_less_arg(int a) { } /* check-should-fail */ void arg_vararg(int a); void arg_vararg(int a, ...) { } /* check-should-fail */ /* * check-name: function-redecl * * check-error-start function-redecl.c:5:6: error: symbol 'ret_type' redeclared with different type (originally declared at function-redecl.c:4) - different base types function-redecl.c:9:11: error: symbol 'ret_const' redeclared with different type (originally declared at function-redecl.c:8) - different modifiers function-redecl.c:13:13: error: symbol 'ret_as' redeclared with different type (originally declared at function-redecl.c:12) - different address spaces function-redecl.c:17:12: error: symbol 'ret_mod' redeclared with different type (originally declared at function-redecl.c:16) - different modifiers function-redecl.c:21:6: error: symbol 'arg_type' redeclared with different type (originally declared at function-redecl.c:20) - incompatible argument 1 (different base types) function-redecl.c:29:6: error: symbol 'arg_as' redeclared with different type (originally declared at function-redecl.c:28) - incompatible argument 1 (different address spaces) function-redecl.c:33:6: error: symbol 'arg_mod' redeclared with different type (originally declared at function-redecl.c:32) - incompatible argument 1 (different modifiers) function-redecl.c:37:6: error: symbol 'arg_more_arg' redeclared with different type (originally declared at function-redecl.c:36) - different argument counts function-redecl.c:41:6: error: symbol 'arg_less_arg' redeclared with different type (originally declared at function-redecl.c:40) - different argument counts function-redecl.c:45:6: error: symbol 'arg_vararg' redeclared with different type (originally declared at function-redecl.c:44) - incompatible variadic arguments * check-error-end */ sparse-0.6.1/validation/function-redecl2.c000066400000000000000000000006421355073133200204600ustar00rootroot00000000000000extern 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 * * check-known-to-fail * */ sparse-0.6.1/validation/goto-label.c000066400000000000000000000004601355073133200173400ustar00rootroot00000000000000void 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.1/validation/goto-reserved.c000066400000000000000000000003101355073133200200720ustar00rootroot00000000000000static 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.1/validation/identifier_list.c000066400000000000000000000010431355073133200204660ustar00rootroot00000000000000typedef 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.1/validation/implicit-KR-arg-type0.c000066400000000000000000000004431355073133200212460ustar00rootroot00000000000000int 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.1/validation/implicit-KR-arg-type1.c000066400000000000000000000005471355073133200212540ustar00rootroot00000000000000int foo(a, b) int a; { if (b) 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:12: error: missing type declaration for parameter 'b' * check-error-end */ sparse-0.6.1/validation/implicit-ret-type.c000066400000000000000000000006131355073133200206740ustar00rootroot00000000000000fun(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.1/validation/implicit-type.c000066400000000000000000000004561355073133200201110ustar00rootroot00000000000000extern 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.1/validation/inc-dec-float.c000066400000000000000000000005471355073133200177260ustar00rootroot00000000000000double 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.1/validation/include-eval.c000066400000000000000000000001571355073133200176660ustar00rootroot00000000000000/* nothing */ /* * check-name: include-eval.c * check-command: sparse -include ./include-eval.inc $file */ sparse-0.6.1/validation/include-eval.inc000066400000000000000000000002311355073133200202060ustar00rootroot00000000000000typedef 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.1/validation/incomplete-struct.c000066400000000000000000000006401355073133200207740ustar00rootroot00000000000000struct 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.1/validation/infinite-loop0.c000066400000000000000000000002511355073133200201450ustar00rootroot00000000000000void 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.1/validation/infinite-loop01.c000066400000000000000000000006641355073133200202360ustar00rootroot00000000000000void 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.1/validation/infinite-loop02.c000066400000000000000000000002301355073133200202240ustar00rootroot00000000000000void foo(void) { int a = 1; while ((a = !a)) ; } /* * check-name: infinite loop 02 * check-command: sparse -Wno-decl $file * check-timeout: */ sparse-0.6.1/validation/infinite-loop03.c000066400000000000000000000003351355073133200202330ustar00rootroot00000000000000static 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.1/validation/infinite-loop04.c000066400000000000000000000003301355073133200202270ustar00rootroot00000000000000extern 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.1/validation/init-char-array.c000066400000000000000000000006401355073133200203050ustar00rootroot00000000000000/* * 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.1/validation/init-char-array1.c000066400000000000000000000015071355073133200203710ustar00rootroot00000000000000/* * 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.1/validation/init_cstring.c000066400000000000000000000004401355073133200200050ustar00rootroot00000000000000static struct alpha { char a[2]; } x = { .a = "ab" }; /* * check-name: -Winit-cstring option * * check-command: sparse -Winit-cstring $file * check-error-start init_cstring.c:3:14: warning: too long initializer-string for array of char(no space for nul char) * check-error-end */ sparse-0.6.1/validation/initializer-entry-defined-twice.c000066400000000000000000000024341355073133200235050ustar00rootroot00000000000000/* 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.1/validation/inline_compound_literals.c000066400000000000000000000003131355073133200223710ustar00rootroot00000000000000struct 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.1/validation/int128.c000066400000000000000000000036371355073133200163510ustar00rootroot00000000000000typedef __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.1/validation/integer-const-expr.c000066400000000000000000000040621355073133200210520ustar00rootroot00000000000000extern 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.1/validation/integer-promotions.c000066400000000000000000000001631355073133200211570ustar00rootroot00000000000000static int add_char(void) { return (char) 127 + (char) 127 + (char) 2; } /* * check-name: Integer promotions */ sparse-0.6.1/validation/ioc-typecheck.c000066400000000000000000000003101355073133200200340ustar00rootroot00000000000000extern 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.1/validation/kill-computedgoto.c000066400000000000000000000003431355073133200207550ustar00rootroot00000000000000void 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.1/validation/kill-cse.c000066400000000000000000000005331355073133200170170ustar00rootroot00000000000000int 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.1/validation/kill-insert-branch.c000066400000000000000000000003751355073133200210100ustar00rootroot00000000000000void 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.1/validation/kill-load.c000066400000000000000000000006541355073133200171700ustar00rootroot00000000000000int 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.1/validation/kill-phi-node.c000066400000000000000000000005511355073133200177500ustar00rootroot00000000000000void 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.1/validation/kill-phi-ttsbb.c000066400000000000000000000006371355073133200201460ustar00rootroot00000000000000int 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.1/validation/kill-phi-ttsbb2.c000066400000000000000000000012621355073133200202230ustar00rootroot00000000000000extern 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.1/validation/kill-phisrc.c000066400000000000000000000004041355073133200175320ustar00rootroot00000000000000int 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.1/validation/kill-pure-call.c000066400000000000000000000006641355073133200201360ustar00rootroot00000000000000int 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.1/validation/kill-replaced-insn.c000066400000000000000000000016661355073133200210010ustar00rootroot00000000000000// 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.1/validation/linear/asm-toplevel.c000066400000000000000000000003031355073133200211710ustar00rootroot00000000000000__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.1/validation/linear/bitfield-expand-deref.c000066400000000000000000000005661355073133200227160ustar00rootroot00000000000000struct 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.1/validation/linear/bitfield-inc.c000066400000000000000000000003151355073133200211150ustar00rootroot00000000000000struct 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.1/validation/linear/bitfield-init-mask.c000066400000000000000000000010011355073133200222310ustar00rootroot00000000000000struct 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.1/validation/linear/bitfield-preinc.c000066400000000000000000000004331355073133200216250ustar00rootroot00000000000000struct 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.1/validation/linear/bitfield-size.c000066400000000000000000000066001355073133200213210ustar00rootroot00000000000000struct 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 { 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 zext.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] zext.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 zext.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] zext.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.1/validation/linear/bitfield-store.c000066400000000000000000000006471355073133200215100ustar00rootroot00000000000000int 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.1/validation/linear/bool-cast-lp32.c000066400000000000000000000010021355073133200212170ustar00rootroot00000000000000extern 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-known-to-fail * * check-output-ignore * check-output-excludes: ptrtu\\. */ sparse-0.6.1/validation/linear/bool-cast-lp64.c000066400000000000000000000010201355073133200212240ustar00rootroot00000000000000extern 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.1/validation/linear/bool-cast.c000066400000000000000000000023731355073133200204550ustar00rootroot00000000000000extern 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.1/validation/linear/builtin_unreachable.c000066400000000000000000000006641355073133200225720ustar00rootroot00000000000000void function_that_never_returns(void); int foo(int c) { if (c) return 1; function_that_never_returns(); __builtin_unreachable(); } /* * check-name: __builtin_unreachable() * check-command: test-linearize -Wno-decl $file * * check-known-to-fail * check-output-start foo: .L0: cbr %arg1, .L3, .L2 .L2: call function_that_never_returns unreach .L3: ret.32 $1 * check-output-end */ sparse-0.6.1/validation/linear/call-basic.c000066400000000000000000000011771355073133200205650ustar00rootroot00000000000000extern 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.1/validation/linear/call-builtin.c000066400000000000000000000010411355073133200211400ustar00rootroot00000000000000typedef 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.1/validation/linear/call-casted-pointer.c000066400000000000000000000010301355073133200224110ustar00rootroot00000000000000typedef 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.1/validation/linear/call-complex-pointer.c000066400000000000000000000010361355073133200226230ustar00rootroot00000000000000int 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.1/validation/linear/call-direct.c000066400000000000000000000006701355073133200207530ustar00rootroot00000000000000extern 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.1/validation/linear/call-indirect.c000066400000000000000000000007411355073133200213010ustar00rootroot00000000000000int 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.1/validation/linear/call-inline.c000066400000000000000000000007601355073133200207570ustar00rootroot00000000000000static 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: call * check-output-pattern(5): ret\\..* \\$42 */ sparse-0.6.1/validation/linear/cast-constant-to-float.c000066400000000000000000000010231355073133200230650ustar00rootroot00000000000000typedef 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.1/validation/linear/cast-constants.c000066400000000000000000000134611355073133200215360ustar00rootroot00000000000000typedef 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.1/validation/linear/cast-volatile.c000066400000000000000000000004411355073133200213330ustar00rootroot00000000000000static 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.1/validation/linear/compound-literal00.c000066400000000000000000000004511355073133200222030ustar00rootroot00000000000000struct 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.1/validation/linear/compound-literal01.c000066400000000000000000000004721355073133200222070ustar00rootroot00000000000000struct 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.1/validation/linear/compound-literal02.c000066400000000000000000000005151355073133200222060ustar00rootroot00000000000000struct 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.1/validation/linear/degen-array.c000066400000000000000000000006311355073133200207630ustar00rootroot00000000000000extern 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.1/validation/linear/degen-function.c000066400000000000000000000014561355073133200215000ustar00rootroot00000000000000extern 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.1/validation/linear/degen-log-not.c000066400000000000000000000010351355073133200212230ustar00rootroot00000000000000extern 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.1/validation/linear/deref-ptr-ptr.c000066400000000000000000000007521355073133200212640ustar00rootroot00000000000000char *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.1/validation/linear/fp-vs-ptrcast.c000066400000000000000000000003261355073133200212770ustar00rootroot00000000000000float *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.1/validation/linear/fp2i-cast.c000066400000000000000000000013421355073133200203550ustar00rootroot00000000000000#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.1/validation/linear/logical-phi0.c000066400000000000000000000010741355073133200210370ustar00rootroot00000000000000int 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.1/validation/linear/logical.c000066400000000000000000000120171355073133200202000ustar00rootroot00000000000000struct 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.1/validation/linear/missing-insn-size.c000066400000000000000000000006611355073133200221560ustar00rootroot00000000000000int 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.1/validation/linear/missing-return0.c000066400000000000000000000002121355073133200216260ustar00rootroot00000000000000static int foo(int a) { if (a) return 1; } /* * check-name: missing-return0 * check-command: sparse -vir -flinearize=last $file */ sparse-0.6.1/validation/linear/missing-return1.c000066400000000000000000000002741355073133200216370ustar00rootroot00000000000000static 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.1/validation/linear/missing-return2.c000066400000000000000000000002271355073133200216360ustar00rootroot00000000000000static int foo(int a) { switch (a) case 3: return 4; } /* * check-name: missing-return2 * check-command: sparse -vir -flinearize=last $file */ sparse-0.6.1/validation/linear/missing-return3.c000066400000000000000000000004201355073133200216320ustar00rootroot00000000000000static 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.1/validation/linear/missing-return4.c000066400000000000000000000003321355073133200216350ustar00rootroot00000000000000static 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.1/validation/linear/missing-return5.c000066400000000000000000000006101355073133200216350ustar00rootroot00000000000000int 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.1/validation/linear/non-const-case.c000066400000000000000000000006411355073133200214150ustar00rootroot00000000000000static 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.1/validation/linear/phi-order01.c000066400000000000000000000003051355073133200206150ustar00rootroot00000000000000int 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.1/validation/linear/phi-order02.c000066400000000000000000000007211355073133200206200ustar00rootroot00000000000000int 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.1/validation/linear/phi-order03.c000066400000000000000000000002411355073133200206160ustar00rootroot00000000000000int fun(void); static int foo(void) { return ((0 || fun()) && fun()); } /* * check-name: phi-order03 * check-command: sparse -vir -flinearize=last $file */ sparse-0.6.1/validation/linear/phi-order04.c000066400000000000000000000002241355073133200206200ustar00rootroot00000000000000static void foo(int *b) { if (1) { int c; b = &c; } } /* * check-name: phi-order04 * check-command: sparse -vir -flinearize=last $file */ sparse-0.6.1/validation/linear/range-op.c000066400000000000000000000005671355073133200203050ustar00rootroot00000000000000static 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.1/validation/linear/struct-init-full.c000066400000000000000000000007001355073133200220070ustar00rootroot00000000000000struct 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.1/validation/linear/struct-init-partial.c000066400000000000000000000011041355073133200225000ustar00rootroot00000000000000struct 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.1/validation/linear/unexamined-base-type.c000066400000000000000000000015501355073133200226120ustar00rootroot00000000000000# 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.1/validation/linear/unreachable-label0.c000066400000000000000000000004061355073133200221730ustar00rootroot00000000000000static int foo(int a) { goto label; switch(a) { default: label: break; } return 0; } /* * check-name: unreachable-label0 * check-command: test-linearize $file * * check-output-ignore * check-output-contains: ret\\. * check-output-excludes: END */ sparse-0.6.1/validation/local-label.c000066400000000000000000000003261355073133200174630ustar00rootroot00000000000000void 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.1/validation/logical.c000066400000000000000000000003131355073133200167220ustar00rootroot00000000000000extern 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.1/validation/mem2reg/000077500000000000000000000000001355073133200165055ustar00rootroot00000000000000sparse-0.6.1/validation/mem2reg/address-used00.c000066400000000000000000000004131355073133200213720ustar00rootroot00000000000000int 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.1/validation/mem2reg/alias-distinct.c000066400000000000000000000003561355073133200215650ustar00rootroot00000000000000extern 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.1/validation/mem2reg/alias-mixed.c000066400000000000000000000005201355073133200210430ustar00rootroot00000000000000extern 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.1/validation/mem2reg/alias-same.c000066400000000000000000000003351355073133200206660ustar00rootroot00000000000000extern 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.1/validation/mem2reg/broken-phi02.c000066400000000000000000000007461355073133200210600ustar00rootroot00000000000000int 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.1/validation/mem2reg/broken-phi03.c000066400000000000000000000010401355073133200210450ustar00rootroot00000000000000int 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.1/validation/mem2reg/cond-expr.c000066400000000000000000000004211355073133200205450ustar00rootroot00000000000000int 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(2): phi\\. * check-output-pattern(3): phisrc\\. */ sparse-0.6.1/validation/mem2reg/cond-expr5.c000066400000000000000000000006071355073133200206400ustar00rootroot00000000000000int 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-excludes: phi\\..*, .*, .* * check-output-pattern(3): phi\\. * check-output-pattern(5): phisrc\\. */ sparse-0.6.1/validation/mem2reg/dead-phisrc.c000066400000000000000000000003311355073133200210310ustar00rootroot00000000000000static 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.1/validation/mem2reg/global-direct-undef.c000066400000000000000000000005101355073133200224540ustar00rootroot00000000000000int 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.1/validation/mem2reg/global-direct.c000066400000000000000000000005061355073133200213620ustar00rootroot00000000000000int 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.1/validation/mem2reg/global-loop.c000066400000000000000000000004511355073133200210600ustar00rootroot00000000000000struct 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.1/validation/mem2reg/global-noalias.c000066400000000000000000000004651355073133200215420ustar00rootroot00000000000000int 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.1/validation/mem2reg/global-pointer.c000066400000000000000000000005761355073133200215770ustar00rootroot00000000000000int 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.1/validation/mem2reg/if-direct.c000066400000000000000000000004361355073133200205220ustar00rootroot00000000000000int 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.1/validation/mem2reg/if-pointer.c000066400000000000000000000005511355073133200207260ustar00rootroot00000000000000int 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.1/validation/mem2reg/init-global-array.c000066400000000000000000000004771355073133200221760ustar00rootroot00000000000000struct 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.1/validation/mem2reg/init-local-array.c000066400000000000000000000005751355073133200220270ustar00rootroot00000000000000static 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.1/validation/mem2reg/init-local-union0.c000066400000000000000000000004501355073133200221110ustar00rootroot00000000000000double 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.1/validation/mem2reg/init-local-union1.c000066400000000000000000000007161355073133200221170ustar00rootroot00000000000000double 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.1/validation/mem2reg/init-local32.c000066400000000000000000000005401355073133200210500ustar00rootroot00000000000000int 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.1/validation/mem2reg/init-local64.c000066400000000000000000000005401355073133200210550ustar00rootroot00000000000000int 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.1/validation/mem2reg/load-dead.c000066400000000000000000000003621355073133200204640ustar00rootroot00000000000000int 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.1/validation/mem2reg/load-deadborn.c000066400000000000000000000001151355073133200213410ustar00rootroot00000000000000static void foo(int a) { return; a; } /* * check-name: load-deadborn */ sparse-0.6.1/validation/mem2reg/loop00.c000066400000000000000000000004221355073133200177600ustar00rootroot00000000000000int 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.1/validation/mem2reg/loop01-global.c000066400000000000000000000004731355073133200212250ustar00rootroot00000000000000extern 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.1/validation/mem2reg/loop02-array.c000066400000000000000000000005101355073133200210740ustar00rootroot00000000000000 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.1/validation/mem2reg/loop02-global.c000066400000000000000000000004001355073133200212140ustar00rootroot00000000000000int 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.1/validation/mem2reg/loop02-local.c000066400000000000000000000004241355073133200210540ustar00rootroot00000000000000 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.1/validation/mem2reg/loop02-pointer.c000066400000000000000000000005031355073133200214400ustar00rootroot00000000000000 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.1/validation/mem2reg/missing-return.c000066400000000000000000000006221355073133200216370ustar00rootroot00000000000000int 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.1/validation/mem2reg/quadra00.c000066400000000000000000000006541355073133200202730ustar00rootroot00000000000000#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.1/validation/mem2reg/quadra01.c000066400000000000000000000010601355073133200202640ustar00rootroot00000000000000#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.1/validation/mem2reg/quadra02.c000066400000000000000000000005031355073133200202660ustar00rootroot00000000000000#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.1/validation/mem2reg/reload-aliasing.c000066400000000000000000000010321355073133200217000ustar00rootroot00000000000000extern 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.1/validation/mem2reg/short-load.c000066400000000000000000000006221355073133200207250ustar00rootroot00000000000000#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.1/validation/mem2reg/store-deadborn.c000066400000000000000000000001221355073133200215540ustar00rootroot00000000000000static void foo(int a) { return; a = 0; } /* * check-name: store-deadborn */ sparse-0.6.1/validation/mem2reg/stray-phisrc.c000066400000000000000000000005071355073133200213030ustar00rootroot00000000000000static 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.1/validation/mem2reg/struct.c000066400000000000000000000004641355073133200202010ustar00rootroot00000000000000struct 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.1/validation/mem2reg/undef00.c000066400000000000000000000005711355073133200201150ustar00rootroot00000000000000static 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.1/validation/mem2reg/undef01.c000066400000000000000000000004121355073133200201100ustar00rootroot00000000000000static 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.1/validation/mem2reg/unused-var.c000066400000000000000000000003661355073133200207470ustar00rootroot00000000000000int 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.1/validation/mem2reg/volatile-store00.c000066400000000000000000000005171355073133200217650ustar00rootroot00000000000000void 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.1/validation/member_of_typeof.c000066400000000000000000000003361355073133200206360ustar00rootroot00000000000000static 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.1/validation/memops-volatile.c000066400000000000000000000005261355073133200204330ustar00rootroot00000000000000static 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.1/validation/missing-ident.c000066400000000000000000000007251355073133200200710ustar00rootroot00000000000000int [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.1/validation/missing-return.c000066400000000000000000000005341355073133200203030ustar00rootroot00000000000000int 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.1/validation/multi-input.c000066400000000000000000000002601355073133200176000ustar00rootroot00000000000000int 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.1/validation/multi_typedef.c000066400000000000000000000003611355073133200201650ustar00rootroot00000000000000typedef 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.1/validation/nested-declarator.c000066400000000000000000000014531355073133200207160ustar00rootroot00000000000000typedef 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.1/validation/nested-declarator2.c000066400000000000000000000023301355073133200207730ustar00rootroot00000000000000typedef 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.1/validation/nocast.c000066400000000000000000000120051355073133200166000ustar00rootroot00000000000000#define __nocast __attribute__((nocast)) typedef unsigned long __nocast ulong_nc_t; extern void use_val(ulong_nc_t); extern void use_ptr(ulong_nc_t *); /* use address */ static void good_use_address(void) { ulong_nc_t t; use_ptr(&t); } static ulong_nc_t *good_ret_address(void) { static ulong_nc_t t; return &t; } static ulong_nc_t good_deref(ulong_nc_t *t) { return *t; } /* assign value */ static ulong_nc_t t; static ulong_nc_t good_assign_self = t; static unsigned long good_assign_sametype = t; /* assign pointer */ static ulong_nc_t *good_ptr = &t; static ulong_nc_t *bad_ptr_to = 1UL; static unsigned long *bad_ptr_from = &t; /* arithmetic operation */ static ulong_nc_t good_arith(ulong_nc_t t, unsigned int n) { return t + n; } /* implicit cast to other types */ static unsigned long good_ret_samecast(ulong_nc_t t) { return t; } static unsigned long long bad_ret_biggercast(ulong_nc_t t) { return t; } static long bad_ret_signcast(ulong_nc_t t) { return t; } static short bad_ret_smallercast(ulong_nc_t t) { return t; } static void assign_val(ulong_nc_t t) { ulong_nc_t good_c = t; unsigned long good_ul = t; unsigned long long bad_ull = t; long bad_l = t; short bad_i = t; } static void assign_via_ptr(ulong_nc_t *t) { ulong_nc_t good_c = *t; unsigned long good_ul = *t; unsigned long long bad_ull = *t; long bad_l = *t; short bad_i = *t; } static void assign_ptr(ulong_nc_t *t) { ulong_nc_t *good_same_type = t; unsigned long *bad_mod = t; unsigned long long __nocast *bad_size = t; short __nocast *bad_i = t; long __nocast *bad_l = t; } /* implicit cast to nocast */ static void implicit_assign_to(void) { ulong_nc_t t; unsigned long ul = 1; unsigned short us = 1; unsigned long long ull = 1; long l = 1; t = ul; /* implicit to nocast from same type: OK? */ t = us; t = ull; t = l; } static void bad_implicit_arg_to(void) { unsigned long ul = 1; unsigned short us = 1; unsigned long long ull = 1; long l = 1; use_val(ul); /* implicit to nocast from same type: OK? */ use_val(us); use_val(ull); use_val(l); } /* implicit cast from nocast */ static unsigned long good_implicit_ret_ul(ulong_nc_t t) { return t; /* implicit to nocast from same type: OK? */ } static unsigned short bad_implicit_ret_us(ulong_nc_t t) { return t; } static unsigned long long bad_implicit_ret_ull(ulong_nc_t t) { return t; } static long bad_implicit_ret_l(ulong_nc_t t) { return t; } /* FIXME: explicit cast: should we complain? */ static ulong_nc_t good_samecast(ulong_nc_t v) { return (ulong_nc_t) v; } static ulong_nc_t bad_tocast(unsigned long v) { return (ulong_nc_t) v; } static unsigned long bad_fromcast(ulong_nc_t v) { return (unsigned long) v; } /* * check-name: nocast.c * * check-error-start nocast.c:34:33: warning: incorrect type in initializer (different base types) nocast.c:34:33: expected unsigned long [nocast] [usertype] *static [toplevel] bad_ptr_to nocast.c:34:33: got unsigned long nocast.c:34:33: warning: implicit cast to nocast type nocast.c:35:39: warning: incorrect type in initializer (different modifiers) nocast.c:35:39: expected unsigned long *static [toplevel] bad_ptr_from nocast.c:35:39: got unsigned long [nocast] * nocast.c:35:39: warning: implicit cast from nocast type nocast.c:50:16: warning: implicit cast from nocast type nocast.c:54:16: warning: implicit cast from nocast type nocast.c:58:16: warning: implicit cast from nocast type nocast.c:65:38: warning: implicit cast from nocast type nocast.c:66:22: warning: implicit cast from nocast type nocast.c:67:23: warning: implicit cast from nocast type nocast.c:74:38: warning: implicit cast from nocast type nocast.c:75:22: warning: implicit cast from nocast type nocast.c:76:23: warning: implicit cast from nocast type nocast.c:82:34: warning: incorrect type in initializer (different modifiers) nocast.c:82:34: expected unsigned long *bad_mod nocast.c:82:34: got unsigned long [nocast] [usertype] *t nocast.c:82:34: warning: implicit cast from nocast type nocast.c:83:49: warning: incorrect type in initializer (different type sizes) nocast.c:83:49: expected unsigned long long [nocast] *bad_size nocast.c:83:49: got unsigned long [nocast] [usertype] *t nocast.c:83:49: warning: implicit cast to/from nocast type nocast.c:84:33: warning: incorrect type in initializer (different type sizes) nocast.c:84:33: expected short [nocast] *bad_i nocast.c:84:33: got unsigned long [nocast] [usertype] *t nocast.c:84:33: warning: implicit cast to/from nocast type nocast.c:85:32: warning: implicit cast to/from nocast type nocast.c:98:13: warning: implicit cast to nocast type nocast.c:99:13: warning: implicit cast to nocast type nocast.c:100:13: warning: implicit cast to nocast type nocast.c:111:17: warning: implicit cast to nocast type nocast.c:112:17: warning: implicit cast to nocast type nocast.c:113:17: warning: implicit cast to nocast type nocast.c:124:16: warning: implicit cast from nocast type nocast.c:129:16: warning: implicit cast from nocast type nocast.c:134:16: warning: implicit cast from nocast type * check-error-end */ sparse-0.6.1/validation/noderef.c000066400000000000000000000014131355073133200167340ustar00rootroot00000000000000# 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.1/validation/non-pointer-null.c000066400000000000000000000002771355073133200205410ustar00rootroot00000000000000static 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.1/validation/old-initializer-nowarn.c000066400000000000000000000002471355073133200217170ustar00rootroot00000000000000struct 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.1/validation/old-initializer.c000066400000000000000000000003271355073133200204140ustar00rootroot00000000000000struct 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.1/validation/old-style-definition0.c000066400000000000000000000003071355073133200214350ustar00rootroot00000000000000extern 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.1/validation/old-style-definition1.c000066400000000000000000000004721355073133200214410ustar00rootroot00000000000000extern 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.1/validation/optim/000077500000000000000000000000001355073133200162775ustar00rootroot00000000000000sparse-0.6.1/validation/optim/address-used01.c000066400000000000000000000004131355073133200211650ustar00rootroot00000000000000int 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.1/validation/optim/and-extend.c000066400000000000000000000007501355073133200204740ustar00rootroot00000000000000typedef 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.1/validation/optim/and-extendx.c000066400000000000000000000006341355073133200206650ustar00rootroot00000000000000typedef unsigned short u16; typedef short s16; typedef unsigned int u32; typedef int s32; typedef unsigned long long u64; typedef long long s64; u64 ufoo(int x) { return x & 0x7fff; } u64 sfoo(int x) { return x & 0x7fff; } /* * check-name: and-extend * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-contains: and\\.64.*0x7fff */ sparse-0.6.1/validation/optim/and-lsr.c000066400000000000000000000004471355073133200200100ustar00rootroot00000000000000// (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.1/validation/optim/and-or-bf0.c000066400000000000000000000004721355073133200202730ustar00rootroot00000000000000struct 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.1/validation/optim/and-or-bf1.c000066400000000000000000000004331355073133200202710ustar00rootroot00000000000000struct 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.1/validation/optim/and-or-bf2.c000066400000000000000000000004511355073133200202720ustar00rootroot00000000000000struct 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.1/validation/optim/and-or-bfs.c000066400000000000000000000006441355073133200203770ustar00rootroot00000000000000struct 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.1/validation/optim/and-or-bfu.c000066400000000000000000000005351355073133200204000ustar00rootroot00000000000000struct 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.1/validation/optim/and-or-bfx.c000066400000000000000000000004111355073133200203740ustar00rootroot00000000000000struct 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.1/validation/optim/and-or-constant0.c000066400000000000000000000003101355073133200215240ustar00rootroot00000000000000int 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.1/validation/optim/and-or-constant1.c000066400000000000000000000004241355073133200215330ustar00rootroot00000000000000int 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.1/validation/optim/and-or-constant2.c000066400000000000000000000004011355073133200215270ustar00rootroot00000000000000int 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.1/validation/optim/and-or-crash.c000066400000000000000000000002151355073133200207170ustar00rootroot00000000000000static unsigned a(unsigned b, unsigned c) { (c << 1 | b & 1 << 1) >> 1; } /* * check-name: catch crashes during AND-OR simplifications */ sparse-0.6.1/validation/optim/and-or-lsr0.c000066400000000000000000000003451355073133200205030ustar00rootroot00000000000000int 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.1/validation/optim/and-or-lsr1.c000066400000000000000000000003631355073133200205040ustar00rootroot00000000000000int 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.1/validation/optim/and-or-lsr2.c000066400000000000000000000004131355073133200205010ustar00rootroot00000000000000int 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.1/validation/optim/and-or-lsrx.c000066400000000000000000000004361355073133200206140ustar00rootroot00000000000000unsigned 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.1/validation/optim/and-or-mask.c000066400000000000000000000003471355073133200205600ustar00rootroot00000000000000int 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.1/validation/optim/and-or-mask0.c000066400000000000000000000003211355073133200206300ustar00rootroot00000000000000int 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.1/validation/optim/and-or-mask1.c000066400000000000000000000003661355073133200206420ustar00rootroot00000000000000int 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.1/validation/optim/and-or-mask2.c000066400000000000000000000004141355073133200206350ustar00rootroot00000000000000int 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.1/validation/optim/and-or-mask3s.c000066400000000000000000000007371355073133200210310ustar00rootroot00000000000000#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.1/validation/optim/and-or-mask3u.c000066400000000000000000000007351355073133200210310ustar00rootroot00000000000000#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.1/validation/optim/and-or-mask4.c000066400000000000000000000007341355073133200206440ustar00rootroot00000000000000#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.1/validation/optim/and-or-maskx.c000066400000000000000000000003761355073133200207520ustar00rootroot00000000000000int 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.1/validation/optim/and-or-shl0.c000066400000000000000000000003161355073133200204670ustar00rootroot00000000000000int 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.1/validation/optim/and-or-shl1.c000066400000000000000000000003631355073133200204720ustar00rootroot00000000000000int 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.1/validation/optim/and-or-shl2.c000066400000000000000000000004131355073133200204670ustar00rootroot00000000000000int 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.1/validation/optim/and-or-shlx.c000066400000000000000000000004421355073133200205770ustar00rootroot00000000000000unsigned 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.1/validation/optim/and-or-trunc0.c000066400000000000000000000003461355073133200210370ustar00rootroot00000000000000char 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.1/validation/optim/and-or-trunc1.c000066400000000000000000000003061355073133200210340ustar00rootroot00000000000000char 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.1/validation/optim/and-or-trunc2.c000066400000000000000000000003611355073133200210360ustar00rootroot00000000000000char 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.1/validation/optim/and-or-truncx.c000066400000000000000000000003651355073133200211500ustar00rootroot00000000000000char 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.1/validation/optim/and-trunc.c000066400000000000000000000005631355073133200203420ustar00rootroot00000000000000short 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.1/validation/optim/binops-same-args.c000066400000000000000000000024711355073133200216160ustar00rootroot00000000000000typedef 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.1/validation/optim/bitfield-init-zero.c000066400000000000000000000024551355073133200221510ustar00rootroot00000000000000struct 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.1/validation/optim/bitfield-size.c000066400000000000000000000022721355073133200212000ustar00rootroot00000000000000struct bfu { unsigned int a:4; unsigned int :2; unsigned int b:4; }; unsigned int get__bfu_a(struct bfu bf) { return bf.a; } unsigned int get__bfu_b(struct bfu bf) { return bf.b; } unsigned int get_pbfu_a(struct bfu *bf) { return bf->a; } unsigned int get_pbfu_b(struct bfu *bf) { return bf->b; } struct bfs { signed int a:4; signed int :2; signed int b:4; }; signed int get__bfs_a(struct bfs bf) { return bf.a; } signed int get__bfs_b(struct bfs bf) { return bf.b; } signed int get_pbfs_a(struct bfs *bf) { return bf->a; } signed int get_pbfs_b(struct bfs *bf) { return bf->b; } struct bfi { int a:4; int :2; int b:4; }; unsigned int get__bfi_a(struct bfi bf) { return bf.a; } unsigned int get__bfi_b(struct bfi bf) { return bf.b; } unsigned int get_pbfi_a(struct bfi *bf) { return bf->a; } unsigned int get_pbfi_b(struct bfi *bf) { return bf->b; } /* * check-name: bitfield size * check-command: test-linearize -Wno-decl $file * check-output-ignore * * check-output-excludes: and\\..*\\$960 * check-output-excludes: zext\\. * check-output-pattern(8): and\\..*\\$15 * check-output-pattern(4): sext\\. * check-output-pattern(4): trunc\\.4 * check-output-pattern(6): lsr\\..*\\$6 */ sparse-0.6.1/validation/optim/bitfield-store-load0.c000066400000000000000000000010371355073133200223550ustar00rootroot00000000000000int 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.1/validation/optim/bitfield-store-loads.c000066400000000000000000000006441355073133200224630ustar00rootroot00000000000000struct s { char :2; 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.1/validation/optim/bitfield-store-loadu.c000066400000000000000000000005561355073133200224670ustar00rootroot00000000000000struct 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.1/validation/optim/bits-not-zero.c000066400000000000000000000006561355073133200211660ustar00rootroot00000000000000int 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.1/validation/optim/bool-context-fp.c000066400000000000000000000032651355073133200214710ustar00rootroot00000000000000#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.1/validation/optim/bool-context.c000066400000000000000000000004211355073133200210550ustar00rootroot00000000000000#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.1/validation/optim/bool-eq0.c000066400000000000000000000004341355073133200200620ustar00rootroot00000000000000int 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.1/validation/optim/bool-int-bool.c000066400000000000000000000005041355073133200211160ustar00rootroot00000000000000_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.1/validation/optim/bool-ne0.c000066400000000000000000000004421355073133200200560ustar00rootroot00000000000000int 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.1/validation/optim/bool-neq0.c000066400000000000000000000004441355073133200202410ustar00rootroot00000000000000int 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.1/validation/optim/bool-same-args.c000066400000000000000000000004471355073133200212600ustar00rootroot00000000000000static 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.1/validation/optim/bool-sext-test.c000066400000000000000000000005171355073133200213370ustar00rootroot00000000000000_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.1/validation/optim/bool-simplify.c000066400000000000000000000013761355073133200212370ustar00rootroot00000000000000int 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.1/validation/optim/bool-simplify2.c000066400000000000000000000101741355073133200213150ustar00rootroot00000000000000typedef 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.1/validation/optim/bool-zext-test.c000066400000000000000000000005171355073133200213460ustar00rootroot00000000000000_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.1/validation/optim/call-complex-pointer.c000066400000000000000000000004561355073133200225060ustar00rootroot00000000000000int 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.1/validation/optim/call-inlined.c000066400000000000000000000006071355073133200210010ustar00rootroot00000000000000static 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-start foo: .L0: select.32 %r10 <- %arg3, %arg3, $0 ret.32 %r10 * check-output-end */ sparse-0.6.1/validation/optim/canonical-add.c000066400000000000000000000017001355073133200211160ustar00rootroot00000000000000int 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.1/validation/optim/canonical-cmp.c000066400000000000000000000043451355073133200211550ustar00rootroot00000000000000typedef signed int sint; typedef unsigned int uint; sint seq(sint p, sint a) { return (123 == p) ? a : 0; } sint sne(sint p, sint a) { return (123 != p) ? a : 0; } sint slt(sint p, sint a) { return (123 > p) ? a : 0; } sint sle(sint p, sint a) { return (123 >= p) ? a : 0; } sint sge(sint p, sint a) { return (123 <= p) ? a : 0; } sint sgt(sint p, sint a) { return (123 < p) ? a : 0; } uint ueq(uint p, uint a) { return (123 == p) ? a : 0; } uint une(uint p, uint a) { return (123 != p) ? a : 0; } uint ubt(uint p, uint a) { return (123 > p) ? a : 0; } uint ube(uint p, uint a) { return (123 >= p) ? a : 0; } uint uae(uint p, uint a) { return (123 <= p) ? a : 0; } uint uat(uint p, uint a) { return (123 < p) ? a : 0; } /* * check-name: canonical-cmp * check-command: test-linearize -Wno-decl $file * * check-output-excludes: \\$123, * * check-output-start seq: .L0: seteq.32 %r3 <- %arg1, $123 select.32 %r4 <- %r3, %arg2, $0 ret.32 %r4 sne: .L2: setne.32 %r8 <- %arg1, $123 select.32 %r9 <- %r8, %arg2, $0 ret.32 %r9 slt: .L4: setlt.32 %r13 <- %arg1, $123 select.32 %r14 <- %r13, %arg2, $0 ret.32 %r14 sle: .L6: setle.32 %r18 <- %arg1, $123 select.32 %r19 <- %r18, %arg2, $0 ret.32 %r19 sge: .L8: setge.32 %r23 <- %arg1, $123 select.32 %r24 <- %r23, %arg2, $0 ret.32 %r24 sgt: .L10: setgt.32 %r28 <- %arg1, $123 select.32 %r29 <- %r28, %arg2, $0 ret.32 %r29 ueq: .L12: seteq.32 %r33 <- %arg1, $123 select.32 %r34 <- %r33, %arg2, $0 ret.32 %r34 une: .L14: setne.32 %r38 <- %arg1, $123 select.32 %r39 <- %r38, %arg2, $0 ret.32 %r39 ubt: .L16: setb.32 %r43 <- %arg1, $123 select.32 %r44 <- %r43, %arg2, $0 ret.32 %r44 ube: .L18: setbe.32 %r48 <- %arg1, $123 select.32 %r49 <- %r48, %arg2, $0 ret.32 %r49 uae: .L20: setae.32 %r53 <- %arg1, $123 select.32 %r54 <- %r53, %arg2, $0 ret.32 %r54 uat: .L22: seta.32 %r58 <- %arg1, $123 select.32 %r59 <- %r58, %arg2, $0 ret.32 %r59 * check-output-end */ sparse-0.6.1/validation/optim/canonical-fcmp.c000066400000000000000000000037541355073133200213260ustar00rootroot00000000000000extern 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.1/validation/optim/canonical-mul.c000066400000000000000000000012341355073133200211650ustar00rootroot00000000000000#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) * 1' * 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.1/validation/optim/cast-kinds.c000066400000000000000000000153721355073133200205130ustar00rootroot00000000000000typedef 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.1/validation/optim/cast-nop.c000066400000000000000000000004201355073133200201630ustar00rootroot00000000000000static 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.1/validation/optim/cse-cmp-next.c000066400000000000000000000004051355073133200207450ustar00rootroot00000000000000void 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.1/validation/optim/cse-commutativity.c000066400000000000000000000013761355073133200221400ustar00rootroot00000000000000static 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.1/validation/optim/cse-dual-compare.c000066400000000000000000000026001355073133200215620ustar00rootroot00000000000000static 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.1/validation/optim/cse-fcmp.c000066400000000000000000000003751355073133200201450ustar00rootroot00000000000000extern 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.1/validation/optim/cse-setfval.c000066400000000000000000000003341355073133200206570ustar00rootroot00000000000000int 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.1/validation/optim/cse-size.c000066400000000000000000000003451355073133200201670ustar00rootroot00000000000000static void foo(void) { unsigned short p = 0; int x; for (;;) if (p) p = x; } /* * check-name: cse-size * check-command: test-linearize -Wno-decl $file * * check-output-ignore * check-output-pattern(2): phi\\. */ sparse-0.6.1/validation/optim/double-unop.c000066400000000000000000000005371355073133200207010ustar00rootroot00000000000000typedef 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.1/validation/optim/dup-cond0.c000066400000000000000000000004041355073133200202320ustar00rootroot00000000000000struct 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.1/validation/optim/ext-trunc-greater.c000066400000000000000000000003571355073133200220300ustar00rootroot00000000000000short 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.1/validation/optim/ext-trunc-same.c000066400000000000000000000004621355073133200213210ustar00rootroot00000000000000short 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.1/validation/optim/ext-trunc-smaller.c000066400000000000000000000004221355073133200220270ustar00rootroot00000000000000char 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.1/validation/optim/fpcast-constant.c000066400000000000000000000003651355073133200215560ustar00rootroot00000000000000static 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.1/validation/optim/fpcast-nop.c000066400000000000000000000006361355073133200205220ustar00rootroot00000000000000static 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.1/validation/optim/inline-return.c000066400000000000000000000004741355073133200212430ustar00rootroot00000000000000static 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.1/validation/optim/kill-casts.c000066400000000000000000000007541355073133200205170ustar00rootroot00000000000000extern 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.1/validation/optim/kill-stores0.c000066400000000000000000000006061355073133200207750ustar00rootroot00000000000000struct 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.1/validation/optim/kill-stores1.c000066400000000000000000000012661355073133200210010ustar00rootroot00000000000000struct 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.1/validation/optim/kill-stores2.c000066400000000000000000000003361355073133200207770ustar00rootroot00000000000000extern 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.1/validation/optim/killed-insn.c000066400000000000000000000003021355073133200206470ustar00rootroot00000000000000static 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.1/validation/optim/live-stores0.c000066400000000000000000000006041355073133200207770ustar00rootroot00000000000000void 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.1/validation/optim/load-converted.c000066400000000000000000000003431355073133200213510ustar00rootroot00000000000000static 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.1/validation/optim/load-dead.c000066400000000000000000000003041355073133200202520ustar00rootroot00000000000000void 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.1/validation/optim/load-semi-volatile.c000066400000000000000000000005441355073133200221350ustar00rootroot00000000000000struct 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.1/validation/optim/lsr-and0.c000066400000000000000000000003541355073133200200650ustar00rootroot00000000000000unsigned 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.1/validation/optim/lsr-and1.c000066400000000000000000000007371355073133200200730ustar00rootroot00000000000000// 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.1/validation/optim/lsr-asr.c000066400000000000000000000010171355073133200200250ustar00rootroot00000000000000int 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.1/validation/optim/lsr-shl0.c000066400000000000000000000004161355073133200201100ustar00rootroot00000000000000unsigned 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.1/validation/optim/mask-lsr.c000066400000000000000000000004251355073133200201750ustar00rootroot00000000000000// ((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.1/validation/optim/mask-out.c000066400000000000000000000003421355073133200202020ustar00rootroot00000000000000unsigned 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.1/validation/optim/mask1-setne0.c000066400000000000000000000004751355073133200206610ustar00rootroot00000000000000struct 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.1/validation/optim/missing-select.c000066400000000000000000000004431355073133200213720ustar00rootroot00000000000000static 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.1/validation/optim/muldiv-by-one.c000066400000000000000000000007361355073133200211400ustar00rootroot00000000000000typedef 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.1/validation/optim/muldiv-by-zero.c000066400000000000000000000004151355073133200213300ustar00rootroot00000000000000typedef 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.1/validation/optim/muldiv-minus-one.c000066400000000000000000000007331355073133200216560ustar00rootroot00000000000000typedef 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.1/validation/optim/null-phi.c000066400000000000000000000001151355073133200201700ustar00rootroot00000000000000static int foo(void) { if (0) return 0; } /* * check-name: null-phi */ sparse-0.6.1/validation/optim/or-and-constant1.c000066400000000000000000000006661355073133200215430ustar00rootroot00000000000000unsigned 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.1/validation/optim/phi-ret.c000066400000000000000000000004151355073133200200130ustar00rootroot00000000000000int 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.1/validation/optim/restrict.c000066400000000000000000000016521355073133200203060ustar00rootroot00000000000000extern 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.1/validation/optim/select-zero.c000066400000000000000000000003601355073133200206760ustar00rootroot00000000000000static 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.1/validation/optim/setcc-mask.c000066400000000000000000000003431355073133200204750ustar00rootroot00000000000000int 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.1/validation/optim/setcc-setcc.c000066400000000000000000000012551355073133200206460ustar00rootroot00000000000000static _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.1/validation/optim/setcc-seteq.c000066400000000000000000000005411355073133200206630ustar00rootroot00000000000000static _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.1/validation/optim/setcc-setne.c000066400000000000000000000005441355073133200206630ustar00rootroot00000000000000static _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.1/validation/optim/setne0-sext.c000066400000000000000000000002701355073133200206210ustar00rootroot00000000000000long 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.1/validation/optim/setne0-trunc.c000066400000000000000000000002721355073133200207730ustar00rootroot00000000000000char 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.1/validation/optim/setne0-zext.c000066400000000000000000000003221355073133200206260ustar00rootroot00000000000000unsigned 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.1/validation/optim/sext-sext.c000066400000000000000000000003151355073133200204060ustar00rootroot00000000000000int 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.1/validation/optim/sext.c000066400000000000000000000004201355073133200174220ustar00rootroot00000000000000int sext(int x) { return (x << 5) >> 5; } /* * check-name: sext * check-command: test-linearize -Wno-decl $file * check-known-to-fail * * check-output-ignore * check-output-contains: sext\\.$27 * check-output-excludes: asr\\. * check-output-excludes: shl\\. */ sparse-0.6.1/validation/optim/sh-or-and0.c000066400000000000000000000006471355073133200203220ustar00rootroot00000000000000unsigned 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.1/validation/optim/sh-or-and1.c000066400000000000000000000006511355073133200203160ustar00rootroot00000000000000unsigned 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.1/validation/optim/sh-or-and2.c000066400000000000000000000007511355073133200203200ustar00rootroot00000000000000unsigned 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.1/validation/optim/shift-big.c000066400000000000000000000021431355073133200203170ustar00rootroot00000000000000typedef 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: ret.32 $0 lsr33: .L10: ret.32 $0 shl31: .L12: shl.32 %r20 <- %arg1, $31 ret.32 %r20 shl32: .L14: ret.32 $0 shl33: .L16: ret.32 $0 * check-output-end */ sparse-0.6.1/validation/optim/shift-shift.c000066400000000000000000000031271355073133200206760ustar00rootroot00000000000000unsigned 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.1/validation/optim/shift-zext.c000066400000000000000000000003351355073133200205510ustar00rootroot00000000000000unsigned 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.1/validation/optim/shl-and0.c000066400000000000000000000003541355073133200200530ustar00rootroot00000000000000unsigned 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.1/validation/optim/shl-and1.c000066400000000000000000000007371355073133200200610ustar00rootroot00000000000000// 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.1/validation/optim/shl-lsr0.c000066400000000000000000000004211355073133200201040ustar00rootroot00000000000000unsigned 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.1/validation/optim/store-dominated.c000066400000000000000000000003231355073133200215370ustar00rootroot00000000000000static 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.1/validation/optim/trivial-phis.c000066400000000000000000000003351355073133200210570ustar00rootroot00000000000000void 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.1/validation/optim/trunc-mask-zext.c000066400000000000000000000004171355073133200215210ustar00rootroot00000000000000unsigned 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.1/validation/optim/trunc-or-shl.c000066400000000000000000000003371355073133200210030ustar00rootroot00000000000000char 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-contains: ret\\..*%arg2 */ sparse-0.6.1/validation/optim/trunc-seteq0.c000066400000000000000000000006001355073133200207710ustar00rootroot00000000000000struct 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.1/validation/optim/trunc-setne0.c000066400000000000000000000004361355073133200207750ustar00rootroot00000000000000struct 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.1/validation/optim/trunc-trunc.c000066400000000000000000000003101355073133200207210ustar00rootroot00000000000000char 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.1/validation/optim/void-if-convert.c000066400000000000000000000005071355073133200214600ustar00rootroot00000000000000int 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.1/validation/optim/volatile-bitfield.c000066400000000000000000000003311355073133200220370ustar00rootroot00000000000000struct 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.1/validation/optim/volatile-side-effect.c000066400000000000000000000003311355073133200224330ustar00rootroot00000000000000void 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.1/validation/optim/volatile-store00.c000066400000000000000000000005131355073133200215530ustar00rootroot00000000000000void 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.1/validation/optim/zext-and.c000066400000000000000000000003251355073133200201750ustar00rootroot00000000000000unsigned 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.1/validation/optim/zext-and1.c000066400000000000000000000003341355073133200202560ustar00rootroot00000000000000unsigned 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.1/validation/optim/zext-asr.c000066400000000000000000000003551355073133200202230ustar00rootroot00000000000000unsigned 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.1/validation/optim/zext-sext.c000066400000000000000000000003611355073133200204160ustar00rootroot00000000000000int 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.1/validation/optim/zext-zext.c000066400000000000000000000003721355073133200204270ustar00rootroot00000000000000int 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.1/validation/option-parsing-00.c000066400000000000000000000001271355073133200205010ustar00rootroot00000000000000 /* * check-name: option parsing 00 * check-command: sparse -foptimize-xyz $file */ sparse-0.6.1/validation/option-parsing-01.c000066400000000000000000000001321355073133200204760ustar00rootroot00000000000000 /* * check-name: option parsing 01 * check-command: sparse -fno-optimize-xyz $file */ sparse-0.6.1/validation/outer-scope.c000066400000000000000000000005161355073133200175620ustar00rootroot00000000000000#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.1/validation/overflow.c000066400000000000000000000005311355073133200171550ustar00rootroot00000000000000extern 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.1/validation/pragma-once.c000066400000000000000000000001111355073133200174750ustar00rootroot00000000000000#pragma once #include "pragma-once.c" /* * check-name: #pragma once */ sparse-0.6.1/validation/preprocessor/000077500000000000000000000000001355073133200176755ustar00rootroot00000000000000sparse-0.6.1/validation/preprocessor/base-file.c000066400000000000000000000004141355073133200216670ustar00rootroot00000000000000__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.1/validation/preprocessor/base-file.h000066400000000000000000000000271355073133200216740ustar00rootroot00000000000000__FILE__ __BASE_FILE__ sparse-0.6.1/validation/preprocessor/builtin.c000066400000000000000000000003711355073133200215100ustar00rootroot00000000000000__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.1/validation/preprocessor/cli-D-arg.c000066400000000000000000000002141355073133200215350ustar00rootroot00000000000000A 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.1/validation/preprocessor/cli-D-space.c000066400000000000000000000002311355073133200220560ustar00rootroot00000000000000M(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.1/validation/preprocessor/counter1.c000066400000000000000000000002211355073133200215740ustar00rootroot00000000000000__COUNTER__ __COUNTER__ /* * check-name: __COUNTER__ #1 * check-command: sparse -E $file * * check-output-start 0 1 * check-output-end */ sparse-0.6.1/validation/preprocessor/counter2.c000066400000000000000000000004301355073133200215770ustar00rootroot00000000000000__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.1/validation/preprocessor/counter2.h000066400000000000000000000000251355073133200216040ustar00rootroot00000000000000__FILE__ __COUNTER__ sparse-0.6.1/validation/preprocessor/counter3.c000066400000000000000000000004121355073133200216000ustar00rootroot00000000000000/* * 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.1/validation/preprocessor/dump-macros-empty.c000066400000000000000000000002461355073133200234260ustar00rootroot00000000000000/* * 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.1/validation/preprocessor/dump-macros-multi.c000066400000000000000000000002601355073133200234160ustar00rootroot00000000000000/* * 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.1/validation/preprocessor/dump-macros-only.c000066400000000000000000000016131355073133200232500ustar00rootroot00000000000000 #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.1/validation/preprocessor/dump-macros.c000066400000000000000000000015421355073133200222720ustar00rootroot00000000000000#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.1/validation/preprocessor/dynamic.c000066400000000000000000000007361355073133200214730ustar00rootroot00000000000000#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.1/validation/preprocessor/early-escape.c000066400000000000000000000007271355073133200224210ustar00rootroot00000000000000#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.1/validation/preprocessor/extra-token.c000066400000000000000000000004511355073133200223020ustar00rootroot00000000000000#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.1/validation/preprocessor/has-attribute.c000066400000000000000000000020041355073133200226110ustar00rootroot00000000000000#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.1/validation/preprocessor/has-builtin.c000066400000000000000000000013721355073133200222630ustar00rootroot00000000000000#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.1/validation/preprocessor/ident-pragma.c000066400000000000000000000003131355073133200224060ustar00rootroot00000000000000#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.1/validation/preprocessor/ident.c000066400000000000000000000003061355073133200211430ustar00rootroot00000000000000#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.1/validation/preprocessor/include-level.c000066400000000000000000000003611355073133200225710ustar00rootroot00000000000000__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.1/validation/preprocessor/include-level.h000066400000000000000000000000341355073133200225730ustar00rootroot00000000000000__FILE__: __INCLUDE_LEVEL__ sparse-0.6.1/validation/preprocessor/missing-delim.c000066400000000000000000000005751355073133200226110ustar00rootroot00000000000000static 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.1/validation/preprocessor/phase2-backslash.c000066400000000000000000000030301355073133200231500ustar00rootroot00000000000000/* * '\\' 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.1/validation/preprocessor/phase3-comments.c000066400000000000000000000003671355073133200230550ustar00rootroot00000000000000/* * 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.1/validation/preprocessor/predef-llp64.c000066400000000000000000000003061355073133200222440ustar00rootroot00000000000000#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.1/validation/preprocessor/predef-lp32.c000066400000000000000000000002751355073133200220700ustar00rootroot00000000000000#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.1/validation/preprocessor/predef-lp64.c000066400000000000000000000002751355073133200220750ustar00rootroot00000000000000#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.1/validation/preprocessor/predef-unsigned.c000066400000000000000000000003231355073133200231160ustar00rootroot00000000000000#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.1/validation/preprocessor/predef.c000066400000000000000000000032021355073133200213030ustar00rootroot00000000000000#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); TEST_SIZEOF(INT128, __int128); 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.1/validation/preprocessor/preprocessor1.c000066400000000000000000000003601355073133200226470ustar00rootroot00000000000000#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.1/validation/preprocessor/preprocessor10.c000066400000000000000000000005111355073133200227250ustar00rootroot00000000000000/* 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.1/validation/preprocessor/preprocessor11.c000066400000000000000000000020451355073133200227320ustar00rootroot00000000000000#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.1/validation/preprocessor/preprocessor12.c000066400000000000000000000003161355073133200227320ustar00rootroot00000000000000/* * 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.1/validation/preprocessor/preprocessor13.c000066400000000000000000000006741355073133200227420ustar00rootroot00000000000000/* * 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.1/validation/preprocessor/preprocessor14.c000066400000000000000000000003701355073133200227340ustar00rootroot00000000000000/* * 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.1/validation/preprocessor/preprocessor15.c000066400000000000000000000003341355073133200227350ustar00rootroot00000000000000#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.1/validation/preprocessor/preprocessor16.c000066400000000000000000000014711355073133200227410ustar00rootroot00000000000000#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.1/validation/preprocessor/preprocessor17.c000066400000000000000000000002771355073133200227450ustar00rootroot00000000000000#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.1/validation/preprocessor/preprocessor18.c000066400000000000000000000005571355073133200227470ustar00rootroot00000000000000/* 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.1/validation/preprocessor/preprocessor19.c000066400000000000000000000006711355073133200227450ustar00rootroot00000000000000/* 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.1/validation/preprocessor/preprocessor2.c000066400000000000000000000003201355073133200226440ustar00rootroot00000000000000#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.1/validation/preprocessor/preprocessor20.c000066400000000000000000000003071355073133200227310ustar00rootroot00000000000000#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.1/validation/preprocessor/preprocessor20.h000066400000000000000000000000451355073133200227350ustar00rootroot00000000000000#ifdef X B #endif #ifndef Y A #endif sparse-0.6.1/validation/preprocessor/preprocessor21.c000066400000000000000000000004551355073133200227360ustar00rootroot00000000000000#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.1/validation/preprocessor/preprocessor22.c000066400000000000000000000015041355073133200227330ustar00rootroot00000000000000#define CONFIG_FOO 1 #define define_struct(name, fields...) struct fields name; define_struct(a, { #ifdef CONFIG_FOO int b; #elif defined(CONFIG_BAR) int c; #else int d; #endif }); /* * check-name: Preprocessor #22 * * check-description: Directives are not allowed within a macro argument list, * although cpp deals with it to treat macro more like C functions. * * check-command: sparse -E $file * * check-error-start preprocessor/preprocessor22.c:6:1: error: directive in macro's argument list preprocessor/preprocessor22.c:8:1: error: directive in macro's argument list preprocessor/preprocessor22.c:10:1: error: directive in macro's argument list preprocessor/preprocessor22.c:12:1: error: directive in macro's argument list * check-error-end * * check-output-start struct { int b; } a;; * check-output-end */ sparse-0.6.1/validation/preprocessor/preprocessor23.c000066400000000000000000000025531355073133200227410ustar00rootroot00000000000000#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.1/validation/preprocessor/preprocessor3.c000066400000000000000000000011461355073133200226540ustar00rootroot00000000000000/* * 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.1/validation/preprocessor/preprocessor4.c000066400000000000000000000003641355073133200226560ustar00rootroot00000000000000#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.1/validation/preprocessor/preprocessor5.c000066400000000000000000000003241355073133200226530ustar00rootroot00000000000000#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.1/validation/preprocessor/preprocessor6.c000066400000000000000000000016311355073133200226560ustar00rootroot00000000000000/* 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.1/validation/preprocessor/preprocessor7.c000066400000000000000000000003141355073133200226540ustar00rootroot00000000000000#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.1/validation/preprocessor/preprocessor8.c000066400000000000000000000016451355073133200226650ustar00rootroot00000000000000#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.1/validation/preprocessor/preprocessor9.c000066400000000000000000000004331355073133200226600ustar00rootroot00000000000000/* 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.1/validation/preprocessor/stringify.c000066400000000000000000000004471355073133200220640ustar00rootroot00000000000000#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.1/validation/preprocessor/wide.c000066400000000000000000000003431355073133200207710ustar00rootroot00000000000000#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.1/validation/prototype.c000066400000000000000000000001771355073133200173650ustar00rootroot00000000000000static int prototype(void); /* * check-name: Compile skip function prototype * check-command: sparsec -c $file -o tmp.o */ sparse-0.6.1/validation/ptr-inherit.c000066400000000000000000000037021355073133200175620ustar00rootroot00000000000000#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.1/validation/ptr-sub-blows.c000066400000000000000000000006371355073133200200410ustar00rootroot00000000000000static 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.1/validation/pure-function.c000066400000000000000000000003331355073133200201100ustar00rootroot00000000000000 static __attribute__((__pure__)) int pure1(void) { int i = 0; return i; } static __attribute__((__pure__)) void *pure2(void) { void *i = (void *)0; return i; } /* * check-name: Pure function attribute */ sparse-0.6.1/validation/range-syntax.c000066400000000000000000000006751355073133200177430ustar00rootroot00000000000000 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.1/validation/repeat.h000066400000000000000000000027751355073133200166130ustar00rootroot00000000000000#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.1/validation/reserved.c000066400000000000000000000164231355073133200171400ustar00rootroot00000000000000static 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.1/validation/restrict-array.c000066400000000000000000000017231355073133200202710ustar00rootroot00000000000000#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.1/validation/restrict.c000066400000000000000000000054471355073133200171640ustar00rootroot00000000000000void 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 (originally declared at restrict.c:3) - incompatible argument 1 (different modifiers) restrict.c:12:6: error: symbol 'f03' redeclared with different type (originally declared at restrict.c:4) - incompatible argument 1 (different modifiers) 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.1/validation/restricted-typeof.c000066400000000000000000000002741355073133200207720ustar00rootroot00000000000000typedef 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.1/validation/self-quote-args.c000066400000000000000000000003451355073133200203330ustar00rootroot00000000000000/* * 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.1/validation/shift-negative.c000066400000000000000000000011331355073133200202260ustar00rootroot00000000000000unsigned 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:45: warning: shift count is negative (-1) shift-negative.c:2:45: 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.1/validation/shift-undef-long.c000066400000000000000000000012441355073133200204650ustar00rootroot00000000000000static 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:16: warning: shift too big (4294967295) for type unsigned int shift-undef-long.c:5:16: warning: shift too big (4294967296) for type unsigned int shift-undef-long.c:6:16: warning: shift too big (4294967296) for type unsigned int shift-undef-long.c:7:16: warning: shift count is negative (-4294967296) * check-error-end */ sparse-0.6.1/validation/shift-undef.c000066400000000000000000000132061355073133200175310ustar00rootroot00000000000000int 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:15: warning: shift too big (100) for type int shift-undef.c:4:15: warning: shift too big (101) for type unsigned int shift-undef.c:5:15: warning: shift too big (102) for type unsigned int shift-undef.c:6:15: warning: shift count is negative (-1) shift-undef.c:7:15: warning: shift count is negative (-2) shift-undef.c:8:15: warning: shift count is negative (-3) shift-undef.c:9:25: warning: shift too big (103) for type int shift-undef.c:10:25: warning: shift too big (104) for type unsigned int shift-undef.c:11:25: warning: shift too big (105) for type unsigned int shift-undef.c:12:25: warning: shift count is negative (-4) shift-undef.c:13:25: warning: shift count is negative (-5) shift-undef.c:14:25: warning: shift count is negative (-6) shift-undef.c:15:30: warning: shift too big (106) for type int shift-undef.c:16:30: warning: shift too big (107) for type unsigned int shift-undef.c:17:30: warning: shift too big (108) for type unsigned int shift-undef.c:18:30: warning: shift count is negative (-7) shift-undef.c:19:30: warning: shift count is negative (-8) shift-undef.c:20:30: warning: shift count is negative (-9) 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:11: warning: shift too big (100) for type int shift-undef.c:33:11: warning: shift too big (101) for type unsigned int shift-undef.c:34:11: warning: shift too big (102) for type unsigned int shift-undef.c:35:11: warning: shift count is negative (-1) shift-undef.c:36:11: warning: shift count is negative (-2) shift-undef.c:37:11: warning: shift count is negative (-3) shift-undef.c:38:25: warning: shift too big (103) for type int shift-undef.c:39:25: warning: shift too big (104) for type unsigned int shift-undef.c:40:25: warning: shift too big (105) for type unsigned int shift-undef.c:41:25: warning: shift count is negative (-4) shift-undef.c:42:25: warning: shift count is negative (-5) shift-undef.c:43:25: warning: shift count is negative (-6) shift-undef.c:44:30: warning: shift too big (106) for type int shift-undef.c:45:30: warning: shift too big (107) for type unsigned int shift-undef.c:46:30: warning: shift too big (108) for type unsigned int shift-undef.c:47:30: warning: shift count is negative (-7) shift-undef.c:48:30: warning: shift count is negative (-8) shift-undef.c:49:30: warning: shift count is negative (-9) 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 int shift-undef.c:52:26: warning: shift too big (111) for type 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.1/validation/sizeof-bool.c000066400000000000000000000005141355073133200175430ustar00rootroot00000000000000static 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.1/validation/sizeof-builtin.c000066400000000000000000000004441355073133200202600ustar00rootroot00000000000000int 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.1/validation/sizeof-compound-postfix.c000066400000000000000000000002261355073133200221260ustar00rootroot00000000000000struct 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.1/validation/sizeof-function.c000066400000000000000000000020451355073133200204360ustar00rootroot00000000000000extern 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.1/validation/sizeof-incomplete-type.c000066400000000000000000000014551355073133200217330ustar00rootroot00000000000000struct 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.1/validation/sizeof-void.c000066400000000000000000000022571355073133200175570ustar00rootroot00000000000000#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.1/validation/specifiers1.c000066400000000000000000000032131355073133200175270ustar00rootroot00000000000000static 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.1/validation/specifiers2.c000066400000000000000000000151561355073133200175410ustar00rootroot00000000000000typedef 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.1/validation/static-forward-decl.c000066400000000000000000000003421355073133200211500ustar00rootroot00000000000000static int f(void); int f(void) { return 0; } /* * check-name: static forward declaration * * check-error-start static-forward-decl.c:3:5: warning: symbol 'f' was not declared. Should it be static? * check-error-end */ sparse-0.6.1/validation/static_assert.c000066400000000000000000000033051355073133200201640ustar00rootroot00000000000000_Static_assert(1, "global ok"); struct foo { _Static_assert(1, "struct ok"); }; void bar(void) { _Static_assert(1, " func1 ok"); int i; i = 0; _Static_assert(1, " func2 ok"); if (1) { _Static_assert(1, " func3 ok"); } } _Static_assert(0, "expected assertion failure"); static int f; _Static_assert(f, "non-constant expression"); static int *p; _Static_assert(p, "non-integer expression"); _Static_assert(0.1, "float expression"); _Static_assert(!0 == 1, "non-trivial expression"); static char array[4]; _Static_assert(sizeof(array) == 4, "sizeof expression"); static const char non_literal_string[] = "non literal string"; _Static_assert(0, non_literal_string); _Static_assert(1 / 0, "invalid expression: should not show up?"); struct s { char arr[16]; _Static_assert(1, "inside struct"); }; union u { char c; int i; _Static_assert(1, "inside union"); }; _Static_assert(sizeof(struct s) == 16, "sizeof assertion"); _Static_assert(1, ); _Static_assert(, ""); _Static_assert(,); /* * check-name: static assertion * * check-error-start static_assert.c:19:16: error: static assertion failed: "expected assertion failure" static_assert.c:22:16: error: bad constant expression static_assert.c:25:16: error: bad constant expression static_assert.c:27:16: error: bad constant expression static_assert.c:35:19: error: 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() * check-error-end */ sparse-0.6.1/validation/storage-struct-member.c000066400000000000000000000006011355073133200215430ustar00rootroot00000000000000int 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.1/validation/strict-prototypes0.c000066400000000000000000000002421355073133200211270ustar00rootroot00000000000000extern void func1(); extern void myfunction(), myfunc2(); /* * check-name: strict-prototypes disabled * check-command: sparse -Wno-strict-prototypes $file */ sparse-0.6.1/validation/strict-prototypes1.c000066400000000000000000000007331355073133200211350ustar00rootroot00000000000000extern 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.1/validation/struct-as.c000066400000000000000000000004711355073133200172420ustar00rootroot00000000000000/* * 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.1/validation/struct-attribute-placement.c000066400000000000000000000001551355073133200226070ustar00rootroot00000000000000struct __attribute__((__aligned__(16))) foo { int a; }; /* * check-name: struct attribute placement */ sparse-0.6.1/validation/struct-ns1.c000066400000000000000000000007171355073133200173430ustar00rootroot00000000000000// 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.1/validation/struct-ns2.c000066400000000000000000000007311355073133200173400ustar00rootroot00000000000000static 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.1/validation/struct-size1.c000066400000000000000000000004551355073133200176740ustar00rootroot00000000000000struct 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.1/validation/switch-long.c000066400000000000000000000011221355073133200175450ustar00rootroot00000000000000void 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.1/validation/tautological-compare.c000066400000000000000000000030071355073133200214260ustar00rootroot00000000000000typedef 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.1/validation/test-suite000077500000000000000000000321501355073133200172040ustar00rootroot00000000000000#!/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 # 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_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-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 $? } ## # 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 -k 1s $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 [ "$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 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 while [ $# -gt 1 ] ; do case "$1" in -a) append=1 ;; -f) fail=1 ;; -l) def_cmd='test-linearize -Wno-decl $file' linear=1 ;; help|-*) do_format_help return 0 ;; *) break ;; esac shift continue done 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 [ $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.1/validation/transparent-union.c000066400000000000000000000005041355073133200210010ustar00rootroot00000000000000struct 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.1/validation/type-compare.c000066400000000000000000000041521355073133200177220ustar00rootroot00000000000000#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.1/validation/type1.c000066400000000000000000000007601355073133200163600ustar00rootroot00000000000000/* * 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.1/validation/typedef-redef-c89.c000066400000000000000000000004771355073133200204470ustar00rootroot00000000000000typedef 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.1/validation/typedef-redef.c000066400000000000000000000004511355073133200200360ustar00rootroot00000000000000typedef 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 (originally declared at typedef-redef.c:4) - different type sizes * check-error-end */ sparse-0.6.1/validation/typedef_shadow.c000066400000000000000000000004361355073133200203230ustar00rootroot00000000000000typedef 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.1/validation/typediff-arraysize.c000066400000000000000000000006041355073133200211340ustar00rootroot00000000000000extern 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.1/validation/typediff-enum.c000066400000000000000000000023201355073133200200640ustar00rootroot00000000000000enum 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.1/validation/typeof-addresspace.c000066400000000000000000000006111355073133200210730ustar00rootroot00000000000000#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.1/validation/typeof-attribute.c000066400000000000000000000005511355073133200206230ustar00rootroot00000000000000#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.1/validation/typeof-bad.c000066400000000000000000000005621355073133200173500ustar00rootroot00000000000000static 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.1/validation/typeof-mods.c000066400000000000000000000045251355073133200175670ustar00rootroot00000000000000#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) { __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.1/validation/typeof-noderef.c000066400000000000000000000004701355073133200202420ustar00rootroot00000000000000#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.1/validation/typeof-safe.c000066400000000000000000000005651355073133200175430ustar00rootroot00000000000000#define __safe __attribute__((safe)) static void test_safe(void) { int __safe obj, *ptr; typeof(obj) var = obj; typeof(ptr) ptr2 = ptr; typeof(*ptr) var2 = obj; typeof(*ptr) *ptr3 = ptr; typeof(obj) *ptr4 = ptr; obj = obj; ptr = ptr; ptr = &obj; obj = *ptr; } /* * check-name: typeof-safe * check-known-to-fail * * check-error-start * check-error-end */ sparse-0.6.1/validation/typesign.c000066400000000000000000000031401355073133200171530ustar00rootroot00000000000000static 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.1/validation/var-undef-partial.c000066400000000000000000000006011355073133200206310ustar00rootroot00000000000000int 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.1/validation/varargs1.c000066400000000000000000000002451355073133200170420ustar00rootroot00000000000000extern 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.1/validation/vla-sizeof-ice.c000066400000000000000000000006461355073133200201360ustar00rootroot00000000000000// 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.1/validation/vla-sizeof.c000066400000000000000000000006541355073133200173770ustar00rootroot00000000000000unsigned 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.1/validation/vla-sizeof0.c000066400000000000000000000004671355073133200174610ustar00rootroot00000000000000#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.1/validation/vla-sizeof1.c000066400000000000000000000005631355073133200174570ustar00rootroot00000000000000#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.1/validation/vla-sizeof2.c000066400000000000000000000005521355073133200174560ustar00rootroot00000000000000#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.1/validation/vla-sizeof3.c000066400000000000000000000005711355073133200174600ustar00rootroot00000000000000#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.1/validation/vla-sizeof4.c000066400000000000000000000006741355073133200174650ustar00rootroot00000000000000#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.1/validation/wide.c000066400000000000000000000002751355073133200162470ustar00rootroot00000000000000static 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 */