pax_global_header 0000666 0000000 0000000 00000000064 14115310122 0014500 g ustar 00root root 0000000 0000000 52 comment=c4706aa764f3ae68258ba60be6325a5662900362
sparse-0.6.4/ 0000775 0000000 0000000 00000000000 14115310122 0013004 5 ustar 00root root 0000000 0000000 sparse-0.6.4/.gitignore 0000664 0000000 0000000 00000000651 14115310122 0014776 0 ustar 00root root 0000000 0000000 # generic
*.a
*.o
*.o.d
*.pyc
*.so
*~
.*.swp
# generated
/version.h
# programs
/c2xml
/compile
/ctags
/example
/graph
/obfuscate
/scheck
/semind
/sparse
/sparse-llvm
/test-dissect
/test-inspect
/test-lexing
/test-linearize
/test-parsing
/test-show-type
/test-unssa
# tags
tags
TAGS
# stgit generated dirs
patches-*
# quilt's files
patches
series
# local makefile
local.mk
.*.mk
# cscope and Qt files
cscope.out
*.pro*
sparse-0.6.4/Documentation/ 0000775 0000000 0000000 00000000000 14115310122 0015615 5 ustar 00root root 0000000 0000000 sparse-0.6.4/Documentation/.gitignore 0000664 0000000 0000000 00000000024 14115310122 0017601 0 ustar 00root root 0000000 0000000 build
dev-options.1
sparse-0.6.4/Documentation/IR.rst 0000664 0000000 0000000 00000025130 14115310122 0016662 0 ustar 00root root 0000000 0000000 .. default-domain:: ir
Intermediate Representation
===========================
Instructions
~~~~~~~~~~~~
This document briefly describes which field of struct instruction is
used by which operation.
Some of those fields are used by almost all instructions,
some others are specific to only one or a few instructions.
The common ones are:
* .src1, .src2, .src3: (pseudo_t) operands of binops or ternary ops.
* .src: (pseudo_t) operand of unary ops (alias for .src1).
* .target: (pseudo_t) result of unary, binary & ternary ops, is
sometimes used otherwise by some others instructions.
* .cond: (pseudo_t) input operands for condition (alias .src/.src1)
* .type: (symbol*) usually the type of .result, sometimes of the operands
Terminators
-----------
.. op:: OP_RET
Return from subroutine.
* .src : returned value (NULL if void)
* .type: type of .src
.. op:: OP_BR
Unconditional branch
* .bb_true: destination basic block
.. op:: OP_CBR
Conditional branch
* .cond: condition
* .type: type of .cond, must be an integral type
* .bb_true, .bb_false: destination basic blocks
.. op:: OP_SWITCH
Switch / multi-branch
* .cond: condition
* .type: type of .cond, must be an integral type
* .multijmp_list: pairs of case-value - destination basic block
.. op:: OP_UNREACH
Mark code as unreachable
.. op:: OP_COMPUTEDGOTO
Computed goto / branch to register
* .src: address to branch to (void*)
* .multijmp_list: list of possible destination basic blocks
Arithmetic binops
-----------------
They all follow the same signature:
* .src1, .src2: operands (types must be compatible with .target)
* .target: result of the operation (must be an integral type)
* .type: type of .target
.. op:: OP_ADD
Integer addition.
.. op:: OP_SUB
Integer subtraction.
.. op:: OP_MUL
Integer multiplication.
.. op:: OP_DIVU
Integer unsigned division.
.. op:: OP_DIVS
Integer signed division.
.. op:: OP_MODU
Integer unsigned remainder.
.. op:: OP_MODS
Integer signed remainder.
.. op:: OP_SHL
Shift left (integer only)
.. op:: OP_LSR
Logical Shift right (integer only)
.. op:: OP_ASR
Arithmetic Shift right (integer only)
Floating-point binops
---------------------
They all follow the same signature:
* .src1, .src2: operands (types must be compatible with .target)
* .target: result of the operation (must be a floating-point type)
* .type: type of .target
.. op:: OP_FADD
Floating-point addition.
.. op:: OP_FSUB
Floating-point subtraction.
.. op:: OP_FMUL
Floating-point multiplication.
.. op:: OP_FDIV
Floating-point division.
Logical ops
-----------
They all follow the same signature:
* .src1, .src2: operands (types must be compatible with .target)
* .target: result of the operation
* .type: type of .target, must be an integral type
.. op:: OP_AND
Logical AND
.. op:: OP_OR
Logical OR
.. op:: OP_XOR
Logical XOR
Integer compares
----------------
They all have the following signature:
* .src1, .src2: operands (types must be compatible)
* .target: result of the operation (0/1 valued integer)
* .type: type of .target, must be an integral type
* .itype: type of the input operands
.. op:: OP_SET_EQ
Compare equal.
.. op:: OP_SET_NE
Compare not-equal.
.. op:: OP_SET_LE
Compare less-than-or-equal (signed).
.. op:: OP_SET_GE
Compare greater-than-or-equal (signed).
.. op:: OP_SET_LT
Compare less-than (signed).
.. op:: OP_SET_GT
Compare greater-than (signed).
.. op:: OP_SET_B
Compare less-than (unsigned).
.. op:: OP_SET_A
Compare greater-than (unsigned).
.. op:: OP_SET_BE
Compare less-than-or-equal (unsigned).
.. op:: OP_SET_AE
Compare greater-than-or-equal (unsigned).
Floating-point compares
-----------------------
They all have the same signature as the integer compares.
The usual 6 operations exist in two versions: 'ordered' and
'unordered'. These operations first check if any operand is a
NaN and if it is the case the ordered compares return false
and then unordered return true, otherwise the result of the
comparison, now guaranteed to be done on non-NaNs, is returned.
.. op:: OP_FCMP_OEQ
Floating-point compare ordered equal
.. op:: OP_FCMP_ONE
Floating-point compare ordered not-equal
.. op:: OP_FCMP_OLE
Floating-point compare ordered less-than-or-equal
.. op:: OP_FCMP_OGE
Floating-point compare ordered greater-or-equal
.. op:: OP_FCMP_OLT
Floating-point compare ordered less-than
.. op:: OP_FCMP_OGT
Floating-point compare ordered greater-than
.. op:: OP_FCMP_UEQ
Floating-point compare unordered equal
.. op:: OP_FCMP_UNE
Floating-point compare unordered not-equal
.. op:: OP_FCMP_ULE
Floating-point compare unordered less-than-or-equal
.. op:: OP_FCMP_UGE
Floating-point compare unordered greater-or-equal
.. op:: OP_FCMP_ULT
Floating-point compare unordered less-than
.. op:: OP_FCMP_UGT
Floating-point compare unordered greater-than
.. op:: OP_FCMP_ORD
Floating-point compare ordered: return true if both operands are ordered
(none of the operands are a NaN) and false otherwise.
.. op:: OP_FCMP_UNO
Floating-point compare unordered: return false if no operands is ordered
and true otherwise.
Unary ops
---------
.. op:: OP_NOT
Logical not.
* .src: operand (type must be compatible with .target)
* .target: result of the operation
* .type: type of .target, must be an integral type
.. op:: OP_NEG
Integer negation.
* .src: operand (type must be compatible with .target)
* .target: result of the operation (must be an integral type)
* .type: type of .target
.. op:: OP_FNEG
Floating-point negation.
* .src: operand (type must be compatible with .target)
* .target: result of the operation (must be a floating-point type)
* .type: type of .target
.. op:: OP_SYMADDR
Create a pseudo corresponding to the address of a symbol.
* .src: input symbol (must be a PSEUDO_SYM)
* .target: symbol's address
.. op:: OP_COPY
Copy (only needed after out-of-SSA).
* .src: operand (type must be compatible with .target)
* .target: result of the operation
* .type: type of .target
Type conversions
----------------
They all have the following signature:
* .src: source value
* .orig_type: type of .src
* .target: result value
* .type: type of .target
Currently, a cast to a void pointer is treated like a cast to
an unsigned integer of the same size.
.. op:: OP_TRUNC
Cast from integer to an integer of a smaller size.
.. op:: OP_SEXT
Cast from integer to an integer of a bigger size with sign extension.
.. op:: OP_ZEXT
Cast from integer to an integer of a bigger size with zero extension.
.. op:: OP_UTPTR
Cast from pointer-sized unsigned integer to pointer type.
.. op:: OP_PTRTU
Cast from pointer type to pointer-sized unsigned integer.
.. op:: OP_PTRCAST
Cast between pointers.
.. op:: OP_FCVTU
Conversion from float type to unsigned integer.
.. op:: OP_FCVTS
Conversion from float type to signed integer.
.. op:: OP_UCVTF
Conversion from unsigned integer to float type.
.. op:: OP_SCVTF
Conversion from signed integer to float type.
.. op:: OP_FCVTF
Conversion between float types.
Ternary ops
-----------
.. op:: OP_SEL
* .src1: condition, must be of integral type
* .src2, .src3: operands (types must be compatible with .target)
* .target: result of the operation
* .type: type of .target
.. op:: OP_FMADD
Fused multiply-add.
* .src1, .src2, .src3: operands (types must be compatible with .target)
* .target: result of the operation (must be a floating-point type)
* .type: type of .target
.. op:: OP_RANGE
Range/bounds checking (only used for an unused sparse extension).
* .src1: value to be checked
* .src2, src3: bound of the value (must be constants?)
* .type: type of .src[123]?
Memory ops
----------
.. op:: OP_LOAD
Load.
* .src: base address to load from
* .offset: address offset
* .target: loaded value
* .type: type of .target
.. op:: OP_STORE
Store.
* .src: base address to store to
* .offset: address offset
* .target: value to be stored
* .type: type of .target
Others
------
.. op:: OP_SETFVAL
Create a pseudo corresponding to a floating-point literal.
* .fvalue: the literal's value (long double)
* .target: the corresponding pseudo
* .type: type of the literal & .target
.. op:: OP_SETVAL
Create a pseudo corresponding to a string literal.
The value is given as an expression EXPR_STRING.
* .val: (expression) input expression
* .target: the resulting value
* .type: type of .target, the value
.. op:: OP_LABEL
Create a pseudo corresponding to a label-as-value.
* .bb_true: the BB corresponding to the label
* .target: the resulting value
* .type: type of .target (void \*)
.. op:: OP_PHI
Phi-node (for SSA form).
* .phi_list: phi-operands (type must be compatible with .target)
* .target: "result"
* .type: type of .target
.. op:: OP_PHISOURCE
Phi-node source.
Like OP_COPY but exclusively used to give a defining instructions
(and thus also a type) to *all* OP_PHI operands.
* .phi_src: operand (type must be compatible with .target, alias .src)
* .target: the "result" PSEUDO_PHI
* .type: type of .target
* .phi_node: the unique phi instruction using the target pseudo
.. op:: OP_CALL
Function call.
* .func: (pseudo_t) the function (can be a symbol or a "register",
alias .src))
* .arguments: (pseudo_list) list of the associated arguments
* .target: function return value (if any)
* .type: type of .target
* .fntypes: (symbol_list) list of the function's types: the first
entry is the full function type, the next ones are the type of
each arguments
.. op:: OP_INLINED_CALL
Only used as an annotation to show that the instructions just above
correspond to a function that have been inlined.
* .func: (pseudo_t) the function (must be a symbol, alias .src))
* .arguments: list of pseudos that where the function's arguments
* .target: function return value (if any)
* .type: type of .target
.. op:: OP_SLICE
Extract a "slice" from an aggregate.
* .base: (pseudo_t) aggregate (alias .src)
* .from: offset of the "slice" within the aggregate
* .target: result
* .type: type of .target
.. op:: OP_ASM
Inlined assembly code.
* .string: asm template
* .asm_rules: asm constraints, rules
Sparse tagging (line numbers, context, whatever)
------------------------------------------------
.. op:: OP_CONTEXT
Currently only used for lock/unlock tracking.
* .context_expr: unused
* .increment: (1 for locking, -1 for unlocking)
* .check: (ignore the instruction if 0)
Misc ops
--------
.. op:: OP_ENTRY
Function entry point (no associated semantic).
.. op:: OP_BADOP
Invalid operation (should never be generated).
.. op:: OP_NOP
No-op (should never be generated).
.. op:: OP_DEATHNOTE
Annotation telling the pseudo will be death after the next
instruction (other than some other annotation, that is).
.. # vim: tabstop=4
sparse-0.6.4/Documentation/Makefile 0000664 0000000 0000000 00000001126 14115310122 0017255 0 ustar 00root root 0000000 0000000 # Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS = -a
SPHINXBUILD = sphinx-build
SPHINXPROJ = sparse
SOURCEDIR = .
BUILDDIR = build
targets := help
targets += html
targets += man
# Put it first so that "make" without argument is like "make help".
help:
# route all targets to Sphinx using the new "make mode" option.
$(targets): conf.py Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS)
%.1: %.rst man
@mv build/man/$@ $@
.PHONY: Makefile # avoid circular deps with the catch-all rule
sparse-0.6.4/Documentation/TODO.md 0000664 0000000 0000000 00000007735 14115310122 0016720 0 ustar 00root root 0000000 0000000 TODO
====
Essential
---------
* SSA is broken by simplify_loads() & branches rewriting/simplification
* add support for bitwise enums (wip)
Documentation
-------------
* document the API
* document the limitations of modifying ptrlists during list walking
* document the data structures
* document flow of data / architecture / code structure
Core
----
* if a variable has its address taken but in an unreachable BB then
its MOD_ADDRESSABLE may be wrong and it won't be SSA converted.
- let kill_insn() check killing of SYMADDR,
- add the sym into a list and
- recalculate the addressability before memops's SSA conversion
* bool_ctype should be split into internal 1-bit / external 8-bit
Testsuite
---------
* there are 60 failing tests. They should be fixed
(but most are non-trivial to fix).
Misc
----
* GCC's -Wenum-compare / clangs's -Wenum-conversion -Wassign-enum
* parse __attribute_((fallthrough))
* add support for format(printf()) (WIP by Ben Dooks)
* make use of UNDEFs (issues warnings, simplification, ... ?)
* make memory accesses more explicit: add EXPR_ACCESS (wip)
* it would be nice to do our own parsing of floating point (wip)
* some header files needed for crypto/ need __vector or __fp16
* some even need __complex
Optimization
------------
* a lot of small simplifications are waiting to be upstreamed
* the domtree need to be rebuilt (or updated)
* critical edges need to be split
* the current way of doing CSE uses a lot of time
* add SSA based DCE
* add SSA based PRE
* Add SSA based SCCP
* add a pass to inline small functions during simplification.
* use better/more systematic use of internal verification framework
* tracking of operands size should be improved (WIP)
* OP_INLINE is sometimes in the way
* would be nice to strictly separate phases that don't changes the
CFG and thus the dominance tree.
IR
--
* pseudos are untyped, it's usually OK but often it complicates things:
- PSEUDO_REGs are defined by instructions and their type is normally
retrievable via this defining instruction but in some cases they're not:
for example, pseudos defined by ASM output.
- PSEUDO_ARGs are considered as defined by OP_ENTRY and are used like
this for liveness trackability but their type can't simply be
retrieved via this instruction like PSEUDO_REGs are (with ->def->type).
- PSEUDO_VALs are completely typeless.
Maybe a few bits should be used to store some kind of low-level type.
* OP_SET should return a bool, always
* add IR instructions for va_arg() & friends
* add a possibility to import of file in "IR assembly"
* dump the symtable
* dump the CFG
LLVM
----
* fix ...
Internal backends
-----------------
* it would be nice the upstream the code generator
* add a pass to transform 3-addresses code to 2-addresses
* add some basic register allocation
* add a pass to order the BBs and changes 2-ways CBR into one-way branches
* what can be done for x86?
* add support to add constraints in the MD rules
Longer term/to investigate
--------------------------
* attributes are represented as ctypes's alignment, modifiers & contexts
but plenty of attributes doesn't fit, for example they need arguments.
* format(printf, ...),
* section("...")
* assume_aligned(alignment[, offsert])
* error("message"), warning("message")
* ...
* should support "-Werror=..." ?
* All warning messages should include the option how to disable it.
For example:
"warning: Variable length array is used."
should be something like:
"warning: Variable length array is used. (-Wno-vla)"
* ptrlists must not have elements removed while being iterated;
this should somehow be enforced.
* having 'struct symbol' used to represent symbols *and* types is
quite handy but it also creates lots of problems and complications
* Possible mixup of symbol for a function designator being not a pointer?
This seems to make evaluation of function pointers much more complex
than needed.
* extend test-inspect to inspect more AST fields.
* extend test-inspect to inspect instructions.
sparse-0.6.4/Documentation/annotations.rst 0000664 0000000 0000000 00000007353 14115310122 0020714 0 ustar 00root root 0000000 0000000 Annotations
===========
Sparse extends C's type system with a number of extra type qualifiers
which add restrictions on what you can do on objects annotated with them.
These qualifiers are specified with GCC's ``__attribute__`` syntax.
address_space(*name*)
---------------------
This attribute is to be used on pointers to specify that its target is
in address space *name* (an identifier or a constant integer).
Sparse treats pointers with different address spaces as distinct types
and will warn on casts (implicit or explicit) mixing the address spaces.
An exception to this is when the destination type is ``uintptr_t`` or
``unsigned long`` since the resulting integer value is independent
of the address space and can't be dereferenced without first casting
it back to a pointer type.
bitwise
-------
This attribute is to be used to define new, unique integer types that
cannot be mixed with other types. In particular, you can't mix a
"bitwise" integer with a normal integer expression, and you can't even
mix it with another bitwise expression of a different type.
The integer 0 is special, though, and can be mixed with any bitwise type
since it's safe for all bitwise operations.
Since this qualifier defines new types, it only makes sense to use
it in typedefs, which effectively makes each of these typedefs
a single "bitwise class", incompatible with any other types.
context(*ctxt*, *entry*, *exit*)
--------------------------------
This attribute is to be used on function declarations to specify
the function's entry and exit count for a given context. This
context can be pretty much anything that can be counted.
Sparse will check that the function's entry and exit contexts match, and
that no path through a function is ever entered with conflicting contexts.
In particular, it is designed for doing things like matching up a "lock"
with the pairing "unlock". For example, a function doing a lock should be
annotated with an entry value of 0 and an exit value of 1, the corresponding
unlock function should use the values 1 and 0, and a function that should
only be called on some locked data, release the lock but which doesn't exit
without reacquiring the lock being, should use entry and exit values of 1.
The first argument, *ctxt*, is an expression only used as documentation
to identify the context. Usually, what is used is a pointer to the structure
containing the context, for example, the structure protected by the lock.
See also https://lwn.net/Articles/109066/.
noderef
-------
This attribute is to be used on a r-value to specify it cannot be
dereferenced. A pointer so annotated is in all other aspects exactly
like a pointer but trying to actually access anything through it will
cause a warning.
nocast
------
This attribute is similar to ``bitwise`` but in a much weaker form.
It warns about explicit or implicit casting to different types.
However, it doesn't warn about the mixing with other types and it easily
gets lost: you can add plain integers to __nocast integer types and the
result will be plain integers.
So, it ends to be more useful for big integers that still need to act
like integers, but you want to make it much less likely that they get
truncated by mistake. For example, a 64-bit integer that you don't want
to mistakenly/silently be returned as int.
See also `Linus' e-mail about __nocast vs __bitwise
`_.
safe
----
This attribute specifies that the object, which should be a pointer,
is defined to never be NULL or nontrapping.
It causes a warning if the object is tested in a conditional.
force
-----
This attribute is to be used in casts to suppress warnings that would
otherwise be caused by the presence of one of these extra qualifiers.
sparse-0.6.4/Documentation/api.rst 0000664 0000000 0000000 00000000530 14115310122 0017116 0 ustar 00root root 0000000 0000000 Sparse API
==========
.. contents::
:local:
:depth: 2
Utilities
~~~~~~~~~
.. c:autodoc:: ptrlist.c
.. c:autodoc:: utils.h
.. c:autodoc:: flowgraph.h
Parsing
~~~~~~~
.. c:autodoc:: expression.h
Typing
~~~~~~
.. c:autodoc:: evaluate.h
Optimization
~~~~~~~~~~~~
.. c:autodoc:: optimize.c
.. c:autodoc:: flow.c
.. c:autodoc:: simplify.c
sparse-0.6.4/Documentation/conf.py 0000664 0000000 0000000 00000012505 14115310122 0017117 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
# If extensions (or modules to document with autodoc) are in another
# directory, add these directories to sys.path here. If the directory
# is relative to the documentation root, use os.path.abspath to make
# it absolute, like shown here.
# sys.path.insert(0, os.path.abspath('.'))
import os
import sys
import datetime
# -- General configuration ------------------------------------------------
needs_sphinx = '1.7'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
sys.path.insert(0, os.path.abspath('sphinx'))
extensions = [
'cdoc'
, 'ir'
]
# support .md with python2 & python3
if sys.version_info[0] > 2:
from recommonmark.parser import CommonMarkParser
source_parsers = {
'.md': CommonMarkParser,
}
else:
source_parsers = {
'.md': 'recommonmark.parser.CommonMarkParser',
}
# Add any paths that contain templates here, relative to this directory.
templates_path = ['templates']
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
source_suffix = ['.rst', '.md']
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = 'sparse'
copyright = '2003 - ' + str(datetime.datetime.now().year)
author = "sparse's development community"
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The full version, including alpha/beta/rc tags.
release = next(open('../Makefile', 'r')).split('=')[1].rstrip()
# The short X.Y version.
version = release.split('-')[0]
# it's a C project, so:
primary_domain = 'c'
# disable syntax highlight in non-code sections
highlight_language = 'none'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
exclude_patterns = ['build']
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = True
# -- Options for cdoc extension -------------------------------------------
cdoc_srcdir = '..'
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
try:
html_theme = 'sphinx_rtd_theme'
import sphinx_rtd_theme
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
except:
sys.stderr.write("Warning: theme '%s' not found\n" % html_theme)
html_theme = 'classic'
# html_theme_options = {}
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['sphinx/static']
html_context = {
'css_files': [
'_static/theme_overrides.css',
],
}
# Custom sidebar templates, must be a dictionary that maps document names
# to template names.
#
# This is required for the alabaster theme
# refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars
html_sidebars = { }
html_logo = 'logo.svg'
# -- Options for HTMLHelp output ------------------------------------------
# Output file base name for HTML help builder.
htmlhelp_basename = 'sparsedoc'
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
'papersize': 'a4paper',
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
('index', 'sparse.tex', u'sparse Documentation', author, 'manual'),
]
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('dev-options', 'dev-options', u'options for development', [author], 1),
]
# -- Options for Texinfo output -------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', 'sparse', u'sparse Documentation', author, 'sparse', 'C semantic parser & checker', 'Software development'),
]
# vim: tabstop=4
sparse-0.6.4/Documentation/data-structures.txt 0000664 0000000 0000000 00000010343 14115310122 0021511 0 ustar 00root root 0000000 0000000 - A slightly edited irc discussion with Josh Triplett.
- Describes most data structures used in sparse.
As far as the parsing structures go...
The C parser exists in two main files: parse.c, which parses statements, and expression.c, which parses expressions.
parse.h contains the definition of struct statement, which represents a C statement.
That includes only those things which can't appear as an expression, which primarily includes control flow statements such as if, loops, switch/case, and goto.
expression.h contains the definition of struct expression, which represents a C expression. That has a lot more content, since most C constructs can appear in expressions.
A series of statements forms a compound statement (STMT_COMPOUND).
That appears as another struct statement which has a statement_list member.
A function body consists of a compound statement.
When you look at a loop body, if or else body, or case body, you'll notice that they just have a struct statement, not a statement_list; they can have multiple statements by using a compound statement.
Also note that all loops get turned into a single "iterator" statement.
for, while, and do-while.
A symbol, then, represents a name in a C file. A symbol might represent a variable, a function, a label, or various other things.
See symbol.h.
"struct symbol" represents one symbol.
As with the various other structures, it has some common data and a union of sub-structures for the parts that differ between different types.
Most of the interesting bits come in the NS_SYMBOL case.
Among other things, it has a struct statement for the body of a function (if any), a list of symbols for the arguments, an expression for a variable initializer, and so on.
Together, struct symbol, struct statement, and struct expression represent most of the abstract syntax tree for C.
So, that represents most of the "front-end" of Sparse: parsing C and generating that abstract syntax tree.
That much occurs in pretty much any program using the Sparse frontend.
The backend varies among programs.
For instance, the c2xml backend goes that far, then outputs XML.
The sparse static analysis backend has a few steps: it generates linearized bytecode, does some evaluation on that, and outputs some warnings.
Several other backends run that linearized bytecode stage.
The linearized bytecode itself has a set of nested structures.
linearize.h defines all of them.
At the top level, it has struct entrypoint.
That represents an entrypoint to the code, which would normally mean a function.
An entrypoint has a list of basic blocks.
struct basic_block.
A basic block represents a series of instructions with no branches.
Straight-line code.
A branch only occurs at the end of a basic block, and branches can only target the beginning of a basic block.
Typically, a conditional will consist of a basic block leading up to the branch, a basic block for the true case, a basic block for the false case, and a basic block where the two paths merge back together.
Either the true or the false case may not exist.
A loop will normally have a basic block for the loop body, which can branch to the top at the end or continue to the next basic block.
So basic blocks represent a node in the control flow graph.
The edges in that graph lead from one basic block to a basic block which can follow it in the execution of the program.
Each basic block has a series of instructions, "struct instruction".
"enum opcode" lists all the instructions.
Fairly high-level instruction set, corresponding directly to bits of C.
So you have an entrypoint, which has a graph of basic blocks, each of which has a list of instructions.
An entrypoint also has a pointer to the first instruction.
One last bit of trickiness: struct pseudo.
Have you ever heard of "static single assignment" or SSA form?
struct pseudo represents one of those single-assignment variables.
Each one has a pointer to the symbol it represents (which may have many pseudos referencing it).
Each one also has a pointer to the instruction that defines it.
That covers most of the major data structures in Sparse.
Now, given all that, some of the top-level stuff in sparse.c may make more sense.
For instance, the context checking works in terms of basic blocks.
Hopefully some of that helped you understand Sparse better.
sparse-0.6.4/Documentation/dev-options.rst 0000664 0000000 0000000 00000002206 14115310122 0020616 0 ustar 00root root 0000000 0000000 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.4/Documentation/doc-guide.rst 0000664 0000000 0000000 00000006452 14115310122 0020216 0 ustar 00root root 0000000 0000000 Documentation guide
===================
Introduction
------------
The documentation for Sparse is written in plain text augmented with
either `reStructuredText`_ (.rst) or `MarkDown`_ (.md) markup. These
files can be organized hierarchically, allowing a good structuring
of the documentation.
Sparse uses `Sphinx`_ to format this documentation in several formats,
like HTML or PDF.
All documentation related files are in the ``Documentation/`` directory.
In this directory you can use ``make html`` or ``make man`` to generate
the documentation in HTML or manpage format. The generated files can then
be found in the ``build/`` sub-directory.
The root of the documentation is the file ``index.rst`` which mainly
lists the names of the files with real documentation.
.. _Sphinx: http://www.sphinx-doc.org/
.. _reStructuredText: http://docutils.sourceforge.net/rst.html
.. _MarkDown: https://en.wikipedia.org/wiki/Markdown
.. _rest-markup:
Minimal reST cheatsheet
-----------------------
Basic inline markup is:
* ``*italic*`` gives *italic*
* ``**bold**`` gives **bold**
* ````mono```` gives ``mono``
Headings are created by underlining the title with a punctuation
character; it can also be optionally overlined::
#############
Major heading
#############
Minor heading
-------------
Any punctuation character can be used and the levels are automatically
determined from their nesting. However, the convention is to use:
* ``#`` with overline for parts
* ``*`` with overline for chapters
* ``=`` for sections
* ``-`` for subsections
* ``^`` for subsubsections
Lists can be created like this::
* this is a bulleted list
* with the second item
on two lines
* nested lists are supported
* subitem
* another subitem
* and here is the fourth item
#. this is an auto-numbered list
#. with two items
1. this is an explicitly numbered list
2. with two items
Definition lists are created with a simple indentation, like::
term, concept, whatever
Definition, must be indented and
continue here.
It can also have several paragraphs.
Literal blocks are introduced with ``::``, either at the end of the
preceding paragraph or on its own line, and indented text::
This is a paragraph introducing a literal block::
This is the literal block.
It can span several lines.
It can also consist of several paragraphs.
Code examples with syntax highlighting use the *code-block* directive.
For example::
.. code-block:: c
int foo(int a)
{
return a + 1;
}
will give:
.. code-block:: c
int foo(int a)
{
return a + 1;
}
Autodoc
-------
.. highlight:: none
.. c:autodoc:: Documentation/sphinx/cdoc.py
For example, a doc-block like::
///
// increment a value
//
// @val: the value to increment
// @return: the incremented value
//
// This function is to be used to increment a
// value.
//
// It's strongly encouraged to use this
// function instead of open coding a simple
// ``++``.
int inc(int val)
will be displayed like this:
.. c:function:: int inc(int val)
:noindexentry:
:param val: the value to increment
:return: the incremented value
This function is to be used to increment a
value.
It's strongly encouraged to use this
function instead of open coding a simple
``++``.
Intermediate Representation
---------------------------
.. c:autodoc:: Documentation/sphinx/ir.py
sparse-0.6.4/Documentation/index.rst 0000664 0000000 0000000 00000005401 14115310122 0017456 0 ustar 00root root 0000000 0000000 .. sparse documentation master file.
Welcome to sparse's documentation
=================================
.. toctree::
:maxdepth: 1
About Sparse
------------
Sparse, the semantic parser, provides a compiler frontend capable of
parsing most of ANSI C as well as many GCC extensions, and a collection
of sample compiler backends, including a static analyzer also called `sparse`.
Sparse provides a set of annotations designed to convey semantic information
about types, such as what address space pointers point to, or what locks
function acquires or releases.
Linus Torvalds started writing Sparse in 2003, initially targeting issues such
as mixing pointers to user address space and pointers to kernel address space.
Josh Triplett was Sparse's first maintainer in 2006. This role was taken over
by Christopher Li in 2009 and by Luc Van Oostenryck in late 2018.
Getting Sparse
--------------
The most recent version can be obtained directly from the Git
repository with the command::
git clone git://git.kernel.org/pub/scm/devel/sparse/sparse.git
You can also `browse the Git repository `_
or use the mirror at https://github.com/lucvoo/sparse.
The tarballs of released versions of Sparse and their signatures can be found at
https://www.kernel.org/pub/software/devel/sparse/dist/.
Once you have the sources, to build Sparse and install it in your ~/bin
directory, just do::
cd sparse
make
make install
To install it in another directory, use::
make PREFIX= install
Contributing and reporting bugs
-------------------------------
Submission of patches and reporting of bugs, as well as discussions
related to Sparse, should be done via the mailing list:
linux-sparse@vger.kernel.org.
You do not have to be subscribed to the list to send a message there.
Previous discussions and bug reports are available on the list
archives at https://marc.info/?l=linux-sparse.
To subscribe to the list, send an email with
``subscribe linux-sparse`` in the body to ``majordomo@vger.kernel.org``.
Bugs can also be reported and tracked via the `Linux kernel's bugzilla for sparse
`_.
.. toctree::
:caption: User Documentation
:maxdepth: 1
annotations
Some interesting external documentation:
* `Sparse: a look under the hood `_
* `Sparse: a short overview `_
.. toctree::
:caption: Development
:maxdepth: 1
submitting-patches
types
api
dev-options
IR
test-suite
doc-guide
TODO
.. toctree::
:caption: Release Notes
:maxdepth: 1
release-notes/index
Indices and tables
==================
* :ref:`genindex`
sparse-0.6.4/Documentation/logo.svg 0000664 0000000 0000000 00000013373 14115310122 0017305 0 ustar 00root root 0000000 0000000
image/svg+xml
Layer 1
sparse-0.6.4/Documentation/release-notes/ 0000775 0000000 0000000 00000000000 14115310122 0020363 5 ustar 00root root 0000000 0000000 sparse-0.6.4/Documentation/release-notes/index.rst 0000664 0000000 0000000 00000000357 14115310122 0022231 0 ustar 00root root 0000000 0000000 *************
Release Notes
*************
.. toctree::
:maxdepth: 1
v0.6.4
v0.6.3
v0.6.2
v0.6.1
v0.6.0
v0.5.2
v0.5.1
v0.5.0
v0.4.5-rc1
v0.4.4
v0.4.3
v0.4.2
v0.4.1
v0.4
v0.3
v0.2
v0.1
sparse-0.6.4/Documentation/release-notes/v0.1.rst 0000664 0000000 0000000 00000004652 14115310122 0021610 0 ustar 00root root 0000000 0000000 v0.1 (2006-11-06)
=================
I have tagged and tarballed a 0.1 release of Sparse, now available
from http://ftp.be.debian.org/pub/software/devel/sparse/dist/, with sha1sum 9e0a4d5abb8e8a4be4cf8d9fe632c69dbec3e242.
As discussed in [http://marc.info/?l=linux-sparse&m=116231992212971 Re: Official releases?],
I've taken maintainership of sparse. Thanks to Linus Torvalds for his
previous maintainership. As a result, this release comes from my sparse Git
repository. You can find more information about obtaining sparse via Git at
the [https://www.kernel.org/pub/linux/kernel/people/josh/sparse/ new sparse homepage].
In addition to all the work in the
[https://www.kernel.org/pub/scm/devel/sparse/sparse.git/ previous Sparse repository],
this release includes the following changes:
Adam DiCarlo (1):
* Add type information to enum mismatch warning
Al Viro (2):
* added a bunch of gcc builtins
* switch to hash-based get_one_special()
Josh Triplett (15):
* "Initializer entry defined twice" should not trigger with zero-size fields
* Fix incorrect symbol in comment on #endif for multiple-inclusion guard
* Add -Wno-uninitialized
* graph: Show position in basic block nodes
* bb_terminated: Use boundary values rather than specific opcodes
* Turn on -Wcontext by default
* Merge branch 'fix-defined-twice-error-on-empty-struct' into staging
* Merge branch 'graph' into staging
* merge branch 'more-warning-flags' into staging and fix conflicts
* merge branch 'no-semantic-h' into staging and fix conflicts
* Merge branch 'Wcontext-default' into staging
* Add test cases to validation/context.c for the Linux __cond_lock macro
* Merge branch 'context-test-cases-for-cond-lock' into josh
* Rename test case bad-assignement.c to bad-assignment.c, fixing the typo.
* Stop building and installing libsparse.so
Josh Triplett and Pavel Roskin (1):
* Recognize and ignore __alias__ and __visibility__
Pavel Roskin (4):
* Compile sparse executable under it's own name, not as "check"
* Add support for __builtin_strpbrk()
* Typo fixes
* Install cgcc on "make install", refactor installation code
Known issue with this release:
* Sparse does not produce the expected set of warnings for several of the validation programs, included in the sparse source in the directory 'validation/'. Some scripts should provoke warnings but don't, and others provoke warnings they shouldn't.
-- Josh Triplett
sparse-0.6.4/Documentation/release-notes/v0.2.rst 0000664 0000000 0000000 00000003112 14115310122 0021577 0 ustar 00root root 0000000 0000000 v0.2 (2006-12-05)
=================
I have tagged and tarballed a 0.2 release of Sparse, now available from http://ftp.be.debian.org/pub/software/devel/sparse/dist/, with sha1sum 1762fc609fe436e74b87356a52690b5f7bb40c81.
In addition to plenty of bug fixes, this release includes several notable new
features:
* -Wall, thanks to Pavel Roskin
* '#strong_define' and '#strong_undef', thanks to Oleg Nesterov
* Argument parsing functions no longer mangle the argv passed to them, thanks to Christopher Li
* static library and header files now installed, along with a pkg-config file to find them
* Makefile now supports DESTDIR, useful for packagers
Full changelog:
Christopher Li (4):
* trivial fix for seg fault.
* Fix warning on self check.
* delay removing file scope
* cleanup write to argument array hack
Damien Lespiau (1):
* trivial: more .gitignore stuff
Josh Triplett (5):
* Update the FAQ: add sparse website and gitweb, update git URL, remove old BK url
* Rename "check.c" to "sparse.c" to match program name; update .gitignore
* Install static library and header files
* Generate and install a pkg-config file. Add DESTDIR support to Makefile.
* Remove old SCCS target from Makefile.
Nicolas Kaiser (1):
* double inclusions
Oleg Nesterov (7):
* use lookup_macro() in handle_undef()
* kill NS_INVISIBLEMACRO, introduce NS_UNDEF
* fix redefine of #weak_define
* fix 'weak' attribute loss
* prepare for #strong_{define,undef}
* implement #strong_define
* implement #strong_undef
Pavel Roskin (1):
* Support -Wall flag
-- Josh Triplett
sparse-0.6.4/Documentation/release-notes/v0.3.rst 0000664 0000000 0000000 00000012405 14115310122 0021605 0 ustar 00root root 0000000 0000000 v0.3 (2007-05-01)
=================
I have tagged and tarballed a 0.3 release of Sparse, now available from
http://ftp.be.debian.org/pub/software/devel/sparse/dist/ with sha1sum 1d868b29234176abd5f3f5463aad1f11d5268dc2.
Note that the Sparse Git repository has moved to:
https://git.kernel.org/cgit/devel/sparse/sparse.git
The old repository location will continue to work for now, but please update
any references you have to the old location.
Thanks to Christopher Li for contributing heavily to this release, including
several notable new features. See the full changelog for details.
In addition to numerous bug fixes, cleanups, and new test cases, this release
includes several new visible features:
* A Sparse-based implementation of ctags, thanks to Christopher Li. Now you can have a ctags that 'actually parses C'.
* Sparse now correctly supports annotations on inline functions.
* The new '-ventry' option will make Sparse dump its internal linearized bytecode format.
* Sparse now parses the '__regparm__' attribute just like 'regparm', fixing a warning with pthreads from glibc 2.5.
* Sparse now interprets the integer size attributes 'QI', 'SI', 'HI', and 'DI', and their double-underscore variants.
* Sparse now supports attributes on labels, sometimes used to specify 'unused' labels.
* Sparse now handles structure attributes between the structure keyword and the name, fixing many parse errors.
* Sparse now supports more than one command-line include file.
* The pkg-config file gets installed to its correct location under 'lib', rather than under 'share'.
Notable internal changes:
* Fully table-driven attribute parsing. This simplifies the C parser and makes it far easier to add new attributes to Sparse.
* Many internal cleanups, more information preserved for backends, more useful formats for that information, and lower memory usage.
* Fixed Sparse warnings about itself.
Full changelog:
Christopher Li (24):
* Sparse-based Ctags implementation
* Change the symbol access list to a pseudo list
* Add instruction to pseudo user tracking.
* Update usage chain for dead instructions
* Update usage chain for dead branch instruction.
* Allow more than one command line include file.
* Enhance debug information.
* Another attempt to fix the attribute parsing.
* Marking anonymous string.
* Bug fix in pointer modifier inheritance at function degeneration.
* Handle structure attributes between the structure keyword and the name
* Fix the segfault when initializer has unknown symbol
* Fix double semicolon in struct declaration
* Make the ptrlist using the sparse allocator.
* Fix core dump on anonymous symbol.
* Fix a bug that match_idents forget to end with NULL
* Adding debug option for showing the linearized instruction.
* Fix core dump on huge switch
* Introduce expression_error
* Disable liveness "dead" instruction by default.
* Add annotation for inline function call.
* Introduce keyword driven attribute parsing
* Fix the annotated inline call position
* handle label attributes
Christopher Li and Josh Triplett (4):
* Introduce top level parsing for asm parsing.
* Introducing statement keywords
* Free up some special bits in modifiers.
* Moving statement parsing into smaller functions.
James Westby (2):
* Fix mistaken comparison that becomes a no-op.
* Update the information in README about using the library.
Josh Triplett (30):
* Add ctags to .gitignore
* Add a return in the last case of a switch; redundant but less error-prone.
* Coding style fix: in a pointer type, * goes with the name, not the type.
* Add missing #include "allocate.h" in linearize.h for DECLARE_ALLOCATOR.
* Add test case for function pointer modifier inheritance
* Add test case for structure attribute placement.
* Add test case for double semicolon in structure declaration.
* Coding style fix: use parentheses with sizeof
* Move pkg-config file to lib, rather than share
* Add static to declarations in test cases, to remove unrelated warnings.
* Fix typo in symbol.h: s/keywrods/keywords/
* Fix typos in comments
* Use GCC format and sentinel attributes on appropriate functions
* Fix two potential NULL pointer dereferences in dissect.c
* Avoid returning an uninitialized pointer from dup_list of an empty list
* Remove stray space from expand_compare in expand.c
* Prevent potential NULL pointer dereference in expand_compare
* Add test case for basic address_space annotations.
* Use noreturn on die() and error_die()
* Parse and ignore the __regparm__ attribute, just like regparm.
* Fix comment to reference #weak_define rather than #ifndef, matching code
* Teach cgcc about -Wtransparent-union and -Wno-transparent-union
* Declare die_if_error extern in lib.h
* Remove unused variable "include" from lib.c
* Declare do_error static
* Declare gcc_patchlevel extern in lib.h
* compile-i386.c: Declare regs_in_use static
* simplify.c: Declare delete_pseudo_user_list_entry static
* linearize: DECLARE_ALLOCATOR for asm_constraint and asm_rules
* Fix most -Wshadow warnings in Sparse.
Oleg Nesterov (3):
* dissect: cleanup report_implicit()
* dissect: fix multidimensional array initializer
* dissect: simplify lookup_member()
-- Josh Triplett
sparse-0.6.4/Documentation/release-notes/v0.4.1.rst 0000664 0000000 0000000 00000001475 14115310122 0021752 0 ustar 00root root 0000000 0000000 v0.4.1 (2007-11-13)
===================
I have tagged and tarballed Sparse 0.4.1, now available from http://ftp.be.debian.org/pub/software/devel/sparse/dist/ with sha1sum 14085c5317cd7f2c8392fb762969906fa91888ef.
This bugfix release fixes a Sparse assertion which recent Linux kernels started
triggering, along with a few other fixes.
Full changelog:
Christopher Li (1):
* Perform local label lookup
Emil Medve (1):
* Handle ignored attribute malloc
Josh Triplett (4):
* Add comment on taint flags enum referencing expr->taint
* Add test-suite metadata to validation/local-label.c
* Add known-to-fail test case for a static forward declaration
* Makefile: VERSION=0.4.1
Mike Frysinger (1):
* fix install perms of manpages
Tilman Sauerbeck (1):
* Added a prototype for mempcpy().
-- Josh Triplett
sparse-0.6.4/Documentation/release-notes/v0.4.2.rst 0000664 0000000 0000000 00000014635 14115310122 0021755 0 ustar 00root root 0000000 0000000 v0.4.2 (2009-10-16)
===================
I have tagged and released the sparse version 0.4.2 at http://ftp.be.debian.org/pub/software/devel/sparse/dist/ with sha1sum a2adef3f78c7409e8c0bb80941f473d775afcac4
As previously discussed on the sparse mailing list, I am the new maintainer of
the sparse project. This is my first release for sparse.
Thanks Josh Triplett for the previous maintenance of the project.
I also created a new sparse wiki, it will replace the current sparse
home page. https://sparse.wiki.kernel.org/
A lot of bug fixes and enhancements have gone into this release.
Special thanks to Al Viro for overhauling the parser. Now sparse
has better ctype and attribute handling. The detailed changes follow.
Al Viro (39):
* saner warnings for restricted types
* fix show_typename()
* catch !x & y brainos
* fun with declarations and definitions
* Fix type_info_expression()
* fun with declarations and definitions
* Fix handling of ident-less declarations
* Separate parsing of identifier-list (in K&R-style declarations)
* More nested declarator fixes
* Fix attribute/asm handling
* more direct_declarator() sanitizing
* Warn about non-empty identifier list outside of definition
* Apply attributes after ( to the right place
* Leave applying attributes until we know whether it's a nested declarator
* Don't mess with passing symbol to declarator/direct_declarator
* Fix braino in which_kind()
* Sanitize direct_declarator logics
* Separating ctype and parser state, part 1
* Propagate decl_state to declaration_specifiers()
* Fix regression created by commit af30c6df74f01db10fa78ac0cbdb5c3c40b5c73f
* Take the rest of storage class keywords to parse.c
* Fix handling of typedefs with several declarators
* preparations to ->declarator() cleanup - separate typedef handling
* Take the rest of specifiers to parse.c
* Saner type for __builtin_va_list
* Rewrite and fix specifiers handling
* Have ->declarator() act directly on ctype being affected
* Clean up and split declaration_specifiers()
* Pass decl_state down to ->declarator() and handle_attributes()
* Pass decl_state down to ->attribute()
* Restore __attribute__((mode)) handling
* Fix enumeration constants' scope beginning
* Fix declaration_specifiers() handling of typedef name shadowed by NS_SYMBOL
* Fix __label__ handling
* Simplify get_number_value() and ctype_integer()
* Don't mix storage class bits with ctype->modifiers while parsing type
* Sanitize pointer()
* Segfault at evaluate.c:341
* warn directive in argument list
Alberto Bertogli (1):
* Support the __thread storage class
Alexander Shishkin (1):
* don't call sparse when called to generate dependencies
Alexey Zaytsev (16):
* Remove symbol.id_list
* Replace the -specs cgcc option with -target
* Make show_symbol newline-consistent
* Handle a terminal -o option properly.
* Looks more evident this way.
* Mark handle_switch as static and don't export it from lib.h
* Handle missing argument to -D.
* Gdb macros to get a better look at some sparse data structures.
* A slightly edited irc discussion with Josh Triplett.
* Warning should be enough for an unhandled transparent union
* Set gcc include path at runtime.
* Let cgcc pass -gcc-base-dir to sparse.
* Document -gcc-base-dir in sparse.1
* Rename dirafter to idirafter.
* Let void have sizeof 1
* Also warn about sizeof(function)
Blue Swirl (6):
* Sparc64 (Sparc V9, LP64) support
* OpenBSD support
* Ignore attribute __bounded__, used by OpenBSD headers.
* Add c{l,t}z{,l,ll}, ffsl{,l}, popcountll and floating point comparison builtins.
* Add support for TImode type (__int128_t)
* Define __LP64__ for x86_64 unless in 32 bit mode
Christopher Li (11):
* Evaluate iterator symbols
* Remove pre_buffer
* Add enum member list to the parent
* Teach classify_type to handle typeof
* Warn about explicit usage of sizeof(void)
* Makefile automatic header dependency
* Clean up Makefile long lines
* Update the validation check for ftabstop=
* Add validation for restrict and attribute warning
* move extern inline function to file scope
* Sparse 0.4.2
David Given (2):
* Unhardcode byte size being 8 bits.
* Add type information to struct instruction.
Geoff Johnstone (4):
* Add support for GCC's -std=... and -ansi command line options.
* Add builtin functions for use with __FORTIFY_SOURCE
* Fix type mismatches with incomplete types
* Add -Wno-declaration-after-statement
Hannes Eder (4):
* Add -ftabstop=WIDTH
* refactor handle_switch_f
* test-suite: be more verbose on 'unhandled' and 'known to fail' tests
* test-suite: integrate unhandled proprocessor tests
Johannes Berg (8):
* cgcc: handle ppc arch
* make sparse keep its promise about context tracking
* sparse test suite: add test mixing __context__ and __attribute__((context(...)))
* sparse: simple conditional context tracking
* inlined call bugfix & test
* improve -Wcontext code and messages
* fix bug in context tracking code
* Revert the context tracking code
Josh Triplett (2):
* Add test case for new warning about !x & y
* Expand "dubious !x & y" handling to other combinations of !, &, and \|.
Kamil Dudka (4):
* compile-i386: do not generate an infinite loop
* linearize.h: sanitize header
* unssa: track uses when replacing a phi node
* make sparse headers self-compilable...
Linus Torvalds (5):
* Fix cast instruction generation
* Simplify (and warn about) right shifts that result in zero
* Allow array declarators to have 'restrict' in them
* Turn off '-Wtransparent-union' by default
* Avoid "attribute 'warning': unknown attribute" warning
Martin Nagy (3):
* .gitignore: Ignore dependencies and Vim swap files
* Add missing checks for Waddress-space
* Print an error if typeof() lacks an argument
Pavel Roskin (1):
* Ignore "cold" and "hot" attributes, which appeared in gcc 4.3
Pekka Enberg (1):
* sparse: Add GCC pre-defined macros for user-space
Ramsay Jones (1):
* Makefile: suppress error message from pkg-config
Reinhard Tartler (1):
* show_token: handle TOKEN_UNTAINT and TOKEN_ARG_COUNT types
Samuel Bronson (1):
* Have Makefile import local.mk if it exists.
Thomas Schmid (1):
* Fix implicit cast to float
Vegard Nossum (2):
* Fix use of invalid file descriptor
* Set \*tree to NULL on error
-- Chris Li
sparse-0.6.4/Documentation/release-notes/v0.4.3.rst 0000664 0000000 0000000 00000004503 14115310122 0021747 0 ustar 00root root 0000000 0000000 v0.4.3 (2010-11-02)
===================
Hi,
It is final there. The sparse version 0.4.3 is released.
Mostly small fix up. It can parse the recent kernel better, less noise.
For people interested in the sparse internals, there is a sparse inspecting
tools now. Currently it has limited knowledge of AST. It is very easy to
extent though.
Thanks every one for the contribution.
Chris
--
Bernd Petrovitsch (1):
* Fix a typo - "typdef" is neither C nor plain English
Christopher (3):
* evaluate: check for NULL type inside typeof
* Add test case for builtin_unreachable()
* inspect: add some expression inspection
Christopher Li (15):
* Make MOD_NORETURN fits into 32 bit
* Move noreturn attribute out of ignore attr area
* Declare ignored attributres into a list of string.
* Simplify Makefile using static pattern rules
* Adding test case for "x && y && z" .
* Pointer don't inherent the alignment from base type
* Allow parsing L'\0'
* Parsing wide char string
* Adding asm goto label test case
* inspect: add custom ast treeview model
* inspect: add some example inspect for symbol and statement
* inspect: Add test-inspect program
* inspect: cast expression
* Fixup and cleanup modifier_string() function.
* sparse 0.4.3 finial
Damien Lespiau (1):
* Ignore the may_alias GCC attribute
Dan Carpenter (1):
* add test-inspect to .gitignore
Dan McGee (1):
* Makefile: fix permissions mixup on install
Daniel De Graaf (1):
* Fix incorrect linearization of "x && y && z"
Jiri Slaby (3):
* parser: add support for asm goto
* parser: fix and simplify support of asm goto
* parser: define __builtin_unreachable
Joel Soete (1):
* possible fix to cgcc issue in sparse 0.4.2:
Josh Triplett (2):
* Rename -Wall to Wsparse-all, so it doesn't get turned on unintentionally
* New attribute designated_init: mark a struct as requiring designated init
Kamil Dudka (1):
* do not ignore attribute 'noreturn'...
Michael Buesch (2):
* ignore attributes "externally_visible" and "signal"
* Ignore "naked" attribute
Michael Stefaniuc (3):
* Ignore the ms_abi/sysv_abi attributes.
* Ignore the alloc_size attribute.
* Handle __builtin_ms_va_list.
Mike Frysinger (1):
* parser: add Blackfin gcc info
Morten Welinder (1):
* skip may_alias and declare builtin_fabs
sparse-0.6.4/Documentation/release-notes/v0.4.4.rst 0000664 0000000 0000000 00000005363 14115310122 0021755 0 ustar 00root root 0000000 0000000 v0.4.4 (2011-11-25)
===================
This is a long due release. The sparse 0.4.4 can be downloaded from:
https://www.kernel.org/pub/software/devel/sparse/dist/
The new sparse has a lot of bugs fixes. It will report less noise
while checking the new kernel tree. It compiles better with the
new gcc as well.
The sparse project is in the process of moving to the MIT license.
Dan is coordinating the efforts. Most sparse developers sign off
the MIT license already. If you haven't done so, please contact
Dan off the list regarding the new license.
Here is the short summery of the changes in this release.
Have a nice long week end.
Chris
Ben Pfaff (1):
* evaluate: Allow sizeof(_Bool) to succeed.
Christopher Li (13):
* inspect: adding function arugument list
* Allow overwrite CFLAGS from command line
* Ignore attribute vector_size
* Remove set but not used variable
* inspect: Add switch statement and more
* validation: inline switch statement
* Fix inlining switch statement.
* Sparse 0.4.4-rc1
* Add test case for empty asm clobbers
* Fix parsing empty asm clobber
* Sparse 0.4.4-rc2
* Add test case for binary constants
* sparse 0.4.4
Dan Carpenter (1):
* recognize binary constants
Diego Elio Pettenò (3):
* build: allow easy override of GCC_BASE
* build: add an all-installable target that builds the targets to install.
* Fix build with GCC 4.6 series.
Florian Fainelli (1):
* Makefile: warn user when libxml and/or libgtk2 are not available
Jan Pokorný (4):
* remove unused "container" macro
* flow.c: make comment for 'dominates' reflect code
* use ARRAY_SIZE() when possible (continued)
* parse.c: "if(" -> "if (" adjustment
Jonathan Neuschäfer (3):
* fix a memory leak in compile-i386.c
* FAQ: fix a typo ("because or")
* fix common misspellings with codespell
Kamil Dudka (2):
* cse: treat PHI-nodes as other instructions
* cse: update PHI users when throwing away an instruction
Linus Torvalds (5):
* Add new streams to a hash-list based on their names
* Teach 'already_tokenized()' to use the stream name hash table
* Make 'linearize_iterator()' helper function
* Make 'linearize_switch()' helper function
* Make 'linearize_return()' helper function
Michael Stefaniuc (1):
* Ignore the ms_hook_prologue attribute.
Namhyung Kim (3):
* use ARRAY_SIZE() when possible
* Fix tokenizer for octal escape sequences
* Update the validation check for escape sequences
Nicolas Kaiser (1):
* memops.c: always true expression
Pekka Enberg (4):
* sparse: Add 'artifical' to ignore attributes
* sparse: Enable unhandled validation tests
* Show expected vs. actual output on test failure
* sparse: Fix __builtin_safe_p for pure and const functions
sparse-0.6.4/Documentation/release-notes/v0.4.5-rc1.rst 0000664 0000000 0000000 00000000410 14115310122 0022425 0 ustar 00root root 0000000 0000000 v0.4.5-rc1 (2013-05-09)
=======================
I just push the tag for sparse 0.4.5-rc1
It is about time to cut a new release. There have been a lot of
small improvements. It produces less warning on the recent kernel
check.
Please give it a good test.
Chris
sparse-0.6.4/Documentation/release-notes/v0.4.rst 0000664 0000000 0000000 00000034235 14115310122 0021613 0 ustar 00root root 0000000 0000000 v0.4 (2007-09-15)
=================
I have tagged and tarballed Sparse 0.4, now available from
http://kernel.org/pub/software/devel/sparse/dist/sparse-0.4.tar.gz,
with sha1sum a77a10174c8cdb5314eb5000c1e4f24458848b91.
Highlights and visible changes in this release:
* The Sparse validation files have become an automated test suite, invoked via 'make check'. Thanks to Damien Lespiau for the initial 'test-suite' script. Also thanks to Pavel Roskin for ideas, discussion, and prototyping, and to the contributors of new test cases and test-suite metadata for existing test cases.
* New backend 'c2xml' by Rob Taylor, which outputs an XML representation of the variable declarations, function declarations, and data structures in a C file.
* 'sparse' and 'cgcc' now have manpages.
* Warning changes:
* Warn on 'return ;' if given '-Wreturn-void'; off by default because the C99 standard permits this.
* Sparse now defaults to -Wno-do-while.
* Sparse now defaults to -Wdecl.
* -Wno-old-initializer turns off warnings about non-C99 struct initializers
* -Wno-non-pointer-null turns off warnings about using a plain integer as a NULL pointer
* Initializer entries defined twice and bitfields without explicit signs have changed from errors to warnings.
* 'cgcc' no longer passes '-Wall' to 'sparse' if given '-Wall' on the command line. '-Wall' turns on all Sparse warnings, including experimental and noisy ones. Don't include it just because a project wants to pass '-Wall' to 'cc'. If you really want 'cgcc' to run 'sparse' with '-Wall', set 'CHECK='sparse -Wall''
* Support for more builtin functions: '__builtin_labs', '__builtin_offsetof', '__builtin_strcat', '__builtin_strncat', and '__builtin_strlen'.
* Support for more attributes: 'constructor', 'destructor', '__always_inline__', '__noinline__', 'used', '__syscall_linkage__', 'format_arg', 'cdecl', 'stdcall', 'fastcall', 'dllimport', and 'dllexport'.
* Support for the '__DATE__' and '__TIME__' preprocessor symbols.
* Support for '__alignof' (treated like the existing '__alignof__').
* typeof(bitwise_type) now produces the same type, not an incompatible type; thanks to Al Viro.
* Various profile-driven optimizations and changes to compiler optimization flags, making Sparse significantly faster.
* Numerous fixes to C standards compliance, thanks to Al Viro. In particular:
* Sparse now requires integer constant expressions in various places, not arbitrary expressions.
* Sparse now handles NULL pointer constants more correctly.
* Sparse now resolves pointer types more correctly in conditional expressions
* The 'graph' backend now outputs significantly better program graphs, and the Sparse source code includes some 'gvpr' scripts to modify these graphs in various ways, such as only showing the callers or callees of particular functions. Thanks to Dan Sheridan.
* Linux-style 'make' output; for a more verbose make with full command lines, 'make V=1'. Thanks to Damien Lespiau.
* Numerous bugfixes and internal cleanups; thanks to the many Sparse contributors.
Full changelog:
Al Viro (51):
* handle __alignof as equivalent of __alignof__
* saner reporting of overlaps in initializers
* check for whitespace before object-like macro body
* fix alignment for _Bool
* fix interaction of typeof with bitwise types
* better recovery from bad operations on bitwise
* make copying of EXPR_INDEX non-lazy
* tie the fields of struct in simple list
* rewrite of initializer handling
* fix handling of typeof on structs
* missing NULL checks in initializer handling
* take cast_to() out of usual_conversions(), do it in callers
* mechanically split compatible_assignment_types()
* null pointer constants have no special meaning for pointer subtraction
* remove long-dead variable in evaluate_ptr_add()
* remove useless argument in evaluate_ptr_sub()
* cleanup of evaluate_assign_op()
* clean up the typechecking in arithmetics
* clean up usual_conversions(), kill evaluate_shift()
* fix index conversions in evaluate_ptr_add()
* fix default argument promotion
* move degenerate() down into compatible_assignment_types()
* in case of compound literal we want to delay examining type
* warn on return ;
* deal with enum members without excessive PITA
* implement __builtin_offsetof()
* fix handling of integer constant expressions
* fix the comma handling in integer constant expressions
* first pass at null pointer constants
* make size_t better approximate the reality
* fix handling of address_space in casts and assignments
* fix handling of pointers in ?:
* saner show_type()
* clean up evaluate_sign()
* integer_promotions() can't get SYM_NODE or SYM_ENUM
* start cleaning type_difference()
* get compatible_assignment_types() deal with all cases
* fix the sanity check in evaluate_ptr_sub()
* rewrite type_difference()
* deal correctly with qualifiers on arrays
* add __builtin_strlen()
* no such thing as array of functions
* new helper: unfoul()
* handling of typeof in evaluate_member_dereference()
* file and global scopes are the same for purposes of struct redefining
* ...,array should degenerate
* sanitize evaluate_ptr_add(), start checking for pointers to functions
* fix evaluate_compare()
* sanitize evaluate_postop()
* saner -Wtypesign
* braino in conditional_expression()
Alberto Bertogli (1):
* Implement x86-64 support in cgcc.
Alexey Dobriyan (2):
* Fix infinite loop in free_preprocessor_line()
* Fix -E handling
Christopher Li (2):
* combinations string clean up
* Pass a bitmask of keywords to handle_attributes
Damien Lespiau (6):
* Change sparse homepage in ctags headers.
* __DATE__ & __TIME expansion
* Beautify all & install Makefile targets
* test-suite: a tiny test automation script
* test-suite documentation
* Sample test-suite test cases
Dan Sheridan (2):
* Improved graph generation using subgraph clusters for functions
* Add gvpr-based post-processing for graphs
Josh Triplett (118):
* Fix website and repository references in FAQ
* Fix the version number
* Remove old version note.
* gitweb lives at git.kernel.org now.
* Add a "make dist" that requires $(VERSION) to match 'git describe'
* Add test case for __asm__ __volatile__(...)
* Make cgcc not pass -Wall to sparse even if passing it to cc
* Teach cgcc about all currently existing sparse warning options
* Teach cgcc about -ventry and -vdead
* Parse asm after a label as a statement, not an attribute
* Add test case for stdcall and cdecl attributes.
* Add -Wno-old-initializer to turn off warnings about non-C99 struct initializers
* Add test case for -Wno-old-initializer
* Revert unintentional inclusion of warning fix in previous commit.
* Use %td when printing a ptrdiff_t to avoid problems on 64-bit platforms
* Remove extra space.
* Add shebang to gvpr scripts, make them executable, and change usage accordingly
* Fix an __attribute__() parsing error
* Expand calling convention test case to cover fastcall
* Add -Wno-non-pointer-null to turn off warning about using a plain integer as a NULL pointer
* Add __builtin_strcat and __builtin_strncat.
* Ignore the GCC constructor and destructor attributes
* Remove inaccurate comment designating some attributes as windows-specific.
* Move the ident for defined() into the preprocessor section.
* Reorganize attribute list for readability.
* Add double-underscore variant __always_inline__.
* Add double-underscore variant __noinline__.
* Add no-double-underscore variant "used", ignored like "__used__".
* Add double-underscore variant __syscall_linkage__.
* Add no-double-underscore variant format_arg.
* Add explanatory comment about direct use of __IDENT for preprocessor idents.
* Sparse always defines __STDC__ 1, so cgcc does not need to do so
* Fix old typo: s/wierd/weird/
* Canonicalize URL in FAQ: add www., add trailing slash
* Change "LD" to "LINK" in Makefile prettyprinting.
* Makefile prettyprinting: make INSTALL and other output line up correctly
* Add test case for infinite loop in free_preprocessor_line()
* Turn on -Wdecl by default.
* ctags: Use const as appropriate in cmp_sym()
* validation/old-initializer.c: Make the_s static to avoid extraneous warning.
* validation/restricted-typeof.c: Make globals static to avoid extraneous warnings.
* validation/escapes.c: Make globals static to avoid extraneous warnings.
* validation/non-pointer-null.c: Make global static to avoid extraneous warning.
* Merge commit 'viro/integer-constant'
* Move all the preprocessor tests into validation/preprocessor/
* Move test-suite output files to validation/.gitignore
* .gitignore: Stop ignoring all dotfiles
* validation: Update comments for current Sparse behavior and test-suite.
* Add test-suite comments to all the obvious preprocessor tests
* Make preprocessor-loop a normal numbered preprocessor test
* Add test-suite comment to preprocessor21.
* Add test-suite comment to address_space.c
* Make clean depend on clean-check
* Rename asm-volatile to better describe what it tests
* Add test-suite comment to label-asm.c
* Remove "check-exit-value: 0" and rely on default; remove extra blank line.
* Add test-suite comment to bad-array-designated-initializer.c
* Add c2xml to .gitignore
* Split c2xml build rule into compile and link stages, and add the quiet prefixes
* expression.h needs lib.h for struct position and symbol.h for int_ctype
* Fix GCC warnings in c2xml
* Fix sparse warnings in c2xml: mark globals static and remove unused globals
* Fix test-suite to handle stdout and stderr separately, and fix up tests
* Add test-suite metadata to bad-cast.c
* Add test-suite metadata to bad-ternary-cond.c, and remove now-redundant comment
* Add test-suite metadata to initializer-entry-defined-twice.c
* Add test-suite metadata to context.c
* Add test-suite metadata to escapes.c
* Add test-suite metadata to calling-convention-attributes.c
* Fix typos in test-suite documentation
* Makefile: stop cleaning files we didn't make and have no business cleaning
* Add test-suite metadata to old-initializer.c; also test with -Wno-initializer
* allocate.h: Stop needlessly returning a void value in __DO_ALLOCATOR
* Turn off -Wdo-while by default.
* Add test-suite metadata to label-attr.c
* validation/builtin_safe1.c: Show the unsafe macro argument
* Make "Initializer entry defined twice" a warning, not an error
* Remove explicit restatements of defaults in metadata for member_of_typeof test
* Remove explicit restatements of defaults in metadata for outer-scope test
* Remove explicit restatements of defaults in metadata for comma test
* Add test case for comparing null pointer constant to int.
* Makefile: Use -O2 -finline-functions, not just -O
* cse: Size insn_hash_table more realistically, speeding up CSE significantly
* Add some missing dependencies in the Makefile
* Drop -fpic; it hurts performance and we don't build libsparse.so by default
* Add another test case to validation/comma.c
* ctags: Handle some new namespaces and symbol types.
* is_zero_constant: declare saved const
* Add test case for -Wtypesign
* Sort warning options in lib.c and lib.h
* Rename Wcast_to_address_space to Wcast_to_as to match the command-line argument
* Add a manpage for sparse
* Install the Sparse manpage
* cgcc: Sparse accepts -Wcast-to-as, not -Wcast-to-address-space
* Rename Wundefined_preprocessor to Wundef to match the command-line argument
* cgcc: Sparse accepts -Wundef, not -Wundefined-preprocessor
* Use -fno-strict-aliasing, as the ptrlist code seems to violate C99 strict aliasing rules
* Add test-suite annotations to restricted-typeof.c
* Add test-suite annotations to double-semicolon.c
* Add test-suite annotations to check_byte_count-ice.c
* Add test-suite annotations to badtype4.c
* Add test-suite annotations to varargs1.c
* Add test-suite annotations to struct-attribute-placement.c
* Add test-suite annotations to non-pointer-null.c
* Add test-suite annotations to struct-ns1.c
* Add test-suite annotations to noderef.c
* Makefile: Use ?= to allow overriding OS or AR on the Make command line
* FAQ: Point to URL on vger for subscription instructions and archives
* README: recode from ISO-8859-1 to UTF-8
* validation: Rename typeconvert.c to integer-promotions.c to match its purpose
* Add test-suite annotations to integer-promotions.c
* Add test-suite annotations to cond_expr.c
* Add test-suite annotations to function-pointer-modifier-inheritance.c
* validation: Update comment in type1.c to reflect current state of Sparse
* Add test-suite annotations to init-char-array.c
* Add a manpage for cgcc
* Add SEE ALSO for cgcc in sparse manpage
* Makefile: VERSION=0.4
Kovarththanan Rajaratnam (1):
* libxml compile fix on Cygwin
Michael Stefaniuc (3):
* Ignore the cdecl and stdcall attributes for now.
* Add test for typedef on pointer to function with stdcall attribute.
* '\?' is a valid escape character defined by ANSI C. Its value is '?'.
Mike Frysinger (1):
* Makefile: improve flag handling
Pavel Roskin (5):
* Improve error message if using a member of an incomplete struct or union
* Bitfield without explicit sign should be a warning, not an error
* cgcc: preserve sparse exit code if -no-compile is used
* Avoid use of libc headers in the validation suite
* Fix warnings about undeclared globals, they are irrelevant to the test
Ramsay Jones (2):
* Add (more) support for WIN32 attribute names
* Add cygwin support to cgcc
Randy Dunlap (1):
* add __builtin_labs()
Rob Taylor (4):
* add end position to symbols
* add sparse_keep_tokens api to lib.h
* new get_type_name function
* add c2xml program
Yura Pakhuchiy (1):
* Make cgcc filter out all sparse warning related options
ricknu-0@student.ltu.se (4):
* tokenize.c: Replace handwritten strncmp with existing function.
* expression.c: Clean up match_oplist() and add missing va_end()
* parse.c: Adding va_end().
* tokenize.c: Simplify drop_stream_eoln().
-- Josh Triplett
sparse-0.6.4/Documentation/release-notes/v0.5.0.rst 0000664 0000000 0000000 00000023744 14115310122 0021755 0 ustar 00root root 0000000 0000000 v0.5.0 (2014-04-01)
===================
As a lot of you have already noticed. The sparse 0.5.0 has been
tagged and upload to the kernel.org for a while.
One of the most noticeable change of the sparse 0.5.0 is the
license. The sparse project has finally move to the MIT license.
Thanks for the hard work of Dan Carpenter and Franz Schrober,
who contact and collect permissions from the sparse developers.
We actually manage to get *all* developer's blessing on the MIT
license.
This release also has some enhancement matching the latest
kernel source annotations. It will reduce the noise level of the sparse
warnings.
So there you have it. The official announcement of the sparse 0.5.0.
Chris
---
Al Viro (9):
* Fix handling of __func__
* Fix tab handling in nextchar_slow()
* Fix ,##__VA_ARGS__ kludge
* Gentler handling of bitwise warnings in unary operations
* simplify handling of newline/whitespace flags in expand()
* fix handling of -include
* massage handling of wide string literals/character constants in tokenizer
* switch to delayed handling of escape sequences
* L ## 'a' is valid; so's L ## "a"
Benjamin Herrenschmidt (1):
* sparse, llvm: Fix varargs functions
Christopher Li (19):
* Limit usage of g++ to llvm related programs.
* Merge branch 'sparse-llvm' of git://github.com/penberg/sparse-llvm.git
* Adding default for m64/m32 handle
* Merge branch 'for-chris' of git://github.com/penberg/sparse-llvm
* Merge branch 'llvm/core' of github.com:penberg/sparse-llvm
* remove weak define x86_64
* Merge git://git.kernel.org/pub/scm/linux/kernel/git/viro/sparse into marge
* Clean up some test case error.
* Fix segfault cause by fucntion without ident.
* Get rid of gcc warning about enum values
* Larger buffer size for token concatenation
* Proper variable length array warning
* Allow forced attribute in function argument
* Trivial: Remove redundant Makefile variable
* Merge branch 'llvmcore'
* Merge branch 'Novafora' of git://git.zytor.com/users/hpa/sparse/sparse
into license
* Sparse 0.5.0 rc1
* Fix make dist failure
* Sparse 0.5.0
Emilio G. Cota (1):
* gitignore: add 'version.h'
Ethan Jackson (1):
* sparse: Add 'leaf' to ignored attributes.
Franz Schrober (5):
* Revert "Update the information in README about using the library."
* Revert "Fix mistaken comparison that becomes a no-op."
* sparse: Relicense under the MIT license
* FAQ: Remove outdated sections about the license
* sparse: Also check bit_offset when checking implicit casts
Frederic Crozat (1):
* Add __builtin_stpcpy, __sync_synchronize, __sync_bool_compare_and_swap
to declare_builtin_functions
James Westby (1):
* Update the information in README about using the library.
Jan Pokorný (2):
* unssa: track use of newly added pseudo
* simplify: conservative handling of casts with pointers
Jeff Garzik (15):
* sparse, llvm: if-else code generation
* sparse-llvm: OP_SEL
* sparse-llvm: OP_SWITCH
* sparse-llvm: OP_LOAD
* sparse-llvm OP_PHISOURCE: replace copy with target=src pointer operation
* sparse, llvm: replace FIXME comment with assert(), following
existing style
* sparse, llvm: implement OP_CALL
* sparse, llvm: move OP_PHI code from switch statement to separate function
* sparse, llvm: move OP_CAST code to separate func. support FP casts.
* sparse, llvm: create helper for obtaining instruction's type
* sparse, llvm: store module-local functions on function reference list
* sparse, llvm: move OP_COPY support to separate function. Add FP support.
* sparse, llvm: support OP_STORE
* sparse, llvm: Fix loops, by properly handling OP_PHI forward references
* sparse, llvm: add loop testcase
Jeff Layton (1):
* sparse: add __builtin_va_arg_pack() and __builtin_va_arg_pack_len()
Joe Perches (1):
* There's no current way to know the version of sparse. Add --version to see it.
Jonathan Neuschäfer (9):
* FAQ: update the website address and call it Wiki
* ptrlist.c: fix a typo in a comment
* sparse, llvm: 'Verify' the LLVM module before writing it
* sparse, llvm: convert the condition of branch/select to bool
* sparse, llvm: Fix type of loaded values
* sparse, llvm: Fix resulting type of store address calculations
* sparse, llvm: de-duplicate load/store address calculation code
* sparse, llvm: base load/store address type on insn_symbol_type()
* sparse, llvm: add a struct access test case
Josh Triplett (2):
* Define __SIZEOF_POINTER__
* Support #pragma once
KOSAKI Motohiro (2):
* sparse: Add '__vector_size__' to ignored attributes
* sparse: Add 'error' to ignored attributes
Kamil Dudka (2):
* cse: treat PHI-nodes as other instructions
* cse: update PHI users when throwing away an instruction
Kim Phillips (2):
* sparse: add built-in byte swap identifiers
* sparse: add built-in atomic memory access identifiers
Linus Torvalds (1):
* sparse, llvm: Make function declaration accessible to backend
Masatake YAMATO (3):
* Warn about initialization of a char array with a too long
constant C string.
* Test case for -Winit-cstring option
* Add description for -Winit-cstring option
Mauro Dreissig (1):
* Fix wrong array size expression
Pekka Enberg (69):
* sparse, llvm: Initial commit
* sparse, llvm: Fix assert() in sparse code
* sparse, llvm: Fix global variable initialization
* sparse, llvm: Fix 'sparsec' when it's not in PATH
* llvm, sparse: Separate entry and exit basic blocks
* sparse, llvm: Add switch statement to output_insn()
* sparse, llvm: OP_RET/PSEUDO_VAL code generation
* sparse, llvm: Add support for OP_RET/PSEUDO_ARG
* sparse, llvm: Introduce 'struct function' to clean up code
* sparse, llvm: Add output_op_binary() stub
* sparse, llvm: Implement OP_ADD
* sparse, llvm: Add support for more binary ops
* sparse, llvm: Implement some binary comparison ops
* sparse, llvm: Move binop tests to validation/backend
* sparse, llvm: Implement OP_CAST
* sparse, llvm: Floating point support for binops
* sparse, llvm: Reorganize code generation tests
* sparse, llvm: Bitwise not operator codegen
* sparse, llvm: Kill ifdef'd unssa() call
* sparse, llvm: Kill debugging code
* Merge pull request #1 from jgarzik/hacks
* Merge pull request #2 from jgarzik/hacks
* sparse, llvm: Warn the user when we fall back to GCC
* sparse, llvm: Code generation for string constants
* sparse, llvm: Cleanup output_data()
* sparse, llvm: Fix OP_CAST to use zero-extend
* sparse, llvm: Improve sparsec front-end
* sparse, llvm: Fix PSEUDO_OP code generation
* sparse, llvm: Don't redefine module local functions
* Revert "sparse, llvm: Don't redefine module local functions"
* sparse, llvm: Fix code generation for casts
* sparse, llvm: Fix pseudo_type() for PSEUDO_ARG
* Merge pull request #3 from jgarzik/hacks
* Merge branch 'master' of github.com:penberg/sparse-llvm
* llvm, sparse: Fix symbol_is_fp_type() goof
* Merge pull request #4 from jgarzik/hacks
* sparse, llvm: Fix code generation for 'long double' data type
* sparse, llvm: Add support for struct types
* sparse, llvm: Add support for symbol initializers
* sparse: Bump up sizeof(_Bool) to 8 bits
* sparse, llvm: Add support for logical ops
* sparse, llvm: Fix 'void \*' pointer code generation
* sparse, llvm: Use new LLVM type system API for structs
* sparse, llvm: Fix struct code generation
* sparse, llvm: Fix symbol_type() for bitfields and enums
* sparse, llvm: Add support for array types
* sparse, llvm: Add support for union types
* sparse, llvm: Make 'sparsec' error handling more robust
* sparse, llvm: Function pointer code generation
* sparse, llvm: Fix symbol initializer code generation
* sparse, llvm: Fix 'extern' symbol code generation
* sparse, llvm: Make llc output to stdout in sparsec
* sparse, llvm: Pointer cast code generation
* sparse, llvm: OP_SET_B and OP_SET_A code generation
* sparse, llvm: More comparison ops code generation
* sparse, llvm: Simplify comparison op code generation
* sparse, llvm: FP comparison op code generation
* Merge pull request #6 from jgarzik/hacks
* sparse, llvm: Don't fail the build if LLVM is too old
* sparse, llvm: Use LLVMInt1Type() in sym_basetype_type()
* sparse, llvm: Add test case for type
* Revert "sparse: Bump up sizeof(_Bool) to 8 bits"
* sparse, llvm: Add _Bool to cast validation test
* sparse, llvm: Simplify output_data() type logic
* sparse, llvm: Fix string initializer code generation
* sparse, llvm: Fix global string access code generation
* sparse, llvm: Fix SIGSEGV for extern symbols
* sparse, llvm: Fix 'void' return type code generation
* sparse, llvm: Use LLVM_HOSTTRIPLE
Ramsay Jones (2):
* char.c: Fix parsing of escapes
* symbol.c: Set correct size of array from parenthesized string initializer
Randy Dunlap (1):
* sparse patch v2: add noclone as an ignored attribute
Robert Bedichek (1):
* Novafora license grant using MIT license.
Shakthi Kannan (1):
* I have updated the sparse.1 man page including the __bitwise
relevant content, and created Documentation/sparse.txt with the
complete comparison between __nocast vs __bitwise.
Xi Wang (17):
* compile-i386: fix use-after-free in func_cleanup()
* check missing or duplicate goto labels
* fix casting constant to _Bool
* fix SIGFPE caused by signed division overflow
* sparse, llvm: fix link errors
* sparse, llvm: fix phi generation
* sparse, llvm: simplify function generation
* sparse, llvm: improve pointer arithmetic handling
* sparse, llvm: set target specification
* sparse, llvm: use LLVM_DEFAULT_TARGET_TRIPLE
* sparse, llvm: fix array size
* sparse, llvm: cache symbol_type() result
* sparse, llvm: fix struct name generation
* sparse, llvm: set more data attributes
* sparse, llvm: die if error
* Fix result type of relational and logical operators
* Fix expression type for floating point negation ('!')
sparse-0.6.4/Documentation/release-notes/v0.5.1.rst 0000664 0000000 0000000 00000042134 14115310122 0021750 0 ustar 00root root 0000000 0000000 v0.5.1 (2017-08-18)
===================
It is finally there. Sparse 0.5.1 is released.
I consider this the best quality of release of sparse I ever
experienced so far. There are lots of enhancement and bug fixes
incorporate into this release.
I would like to thank every one that contributes to this release,
people who submit patches, perform testing and send bug
reports.
I want to specially thank Luc Van Oostenryck who makes this
release possible. He has 242 commits in this release, far more
than any one else.
The development effort for the next release has already began.
Finally, I would like to call for developers joining the sparse project.
If you are interested in modern compilers, reading compiler books
and want some excise. Sparse is a good project to start.
Sparse is very small, the project compiles under 10 seconds.
It can digest the full kernel source file, generate internal byte
code representation and perform check on it. All this happens in
1/10 of the time it took gcc to compile the same source file.
Here is some project ideas,
https://git.kernel.org/pub/scm/devel/sparse/sparse.git/tree/Documentation/project-ideas.md?h=v0.5.1
Thanks
Chris
Aaro Koskinen (1):
* build: allow use of PKG_CONFIG to override pkg-config
Andy Shevchenko (1):
* lib.c: skip --param parameters
Ard Biesheuvel (2):
* sparse: treat function pointers as pointers to const data
* Ignore pure attribute in assignement
Azat Khuzhin (2):
* sparse, llvm: compile: skip function prototypes to avoid SIGSEGV
* validation/prototype: regression for skipping prototypes
Christian Borntraeger (1):
* s390x: add the proper defines for data types
Christopher Li (24):
* Minor clean up for option handling
* round up the array element size to byte align
* Make same_symbol list share the same scope
* rename -Werror to -Wsparse-error
* teach next_designators() use array_element_offset()
* Ptr list sorting should use memmove instead of memcpy
* Make macro expanded string immutable
* Fix warning compiling sparse-llvm
* Adding ignored attribute optimize
* Let create_symbol check for previous same symbol
* Add full list of gcc attribute
* bump sparse's version to 0.5.1-rc4
* Adding gcc attribute no_gccisr
* Add test case for the wine dead loop bug
* Makefile: clean up and simplify
* Makefile: add selfcheck target
* Adding _Pragma()
* fix warnings report by selfcheck
* Adding gcc attribute noipa etc
* Adding document for sparse patch submit process
* Documents: project ideas
* test-inspect: handle special case iter==NULL
* test-inspect: Detect gtk3 then gtk2 package
* Sparse 0.5.1
Cody P Schafer (3):
* build: allow use of LLVM_CONFIG to override llvm-config config script
* sparse{i,c}: use LLVM_CONFIG to find llc and lli
* parse: support c99 [static ...] in abstract array declarators
Dan Carpenter (1):
* ptrlist: reading deleted items in NEXT_PTR_LIST()
Daniel Wagner (1):
* parse: Add comment to struct statement
Edward Cree (1):
* Allow casting to a restricted type if !restricted_value
Emilio G. Cota (1):
* Define __CHAR_BIT__
Emily Maier (2):
* linearize: Emit C99 declarations correctly
* validation: Check C99 for loop variables
Hans Verkuil (3):
* Add test case for extern array
* Add test case for anonymous union initializer
* Add test case for the ioc type check
Heiko Carstens (1):
* sparse/parse.c: ignore hotpatch attribute
Jeff Layton (2):
* sparse: make bits_to_bytes round up instead of down
* Handle SForced in storage_modifiers
Joe Perches (1):
* sparse: Allow override of sizeof(bool) warning
Johannes Berg (1):
* implement constant-folding in __builtin_bswap*()
John Keeping (3):
* validation/sizeof-bool: fix broken test case
* evaluate: split out implementation of compatible_assignment_types
* Support GCC's transparent unions
Lance Richardson (3):
* sparse: ignore __assume_aligned__ attribute
* sparse: update __builtin_object_size() prototype
* sparse: add support for _Static_assert
Linus Torvalds (5):
* Add warning about duplicate initializers
* Use any previous initializer to size a symbol
* Fix error at anoymous unions
* Fix scoping of extern symbols in block scope
* Fix initializers in anonymous structs and unions
Luc Van Oostenryck (242):
* Teach sparse about the __COUNTER__ predefined macro
* Fix size calculation of unsized bool array
* Do not drop 'nocast' modifier when taking the address.
* fix mixup in "Handle SForced in storage_modifiers"
* Fix type checking of variadic functions
* add missing #include "char.h" to char.c
* make 'ignored_attributes[]' static
* cleanup: remove evaluate_arguments()'s unused argument
* Warn on unknown attributes instead of throwing errors
* Remove unneeded variable in integer_promotion()
* fix discarded label statement
* add test case for builtin bswap with constant args
* make ptrlist walking against robust against empty blocks
* let "compile" not crash on bools
* give comparable label's names to basic blocks
* OP_SWITCH should use 'insn->cond' instead of 'insn->target'
* remove unused field 'multijump' in struct instruction
* storage should not be inherited by pointers
* testsuite: simplify test function-pointer-inheritance
* use a shorter name for function-pointer-modifier-inheritance.c
* testsuite: test modifiers preserved by '&' operator
* testsuite: test modifiers preserved by 'typeof()'
* some modifiers need to be preserved by 'typeof()'
* Update maintainers in the manpage
* cgcc should not define non-reserved identifiers
* recursive phi_defines cannot happen
* fix missing element in types declaration
* add support for __int128
* fix typing error in compound assignment
* llvm: fix typing when comparing to a constant
* llvm: remove unneeded OP_COPY support
* fix cast to bool
* unssa: do not try to update liveness
* unssa: simplify rewrite of OP_PHISOURCE
* unssa: try to avoid some OP_PHI copies
* unssa: eliminate trivial phisrc copies
* unssa: update comment about the unneeded copies
* volatile loads must not be simplified
* fix superfluous phisrc
* fix phisrc mixup
* missing load simplification
* fix value of label statement
* C11: teach sparse about '_Thread_local'
* C11: teach sparse about '_Noreturn'
* C11: teach sparse about '_Alignof()'
* C11: teach sparse about '_Alignas()'
* C11: teach sparse about '--std={c11,gnu11}'
* fix cast's target type info
* fix crash while testing between conditional & unconditional OP_BR
* kill uses of replaced instructions
* fix killing OP_PHI instructions
* fix killing OP_CAST & friends
* fix killing OP_SELECT
* fix killing OP_COMPUTEDGOTO
* explicitely ignore killing OP_ENTRY
* cleanup kill_instruction()
* fix conditional context test case with void
* add helper: is_scalar_type()
* validate expression's type in conditionals
* remove unused arg in uses/defs functions
* add testcase for wrong early escape conversion
* warn on unknown escapes after preprocessing
* remove 'Escape' from token character class
* fix killing OP_SETVAL instructions
* define __LP64__ & _LP64 if arch_m64 is enabled
* add an helper for common predefined macros
* define __LONG_MAX__ & __SIZEOF_POINTER__
* move OP_MUL simplification in a separate function
* simplify '(x / 1)' to 'x'
* simplify '(x * -1)' to '-x'
* simplify '(x / -1)' to '-x' (but only for signed division)
* simplify '(x % 1)' into '0'
* simplify '~(~x)' and '-(-x)' to 'x'
* simplify '(x || 1)' to '1'
* simplify '(x op x)' to '0', '1' or 'x'
* add warning option '-Wtautological-compare'
* simplify comparisons followed by an equality test against 0 or 1
* simplify '(x || x)' and '(x && x)'
* add support for LLP64 arch
* move evaluation & expansion of builtins in a separate file
* let identical symbols share their evaluate/expand methods
* expand __builtin_bswap*() with constant args
* testsuite: give a proper name to the 'binary-constant' test
* testsuite: make tests known to fail effectively fail
* testsuite: simplify the ioc-typecheck case
* testsuite: add a simple test for -Wenum-mismatch
* testsuite: add tag to ignore the output/error
* testsuite: report as error tests known to fail but which succeed
* allow to launch the test suite from the project root dir
* testsuite: check patterns presence or absence in output
* testsuite: add some selfchecking
* testsuite: check the nbr of times a pattern should be present
* testsuite: use 'error' instead of 'info' for successful tests known to fail
* testsuite: get 'check-known-to-fail' earlier
* testsuite: allow quieter error reporting
* testsuite: quieter error reporting for 'known-to-fail'
* cleanup: there is no 'struct phi' to allocate
* remove unused field 'multijmp' in struct statement
* remove unused field 'goto_bb' in struct statement
* fix show-parse()'s labels
* add killing of OP_SLICEs
* add killing of OP_PHISOURCEs
* add helper kill_use_list()
* fix killing of OP_PHIs
* fix clear_phi(), replace it by kill_instruction()
* remove unused clear_phi()
* fix killing of otherwise not-handled instructions
* kill_instruction() may need to be forced or not
* add killing of pure calls
* fix killing OP_CALL via pointers
* add killing of non-volatile loads
* add killing of stores
* fix killing of rewritten loads
* use kill_instruction() when killing an OP_PHI during CSE
* use kill_instruction() when killing any instructions during CSE
* fix OP_PHI usage in try_to_simplify_bb()
* simplify float-to-float casts that doesn't change size
* CSE: add test cases for comparisons duality
* CSE: use commutativity to identify equivalent instructions
* CSE: avoid hashing removed instructions
* fix expansion cost of pure functions
* add missing braces around FOR_EACH_PTR loop
* make -Wbitwise operational again
* use option: '-Woverride-init'
* add test case for warnings about overlapping initializers
* allow to warn on all overlapping initializers
* fix checking of overlapping initializer
* ignore whole-range overlapping initializer
* fix usage in simplify_seteq_setne()
* fix size of loaded bitfields
* split OP_BR between unconditional & conditional: OP_CBR
* remove unused helper is_branch_goto()
* replace test for c99 for-loop initializers
* add test case for scope of C99 for-loop declarations
* add test cases for storage of c99 for-loop declarations
* add an optional validation method to external_declaration()
* check the storage of C99 for-loop initializers
* move 'extern with initializer' validation after the validate method
* use VOID instead of directly using &void_pseudo
* teach sparse about -Waddress
* add is_func_type()
* warn if testing the address of a function
* add is_array_type()
* warn if testing the address of an array
* fix evaluation of a function or array symbol in conditionals
* fix is_scalar_type()
* fix test for cast to bool on 32bit machines
* predefine __INT_MAX__ and friends
* predefine __SIZEOF_INT__ & friends
* fix test validation/div.c
* fix cast to pointer to floating-point
* do not depends on limits.h to test __CHAR_BIT__
* fix expansion of integers to floats
* avoid crash with test-linearize -vv
* fix OP_PHI usage in try_to_simplify_bb(), correctly
* be more careful with concat_user_list()
* avoid useless warning for 'bool <- restricted type' conversion
* introduce REPEAT_CFG_CLEANUP
* let kill_unreachable_bbs() clear REPEAT_CFG_CLEANUP
* fix: kill unreachable BBs after killing a child
* ignore VOID when trying to if-convert phi-nodes
* fix boolean context for OP_AND_BOOL & OP_OR_BOOL
* fix missing reload
* keyword: add test case for reserved '_Static_assert'
* keyword: regroup the [reserved] keywords
* keyword: explicitly add C99 & C11 keywords
* keyword: add more reserved keywords to the test case
* keyword: add a comment about NS_TYPEDEF & reserved keywords
* keyword: no pre-declaration needed for attribute names
* add get__stats()
* add show_allocation_stats()
* add helper handle_simple_switch()
* teach sparse how to handle '-fmem-report'
* use -fmem-report to report allocation stats
* testsuite: cleanup result files
* fix: kill old branch in insert_branch()
* returns the correct type when evaluating NULL
* remove bit_size & bit_offset from struct access_data
* add test case for linearize_initializer() of bitfields
* fix implicit zero initializer.
* remove alignment from struct access_data
* remove origval from struct access_data
* add support for a new flag: -fdump-linearize[=only]
* more tests for implicit 'bool <- restricted' casts
* avoid warning on explicit 'bool <- restricted' casts
* define ident_list
* teach sparse how to dump macro definitions
* fix hardcoded size of wide chars
* avoid to redefine __INT_MAX__ and friends
* fix definition of __SCHAR_MAX__ & friends
* teach sparse how to handle -dD flag
* let -dD report macro definitions
* testsuite: get all tags in once
* testsuite: grep the expected output only when needed
* testsuite: grep the output patterns only when needed
* testsuite: use shell arithmetic instead of fork-execing expr
* testsuite: remove unneeded './' before commands
* testsuite: avoid fork+execing basename
* teach cgcc about OSX aka darwin
* ret-void: add test case for toplevel asm
* ret-void: warn for implicit type
* use NULL instead of 0 in testcases.
* finer control over error vs. warnings
* Add more declarations for more builtin functions
* keep the warnings table alphabetically sorted
* cgcc: alphasort warning names in check_only_option()
* cgcc: add missing warning names to check_only_option()
* cgcc: filter-out '-fdump-linearize[=...]'
* memcpy()'s byte count is unsigned
* add support for -Wmemcpy-max-count
* add support for -fmemcpy-max-count
* fix: add missing examine in evaluate_dereference()
* fix OP_PHI usage in try_to_simplify_bb() only when non-bogus
* fix: try_to_simplify_bb eargerness
* add fallback for missing __builtin_bswapXX()
* fix: __builtin_bswap{16,32,64}() constantness
* dissect: use built_in_ident() instead of MK_IDENT()
* teach sparse about -m{big,little}-endian
* teach sparse about __{BIG,LITTLE}_ENDIAN__
* teach sparse about __BYTE_ORDER__ & __ORDER_{BIG,LITTLE}_ENDIAN__
* cgcc: teach cgcc about arm64
* cgcc: teach cgcc about ppc64[le]
* cgcc: teach cgcc about arm
* bump sparse's version to -rc3
* fix ptrlist corruption while killing unreachable BBs
* fix infinite simplification loops
* fix BB dependencies on phi-nodes
* fix crash when ep->active is NULL
* fix crash in rewrite_branch()
* fix some crashes in add_dominators()
* fix crash with sym->bb_target == NULL
* take comma expr in account for constant value
* fix: give a type to bad cond expr with known condition
* ptrlist: add a counter for the number of removed elemnets
* ptrlist: adjust ptr_list_size for the new ->rm field
* ptrlist: add MARK_CURRENT_DELETED
* ptrlist: avoid iteration on NULL entries
* mark pseudo users as deleted instead of removing them
* testsuite: add support for commands with timeout
* Remove single-store shortcut
* Bump sparse's version to -rc5
* Sparse v0.5.1
Michael Stefaniuc (3):
* Add the __builtin functions needed for INFINITY and nan().
* Add a define for __builtin_ms_va_copy()
* Add tests for the builtin INF and nan() functions.
Oleg Nesterov (3):
* dissect: teach do_expression() to handle EXPR_OFFSETOF
* dissect: teach do_initializer() to handle the nested EXPR_IDENTIFIER's
* dissect: s/mode_t/usage_t/ in report_member()
Omar Sandoval (1):
* sparse-llvm: Fix LLVM 3.5 linker errors
Pavel Roskin (1):
* Use LLVM_CONFIG instead of llvm-config in Makefile
Ramsay Jones (15):
* Add the __restrict__ keyword
* sparse: add 'gnu_inline' to the ignored attributes
* don't call isdigit/tolower with a char argument
* Makefile: suppress error message from shell
* don't run sparse{c,i} tests when sparse-llvm is disabled
* Add support for multiarch system header files
* cgcc: use only the cc command to determine $gcc_base_dir
* cgcc: use $ccom to set $multiarch_dir if not specified
* test-suite: remove bashism to avoid test failures
* cgcc: avoid passing a sparse-only option to cc
* parse.c: remove duplicate 'may_alias' ignored_attributes
* compile-i386.c: don't ignore return value of write(2)
* sparse: add 'alloc_align' to the ignored attributes
* lib: workaround the 'redeclared with different type' errors
* Makefile: pass -Wno-vla to sparse while checking pre-process.c
Randy Dunlap (1):
* documentation: update email reference link
Rui Teng (1):
* sparse: add no_sanitize_address as an ignored attribute
Thomas Graf (1):
* sparse: Make -Werror turn warnigns into errors
Tony Camuso (2):
* .gitignore: add cscope and Qt project files
* Add default case to switches on enum variables
sparse-0.6.4/Documentation/release-notes/v0.5.2.rst 0000664 0000000 0000000 00000007730 14115310122 0021754 0 ustar 00root root 0000000 0000000 v0.5.2 (2018-04-30)
===================
The latest release of sparse have been pushed to the official
repository. It's a smaller release than the previous one but
it contains some important changes to not be flooded by unimportant
warnings while compiling the kernel.
The most notable changes are:
* better tracking and handling of expression constness
* fix bug with variadic macros
* less warnings on unknown attributes (none by default now)
* teach sparse about __builtin_{isinf_sign,isfinite,isnan}
* various update to the documentation
* do selfcheck with the locally built sparse
* some fixes or improvements for build (armhf, GNU/kfreebsd, ...)
* also evaluate files included via -include
Many thanks to everyone involved.
Luc Van Oostenryck
---
Al Viro (1):
* Sparse preprocessing bug with zero-arg variadic macros
Christopher Li (8):
* gcc attr: add nonstring warn_if_not_aligned
* Makefile: provide CFLAGS for command line override.
* Give the constant pseudo value a size
* sparse-llvm: use pseudo->size to select llvm integer type
* Update gcc attribute list
* Fix crash cause by previous pseudo size change
Jacob Keller (1):
* sparse: document that -Wbitwise is default
Logan Gunthorpe (1):
* add __builtin functions for isinf_sign, isfinite and isnan
Luc Van Oostenryck (13):
* constexpr: rename handle_simple_initializer() to handle_initializer()
* constexpr: collect storage modifiers of initializers
* return an error if too few args
* give default return type in evaluate_call()
* constexpr: flag __builtin_bswap() as constexpr
* build: disable sparse-llvm on non-x86
* fix cgcc ELF version for ppc64/pcc64le
* fix: missing evaluate with '-include' : add testcase
* fix: missing evaluate with '-include'
* Revert "Give the constant pseudo value a size"
* By default disable the warning flag '-Wunknown-attribute'
* bump up version to 0.5.2-RC1
* Sparse v0.5.2
Martin Kepplinger (2):
* compile-i386.c: fix a memory leak in sort_array()
* compile-i386: make use of expression_list_size()
Nicolai Stange (20):
* constexpr: introduce additional expression constness tracking flags
* constexpr: init flags at expression allocation
* constexpr: examine constness of casts at evaluation only
* constexpr: examine constness of binops and alike at evaluation only
* constexpr: examine constness of preops at evaluation only
* constexpr: examine constness of conditionals at evaluation only
* constexpr: add support for tagging arithmetic constant expressions
* constexpr: add support for tagging address constants
* constexpr: check static storage duration objects' intializers' constness
* constexpr: recognize static objects as address constants
* constexpr: recognize address constants created through casts
* constexpr: recognize address constants created through pointer arithmetic
* constexpr: recognize members of static compound objects as address constants
* constexpr: recognize string literals as address constants
* constexpr: recognize references to labels as address constants
* constexpr: examine constness of __builtin_offsetof at evaluation only
* constexpr: flag builtins constant_p, safe_p and warning as constexprs
* constexpr: relax some constant expression rules for pointer expressions
* constexpr: support compound literals as address constants
* constexpr: treat comparisons between types as integer constexpr
Ramsay Jones (1):
* Makefile: use locally built sparse in the selfcheck target
Randy Dunlap (5):
* sparse: minor manpage corrections
* Documentation: make data-structures.txt easier to read
* Documentation: editing fixes in test-suite
* test-suite: handle format with filename.c not existing
* sparse: ignore indirect_branch attribute
Uwe Kleine-König (4):
* build: remove version.h in clean target
* cgcc: teach cgcc about GNU/kFreeBSD
* compile-i386: Use SPARSE_VERSION instead of __DATE__
* cgcc: provide __ARM_PCS_VFP for armhf
sparse-0.6.4/Documentation/release-notes/v0.6.0.rst 0000664 0000000 0000000 00000134437 14115310122 0021760 0 ustar 00root root 0000000 0000000 v0.6.0 (2018-12-26)
===================
The source code can be found at its usual repository:
git://git.kernel.org/pub/scm/devel/sparse/sparse.git v0.6.0
The tarballs are found at:
https://www.kernel.org/pub/software/devel/sparse/dist/
Many thanks to people who have contributed to the 888 non-merge
patches of this release:
Ramsay Jones, Randy Dunlap, Uwe Kleine-König, Joey Pabalinas,
John Levon, Ben Dooks, Jann Horn, Logan Gunthorpe, Vincenzo
Frascino and Tycho Andersen.
Special thanks to Ramsay Jones who has reviewed numerous of my
patches, tested many of my series and found many of my typos.
Best wishes for 2019
-- Luc Van Oostenryck
The changes since the pre-release (v0.6.0-rc1) are:
* add -Wbitwise-pointer to warn on casts to/from bitwise pointers
* beautify how types are displayed:
* no more
* no more redundant type specifiers
* remove double space
* some cleanup of the build system:
* only need includedir from llvm-config
* check if sparse-llvm needs libc++
* remove -finline-functions from CFLAGS
* some random cleanup:
* remove unneeded declarations in "compat.h"
* remove unused arg in add_branch()
* allocate BBs after the guards
* remove redundant check of _Bool bitsize
* remove unused regno()
* remove self-assignment of base_type
* some documentation:
* update comment for struct-expression::cast_expression
* fix list formatting in inline doc
* document that identifiers are now OK for address spaces
* add TODO list.
The main changes since the previous release (v0.5.2) are:
* by default, disable warnings about unknown attributes
* no more warnings about sizeof(void) unless -Wpointer-arith is given
* add support for __has_attribute() & __has_builtin()
* many fixes for type evaluation/checking
* the build should be more friendly for distros
* a huge number of testcases have been added to the testsuite
* the handling of cast instructions has been completely revamped
* the SSA conversion has been is now corrected and has been rewritten with a variant of the classical algorithm
* documentation is now handled with Sphinx and inline doc is extracted from the code.
* online documentation can be found at https://sparse-doc.readthedocs.io/en/master/
A more complete list of changes is:
* add predefined macros for __INTMAX_TYPE__, __INT_MAX__, ...
* prepare to identify & display the address spaces by name
* fix linearization of non-constant switch-cases
* manpage: update maintainer info
* manpage: add AUTHORS section
* fixes for -dD
* add support for -dM
* remove more complex phi-nodes
* fix linearization/SSA when missing a return
* fix linearization/SSA of (nested) logical expressions
* fix linearization of unreachable switch + label
* add support for __has_attribute()
* consolidate instruction's properties into an opcode table
* fix: do not optimize away accesses to volatile bitfields
* support mode(__pointer__) and mode(__byte__)
* do 'classical' SSA conversion (via the iterated dominance frontier).
* fix buggy recursion in kill_dead_stores()
* kill dead stores again after memops simplification is done.
* simplify TRUNC((x & M') | y, N)
* simplify AND(SHIFT(a | b, S), M)
* simplify TRUNC(SHIFT(a | b, S), N)
* add simplification of TRUNC(TRUNC((x))
* add simplification of SHL(LSR(x), S), S)
* generate integer-wide OP_ADD & OP_SUB in linearize_inc_dec()
* simplify mask instructions & bitfield accesses
* fix a few bugs related to the linearization of logical expressions
* simplify those logical expressions.
* add optimized version of some list operations
* some simplifications of OP_SETNE & OP_SETEQ with 0 and 1
* several simplifications involving casts and/or bitfields
* give a correct & sensible warning on negative or over-sized shifts.
* add conservative simplification of such shifts.
* do not optimize the meaningless shift:
* any shift with a negative count
* OP_ASRs with an over-sized shift count.
* try to give a correct negative/too-big error message during simplification.
* simplify chains of shifts.
* simplify ZEXT + ASR into ZEXT + LSR
* cse: try the next pair instead of keeping the first instruction
* cse: compare casts only by kind a size, not by C type.
* optimize away OP_UTPTR & OP_PTRTU which are nops.
* cleanup of list walking macros:
* make untagged pointers the normal case
* use structurally equivalent struct for all pointer lists to avoid needless casting to and fro struct ptrlist
* simplify PREPARE/NEXT/RESET logic by using common PTR_NEXT()
* add validation of the IR.
* improve expansion of builtin dynamic macros (__FILE__, ...)
* add support for __INCLUDE_LEVEL__ & __BASE_FILE__
* improve generation of predefined macros
* add support for builtins doing overflow checking.
* add support for the __has_builtin() macro.
* improve Sphinx doc for IR instructions
* have those instructions in the index
* have a nicer presentation of the generated doc thanks to not having to use level-4 headings anymore
* fixes & improvements to the testsuite; mainly:
* allow to run the testsuite on all the tests of a subdir
* teach 'format' to directly append to the testcase
* validate the 'check-...' tags
Shortlog
--------
Ben Dooks (1):
* tokenize: check show_string() for NULL pointer
Jann Horn (1):
* fix accesses through incorrect union members
Joey Pabalinas (2):
* doc: copy-edit text related to applying sizeof to a _Bool
* sparse: add -Wpointer-arith flag to toggle sizeof(void) warnings
John Levon (2):
* Ignore #ident directives
* Conditionalize 'warning: non-ANSI function ...'
Logan Gunthorpe (1):
* add __builtin functions for isinf_sign, isfinite and isnan
Luc Van Oostenryck (857):
* use long for all mem stats
* diet: use smaller LIST_NODE_NR (29 -> 13)
* diet: remove unused struct scope::token
* diet: remove unused struct symbol::arg_count
* option: add helper to parse/match command line options
* option: rename 'struct warning' to 'struct flag'
* option: let handle_simple_switch() handle an array of flags
* option: extract OPTION_NUMERIC() from handle_switch_fmemcpy_max_count()
* option: add support for options with 'zero is infinity'
* option: add support for '-=unlimited'
* option: use OPTION_NUMERIC() for handle_switch_fmemcpy_max_count()
* option: constify match_option()
* option: handle switches by table
* dump-ir: add defines for the compilation passes
* testsuite: 'echo -n' may not be interpreted as '-n'
* testsuite: allow to test a few cases at once
* testsuite: move verbose() & error()
* testsuite: better message for pattern nbr checking
* testsuite: better message for pattern absence/presence
* use shorter name for constexpr tests
* testsuite: new eq/min/max syntax for pattern checking
* testsuite: obsolete old pattern checking syntax
* testsuite: convert to the new pattern syntax
* use a specific struct for asm operands
* fix: missing evaluate with '-include' : add testcase
* fix: missing evaluate with '-include'
* fix test case kill-phi-ttsb
* add test case for incomplete type
* add test case for bad return type
* diet: remove unused struct symbol::value
* cclass: char is wide enough
* cclass: cleanup
* remove prototype extern int is_ptr_type()
* remove prototype for nonexistent examine_simple_symbol_type()
* graph: do not scan removed instructions
* build: fix effectiveness of generated dependencies
* build: remove unused support for pkgconfig
* cgcc: teach cgcc about freebsd & netbsd
* testsuite: clearer result summary
* testsuite: check error messages first
* testsuite: saner handling of 'must_fail'
* testsuite: allow to parse several options
* testsuite: add support for -q|--quiet
* testsuite: add support for -a|--abort
* testsuite: get options from env too
* testsuite: allow --format & --single
* testsuite: remove useless selftest
* testsuite: remove useless test-be.c
* testsuite: extract disable()
* testsuite: simplify documentation
* testsuite: allow arch-specific tests
* testsuite: save screen real estate
* testsuite: add a blank line before format
* testsuite: 'quiet' must be initialized earlier
* testsuite: move up arg_file()
* testsuite: make do_format() more self-contained
* testsuite: format: saner defaults handling
* testsuite: format: strip .c from default name
* testsuite: add support for 'format -f'
* testsuite: add support for 'format -l'
* remove never-used MOD_TYPEDEF
* MOD_ACCESSED is not a type modifier ...
* reorganize the definition of the modifiers
* remove redundancy in MOD_STORAGE
* define MOD_QUALIFIER for (MOD_CONST | MOD_VOLATILE)
* associate MOD_RESTRICT with restrict-qualified variables
* add support for C11's _Atomic as type qualifier
* build: use '-objs' instead of '_EXTRA_DEPS'
* build: use '-ldlibs' instead of '_EXTRA_OBJS'
* build: allow target-specific CFLAGS, CPPFLAGS, LDFLAGS & LDLIBS
* build: allow CFLAGS & friends from command line
* build: avoid rule-specific CFLAGS
* build: use $LIBS directly in the dependency list
* build: no need to use wildcards for generated dependencies
* build: reuse rule for ALL_OBJS
* build: CHECKER_FLAGS=-Wno-vla for all targets
* build: move tests near their use
* build: add note about overwritable vars
* build: remove references to nonexistent pre-process.h
* build: move clean & clean-check together
* build: make clean targets quieter
* build: remove rule for shared lib, it's unused
* build: normalize rules
* build: remove the dist rule since unused
* build: use one line per item
* build: allow the name 'local.mk' to be configurable via the environment
* build: use standard rules for install
* build: remove unused QUIET_INST_SH
* build: let quiet commands use less indentation
* build: simplify quiet commands
* build: simplify clean pattern
* build: add \*.o to clean-check pattern
* build: avoid foreach
* build: reorg & add comment
* build: use a single space before assignments
* build: add rule to run a single test
* build: let -fno-strict-aliasing be a mandatory flag
* volatile loads are side-effects too
* define MOD_ACCESS for (MOD_ASSIGNED | MOD_ADDRESSABLE)
* fix 'simplification' of float-to-int casts
* fix description setval & symaddr
* flush stdout when warning
* add test case for bogus volatile simplification
* fix: volatile stores must not be simplified
* dump-ir: add testcase for option parsing corner case
* dump-ir: allow to specify the passes to execute via cli's options
* dump-ir: activate/deactivate pass 'mem2reg'
* dump-ir: allow to skip the optimization pass(es)
* dump-ir: saner use of fdump_linearize
* dump-ir: rename -fdump-linearize to -fdump-ir
* dump-ir: make it more flexible
* dump-ir: activate -fdump-ir=mem2reg
* add test case for using multiple input files
* add test case for VLA sizeof
* add test case for memory to register problem
* add test case for conditionally undefined var
* add test case for incomplete type
* add test case bitfields in K&R decl
* add test case storage specifier in struct member
* add test case using sizeof on incomplete type
* add test case for bad layout of bool in bitfields
* add test case for missed overflow detection
* add test cases for canonicalization of add/sub chains
* add test case for compound literals
* add testcase for __builtin_unreachable()
* add test cases for canonicalization of mul chains
* add test case for pre-processor extra tokens warning
* add testcase for return & inline
* add test cases for simplification of equivalent to 'x == 0' or 'x != 0'
* add test case for superfluous cast with volatiles
* add testcase for mem2reg/SSA conversion
* add test cases for canonicalization of boolean expressions
* add test case for missing conversion to select
* show OP_PHI without VOID
* don't output value of anonymous symbol's pointer
* add table to "negate" some opcode
* use opcode table for compare_opcode()
* canonicalize binops before simplification
* canonicalize compare instructions
* add is_signed_type()
* fix usage of inlined calls
* inlined calls should not block BB packing
* give a type to all function arguments
* give a type to OP_PHISOURCEs
* give a type to OP_SELs, always
* give a type to OP_SWITCHs
* add doc about sparse's instructions/IR
* add support for wider type in switch-case
* llvm: remove unneeded arg 'module'
* llvm: remove unneeded 'generation'
* llvm: remove unneeded function::type
* llvm: reduce scope of 'bb_nr'
* llvm: use pseudo_list_size() instead of open coding it
* llvm: give arguments a name
* llvm: give a name to call's return values
* llvm: avoid useless temp variable
* llvm: extract get_sym_value() from pseudo_to_value()
* llvm: fix test of floating-point type
* llvm: fix translation of PSEUDO_VALs into a ValueRefs
* llvm: fix output_op_store() which modify its operand
* llvm: fix output_op_[ptr]cast()
* llvm: add test cases for symbol's address
* llvm: add test cases for pointers passed as argument
* llvm: add test cases for arrays passed as argument
* llvm: add test cases for degenerated pointers
* llvm: add support for OP_NEG
* llvm: add support for OP_SETVAL with floats
* llvm: add support for OP_SETVAL with labels
* llvm: ignore OP_INLINED_CALL
* llvm: fix pointer/float mixup in comparisons
* llvm: fix type in comparison with an address constant
* llvm: give correct type to binops
* llvm: adjust OP_RET's type
* llvm: variadic functions are not being marked as such
* llvm: fix type of switch constants
* llvm: make pseudo_name() more flexible
* llvm: give a name to all values
* llvm: add support for OP_SWITCH with a range
* llvm: fix OP_SWITCH has no target
* llvm: make value_to_pvalue() more flexible
* llvm: make value_to_ivalue() more flexible
* llvm: add test case pointer compare with cast
* llvm: let pseudo_to_value() directly use the type
* llvm: add small script to test LLVM generated bytecode
* llvm: add testcase for calling variadic functions
* llvm: fix variadic calls with constants
* llvm: take care of degenerated rvalues
* llvm: fix mutating function pointer
* llvm: fix mutated OP_RET
* llvm: fix mutated OP_SEL
* llvm: fix mutated OP_SWITCH
* llvm: fix mutated OP_PHISOURCE
* llvm: fix mutated OP_[PTR]CAST
* llvm: add support for restricted types
* llvm: fix get value from initialized symbol
* llvm: fix get value from non-anonymous symbol
* llvm: fix type of bitfields
* llvm: add support for OP_FPCAST
* llvm: add support for cast from floats
* llvm: cleanup of output_[ptr]cast()
* llvm: fix creation of sparsec's tmp files
* llvm: give names easier to debug
* llvm: gracefully catch impossible type/value
* llvm: warn instead of assert on global inits
* llvm: add support for float initializer
* llvm: only compare void pointers
* fix linearize_inc_dec() with floats
* add test case for boolean negation on float
* fix support of floating-point compare
* add support of floating-point specific arithmetic ops
* testsuite: fix: remove unneeded './' before commands
* fix: build sparse-llvm on i686s too.
* add more testcases for using addresses in conditionals
* add testcases linearization of degenerated arrays/functions
* fix: add missing degenerate() for logical not
* testsuite: make the '%.t' rule depends on PROGRAMS too
* testsuite: fix a few more incorrect check-commands
* testsuite: convert to the new pattern syntax
* testsuite: remove old ugly pattern syntax
* testsuite: move verbose/error() before get_tag_value()
* testsuite: add & use warning()
* testsuite: reset 'quiet' at the start of each testcase
* testsuite: fix invalid 'check-...' tags
* testsuite: validate the 'check-...' tags
* testsuite: early return in getopt loop
* testsuite: move do_test_suite out of the getopt loop
* testsuite: move no-arg out of the getopt loop
* testsuite: change do_usage text
* testsuite: allow to test only a subdir
* testsuite: default to shift in the getopt loop
* testsuite: add support for 'format -a'
* add testcase for 'sparse -D M...'
* fix: accept 'sparse -D M...'
* testsuite: add test case for quoting of command's arguments
* testsuite: process extra options without exec
* testsuite: respect command line's quotes & whitespaces
* testsuite: allow default args from environment for test commands
* add test case for space within command line
* fix: spaces in macro definition on the command line
* add testcases for unexamined base type
* fix: evaluate_dereference() unexamined base type
* add testcases for the linearization of calls
* simplify linearize_call_expression()
* fix linearize (\*fun)()
* add testcases for multiple deref of calls
* avoid unneeded alloc on error path
* dereference of a function is a no-op
* add testcase for constant bitfield dereference
* fix expansion of constant bitfield dereference
* add testcase for CSE of floating-point compares
* fix: restore CSE on floating-point compares
* llvm: fix: previous function ref MUST be reused
* llvm: use LLVMModuleRef for get_sym_value()
* llvm: add declares for function prototypes
* testcases: add missing return statements
* warn on empty parenthesized expressions
* fix crash on bad expression in linearize_switch()
* llvm: simplify emit of null pointers
* llvm: default init of arrays & structs
* add more testcases for function designator dereference
* add testcases for type comparison
* fix implicit size of unsized arrays
* let handle_switches() also handle reverse logic
* add support for '-f[no-][un]signed-char'
* fix: dereference null-type
* teach sparse about '-fmax-warnings'
* give a type to builtin functions
* ctags: avoid null deref
* cleanup: make some functions static
* cleanup: remove unused & obsolete symbol_is_typename()
* cleanup: remove unused delete_last_basic_block()
* cleanup: remove declaration of unused merge_phi_sources
* add OP_SETFVAL
* CSE: support CSE of floating-point literal
* doc: fix manpage formatting
* report type & size on non-power-of-2 pointer subtraction
* remove warning "call with no type"
* add testcases for duplicated warning about invalid types
* fix error in bad conditional
* early return if null ctype in evaluate_conditional()
* add helper: valid_type()
* use valid_type to avoid to warn twice on conditionals
* add helpers: valid_expr_type() & valid_subexpr_type()
* do not report bad types twice
* always evaluate both operands
* fix examination of bad typeof
* extract extract eval_insn() from simplify_constant_binop()
* add testcase of dead dominator
* fix dead dominator
* fix missing checks for deleted instructions
* add testcase for bad killing of dominated stores
* add helper for pseudo's user-list's size
* add helper: has_users()
* use has_users() in dead_insn() too
* let kill_instruction() report if changes were made
* add testcases for converted loads
* fix killing of converted loads
* fix usage of deadborn loads
* kill dead loads
* kill dead stores when simplifying symbols
* By default disable the warning flag '-Wunknown-attribute'
* no repetition in unknown attribute warning message
* cleanup: remove unused 'struct pseudo_ptr_list'
* llvm: use list_size() to count the numbers of arguments
* llvm: initialize at declaration time
* show_pseudo(): protect against NULL ->sym
* use show_pseudo() for OP_SYMADDR's symbol
* let struct access_data use a single type
* rename base_type() to bitfield_base_type()
* builtin: add ctype for const {void,char} *
* builtin: make builtins more builtin
* builtin: extract eval_args() from arguments_choose()
* builtin: add typechecking of isnan(), isinf(), ...
* builtin: add testcases for expansion of special FP constants
* builtin: add testcases for expansion of FP classification
* unsigned multiplication is also associative
* no need for signed & unsigned multiplication
* use '%lld' for printing long longs
* build: add -MP for generated dependencies
* build: use -MMD for generated dependencies
* ban use of 'true' or 'false'
* 'amd64' is also ok for sparse-llvm
* testsuite: fix typo with 'test-suite format -a'
* rename variable 'optimize' to 'optimize_level'
* move the optimization loop in its own file
* cse: untangle simplification & hashing
* extract cse_eliminate() from cleanup_and_cse()
* expose interface to CSE
* move liveness interface to its own header
* move inner optimization loop into optimize.c
* move the inner optimization loop into the main loop
* remove unneeded cast in calls to free_ptr_list()
* testsuite: add testcase for some random crash
* testsuite: add testcase about CSE problem
* IR: fix typo in IR doc
* IR: remove now unused OP_LNOP & OP_SNOP
* IR: remove never-generated instructions
* IR: let .cond unionize with .src and not .target
* IR: let OP_COMPUTEGOTO use .src instead of .target
* llvm: normalize sparse-llvm-dis' output
* llvm: fix typo for constant addresses
* testsuite: fix problem with double-escaping in patterns
* add a field 'tainted' to struct instruction
* taint: let check_access() warn just once
* fix address_taken()
* fix symbol cleanup
* cleanup deadborn phi-sources
* optim: add some more optimization tests
* optim: add testcase for internal infinite loop
* optim: add timeout for infinite optim loop tests
* optim: kill unreachable BBS after CFG simplification
* optim: no need to kill_unreachable_bbs() after main loop
* optim: fix optimization loop's condition
* optim: pack bb must set REPEAT_CFG
* optim: load simplification should repeat optimization
* optim: fix REPEAT_CFG_CLEANUP
* add an helper to test the value of a pseudo against zero
* optim: simplify null select
* make remove_usage() more generic
* add remove_use()
* show_label: add (and use) show_label()
* extract alloc_phisrc() from alloc_phi()
* small code reorg of add_store()
* alloc: add missing #include "compat.h"
* defer initialization of bb::context
* fix-return: remove special case for single return
* avoid deadborn loads & stores
* doc: options.md is for development
* doc: document the debug flags
* fix missing handling of OP_FNEG
* graph: do not use insn->symbol for memops
* use -Wpointer-arith for tests
* default to LP64 for all and only for 64 bit ABIs
* fix alignment of 64 bit integers on LLP64
* add testcases for verifying ABI's integer size & align
* use an enum for ARCH_LP32 & friends
* add a flag -mx32 ILP32 env on 64 bit archs
* add testcase for enum / int type difference
* add testcase for array size type difference
* add testcase for typedef redefinition
* export check_duplicates()
* fix: warn on typedef redefinition
* do not to ignore old preprocessor testcases
* use also __x86_64 when __x86_64__ is used
* build: use a variable for $(shell uname -m)
* build: use 'filter' to do pattern matching inside the Makefile
* build: disable LLVM on x86-64-x32
* let cgcc use sparse's predefines for i386 & x86-64
* build: use --dirty with 'git describe'
* fix build on Hurd which doesn't define PATH_MAX
* testsuite: add check-cp-if
* testsuite: add check-assert
* teach sparse about _Floatn and _Floatnx
* add test case bug expand union
* alloc: check if size is too big
* fix: don't dump pointer value in error message
* fix missing checks for deleted instructions
* fix comment about PSEUDO_SYM usage
* fix: remove usage when killing symaddr (part 1)
* fix: remove usage when killing symaddr (part 2)
* OP_SYMADDR is simply an unop
* use function-like syntax for __range__
* increment the version number suffix it with -dev
* doc: fix markdown syntax
* doc: fix headings
* doc: add minimal support for sphinx-doc
* doc: add logo
* doc: automatically set the copyright date
* doc: automatically get the version
* doc: set primary domain to C
* doc: allow .md with py3-sphinx
* doc: move sparse.txt to markdown and rename it
* doc: the testsuite doc in reST
* doc: format dev-options.md as a man page
* doc: use reST for manpages
* api: move evaluate interface to its own header file
* doc: add structured doc to ptrlist.c
* autodoc: extract doc from the C files
* autodoc: convert extracted doc to reST
* autodoc: add a sphinx c:autodoc directive for the extracted doc
* autodoc: add doc from ptrlist.c
* autodoc: add markup to argument's references
* autodoc: doc the doc
* autodoc: by default disable syntax highlighting
* autodoc: add a small cheatsheet for reST markup
* autodoc: support muti-line param & return descriptions
* autodoc: document a few more APIs to test multiline
* autodoc: add autodoc tests in the testsuite
* doc: convert IR.md to reST
* doc: add sphinx domain for IR instruction indexation
* add helper for new parsing errors: unexpected()
* context: fix parsing of attribute 'context'
* context: __context__(...) expect a constant expression
* context: fix crashes while parsing '__context__;' or '__context__(;'
* context: stricter syntax for __context__ statement
* context: extra warning for __context__() & friends
* label: add testcase for label redefinition
* label: avoid multiple definitions
* vla-sizeof: add test cases
* vla-sizeof: add support for sizeof of VLAs
* fix typing of __builtin_expect()
* fix crash on 'goto '
* give a position to end-of-input
* avoid multiple error message after parsing error
* add test for integer-const-expr-ness
* dyn-macro: add testcase for __LINE__ & friends
* dyn-macro: use a table to expand __DATE__, __FILE__, ...
* dyn-macro: add support for __INCLUDE_LEVEL__
* dyn-macro: add real support for __BASE_FILE__
* utils: add xmemdup() & xstrdup()
* utils: convert alloc + copy to {mem,str}dup_alloc()
* builtin: add testcase for builtin macro expansion
* builtin: extract do_define() from do_handle_define()
* builtin: add builtin types {u,}{int,long,long}_ptr_ctype
* builtin: declare __builtin_[us]{add,sub,mul}{,l,ll}_overflow()
* builtin: rename arguments_choose() to args_triadic()
* builtin: add support for __builtin_{add,sub,mul}_overflow(), ...
* extract replace_with_bool() from replace_with_defined()
* builtin: add support for __has_builtin()
* builtin: add predefine()
* builtin: directly predefine builtin macros
* builtin: consolidate predefined_macros()
* doc: API before IR
* builtin: switch calling order of predefined_macros() & friends
* builtin: merge declare_builtin_function() with declare_builtins()
* teach sparse about -m16
* ptrlist: specialize __add_ptr_list() for tag/notag
* ptrlist: remove now unneeded add_ptr_list_notag()
* ptrlist: add helper PTR_UNTAG()
* ptrlist: rename PTR_ENTRY() to PTR_ENTRY_UNTAG()
* ptrlist: make explicit when tagged pointers are used.
* ptrlist: let FOR_EACH_PTR() ignore tags
* utils: add xasprintf() & xvasprintf()
* add support for -fdiagnostic-prefix[=prefix]
* doc: add doc for the -vcompound flag
* testsuite: fix missing return
* keep the debug flags alphabetically sorted
* testsuite: allow extra/default options to test commands
* ir-validate: add framework for IR validation
* ir-validate: validate pseudo's defining instruction
* ir-validate: add validation of (nbr of) phi operands
* ir-validate: add more validation points
* sparsec: simplify & portable use of mktemp
* add predefines for __INT_WIDTH__ & friends
* ptrlist: remove the now unneeded FOR_EACH_PTR_NOTAG()
* ptrlist: let {first,last}_ptr_list() return the raw pointer
* ptrlist: let sort_list() use the raw pointer
* ptrlist: let all pointer lists have the same parameterized structure
* ptrlist: when possible use the real type of the list
* ptrlist: remove now unneeded CHECK_TYPE()
* ptrlist: make add_ptr_list() more readable
* ptrlist: make free_ptr_list() more readable
* ptrlist: remove some unneeded arg from internal macros.
* ptrlist: remove extra ident level
* ptrlist: simplify loop nesting
* ptrlist: simplify DO_NEXT
* ptrlist: simplify PREPARE/NEXT
* ptrlist: shorter continuated lines
* ptrlist: remove ptr_list_empty()
* ptrlist: make {first,last}_ptr_list() out-of-line functions
* ptrlist: move semi-private prototypes close to their user
* ptrlist: use VRFY_PTR_LIST() for sanity check
* ptrlist: keep declaration of head-list-nr together
* ptrlist: make clear what is API and what is implementation.
* ptrlist: move DO_SPLIT() into DO_INSERT_CURRENT()
* ptrlist: add missing doc for some functions
* add testcase for bad fpcast simplification
* fix bad fpcast simplification
* avoid useless deref in simplify_cond_branch()
* new helper: replace_pseudo()
* remove unused arg in simplify_cond_branch()
* add_uniop() should take a type, not an expression
* rename add_uniop() to add_unop()
* add missing entry for OP_FNEG in kill_insn() & validate_insn()
* ir: define an \OP_.. range for unops
* ir: case OP_UNOP ... OP_UNOP_END
* cast: reorg testcases related to casts
* cast: add testcase for bad implicit casts to struct/union
* cast: add testcase for cast to bad typeof
* cast: add tests for warnings issued by sparse -v
* cast: rename evaluate_cast()'s vars with slightly more meaningful names
* cast: force_cast are OK on non-scalar values
* cast: prepare finer grained cast instructions
* cast: specialize FPCAST into [USF]CVTF
* cast: handle NO-OP casts
* cast: specialize floats to integer conversion
* cast: specialize casts from unsigned to pointers
* cast: make [u]intptr_ctype alias of [s]size_t_ctype
* cast: make pointer casts always size preserving
* cast: temporary simplify handling cast to/from void*
* cast: specialize cast from pointers
* cast: add support for -Wpointer-to-int-cast
* cast: make casts from pointer always size preserving
* cast: specialize integer casts
* cast: accept null casts
* cast: do not try to linearize illegal casts
* cse: add testcase for missed opportunity
* new helper: def_opcode()
* cast: simplify simplify_cast()
* cast: merge simplification of constant casts with constant unops
* cast: prepare for more cast simplifications
* cast: keep instruction sizes consistent
* cse: move to next comparable instruction
* cast: simplify TRUNC + ZEXT to AND
* add simple testcases for internal infinite loops
* simplify 'x | ~0' to '~0'
* simplify 'x & ~0' to 'x'
* simplify 'x ^ ~0' to '~x'
* bool: add testcase for bool simplification
* bool: fix add missing check in simplify_seteq_setne()
* bool: simplify ZEXT in bool -> int -> bool
* bool: fix missing boolean context for floats
* bool: generate plain OP_{AND,OR} instead of OP_{AND,OR}_BOOL
* bool: remove OP_{AND,OR}_BOOL instructions
* cast: reorganize testcases for cast optimization
* cast: optimize away casts to/from pointers
* cse: let equivalent casts hash & compare identically
* fix killing OP_SWITCH
* fix: remove dead OP_{SETVAL,SETFVAL,SLICE}
* kds: add testcases for kill_dead_stores()
* kds: add explanation to kill_dead_stores()
* kds: rename kill_dead_stores() to kill_dead_stores_bb()
* kds: add interface for kill_dead_stores()
* kds: kill dead stores after memops simplification
* kds: shortcut for kill_dead_stores()
* kds: fix recursion in kill_dead_stores_bb()
* kds: clarify kill_dead_stores_bb()
* testsuite: reorganize tests for compound literals
* testsuite: add a few more tests catching quadratic behaviour
* testsuite: improve mem2reg testcases
* testsuite: remove useless test for loop-linearization
* graph: build the CFG reverse postorder traversal
* graph: add debugging for (reverse) postorder traversal
* dom: calculate the dominance tree
* dom: add some debugging for the dominance tree
* dom: add support for dominance queries
* dom: build the domtree before optimization
* dom: use domtree for bb_dominates()
* sset: add implementation of sparse sets
* idf: compute the iterated dominance frontier
* idf: add test/debug/example
* add new helper: is_integral_type()
* add PSEUDO_UNDEF & undef_pseudo()
* add insert_phi_node()
* ptrmap: core implementation
* ptrmap: add type-safe interface
* ssa: phase 1: phi-nodes placement
* ssa: phase 2: rename load & stores
* ssa: phase 3: rename phi-nodes
* ssa: activate the new SSA conversion
* ssa: remove unused simplify_symbol_usage()
* ssa: phi worklist
* remove unused finish_address_gen()
* remove unused struct access_data::pos
* no need to assign ad->type for EXPR_POS
* remove obsolete comment: /\* Dummy pseudo allocator \*/
* big-shift: add test for shifts with bad count
* big-shift: mark out-of-range OP_{ASR,LSR,SHL} as tainted
* big-shift: do not evaluate negative or over-sized shifts
* big-shift: don't take the modulo at expand time
* big-shift: move the check into check_shift_count()
* big-shift: use the base type for shift-too-big warning
* big-shift: also check shift count of shift-assignment
* big-shift: do not simplify over-sized OP_ASR to zero
* big-shift: reorder the tests in simplify_asr()
* big-shift: reuse simplify_asr() for LSR & SHL
* big-shift: simplify over-sized OP_LSRs
* big-shift: simplify over-sized OP_SHLs
* big-shift: use the type width for too big shift
* big-shift: fix warning message for negative shift count
* big-shift: fix evaluation of shift-assign
* big-shift: do not truncate the count when checking it
* big-shift: add -Wshift-count-{negative,overflow}
* extract nbr_users() from unssa.c
* add testcases for casts & bitfield insertion/extraction
* bitfield: extract linearize_bitfield_extract()
* bitfield: extract linearize_bitfield_insert()
* cast: simplify [SZ]EXT + TRUNC to original size
* cast: simplify [SZ]EXT + TRUNC to a smaller/greater size
* cast: fix shift signedness in cast simplification
* cast: do not compare sizes, test the opcode
* cast: use a switch to handle TRUNC(AND(x,M),N) in simplify_cast()
* cast: preserve the sizes of TRUNC(AND(x,M),N)
* cast: simplify [ZS]EXT(AND(x,M),N)
* cast: simplify AND(ZEXT(x,M),N)
* cast: simplify SEXT(SEXT(x,N),N')
* cast: simplify ZEXT(ZEXT(x,N),N')
* cast: simplify SEXT(ZEXT(x,N),N')
* bits: add helpers for zero & sign-extension
* big-shift: add testcases for simplification of over-sized shifts
* big-shift: add testcases for simplification of negative shifts
* big-shift: move shift count check in a separate function
* big-shift: fix warning message for negative or over-sized shifts
* big-shift: do not optimize negative shifts
* big-shift: do not optimize over-sized ASRs
* use "%Le" to display floats
* add copy_ptr_list()
* testcases: add testcase for missing detection of out-of-bound stores
* testcases: missing evaluation of side effects in typeof(VLA)
* kill dead OP_FADD & friends
* add ptr_list_empty()
* add ptr_list_multiple()
* add lookup_ptr_list_entry()
* shift: simplify LSR(LSR(x,N),N') & friends
* shift: simplify ASR(LSR(x,N),N')
* shift: avoid simplification of ASR(LSR(x,0),N)
* shift: simplify ASR(ZEXT(X, N), C)
* testcase for SET{EQ,NE}([SZ]EXT(x, N),{0,1})'s simplification
* cleanup of simplify_seteq_setne(): remove tmp vars
* simplify SET{EQ,NE}(ZEXT(x, N),{0,1})
* simplify SET{EQ,NE}(SEXT(x, N),{0,1})
* simplify 'x != 0' or 'x == 1' to 'x'
* add testcase for linearize_logical()
* fix size corruption when simplifying 'x != 0' to 'x'
* protect add_convert_to_bool() against bad types / invalid expressions
* conditional branches can't accept arbitrary expressions
* fix linearize_conditional() for logical ops
* expand linearize_conditional() into linearize_logical()
* simplify linearize_logical()
* simplify SETNE(AND(X,1),0) to AND(X,1)
* simplify SETNE(TRUNC(x,N),{0,1})
* simplify ZEXT(SETCC(x,y), N)
* simplify SEXT(SETCC(x,y), N)
* simplify TRUNC(SETCC(x,y), N)
* simplify AND(SETCC(x,y), M)
* boolean conversion of boolean value is a no-op
* cast: fix warning position in cast_pseudo()
* limit the mask used for bitfield insertion
* expand linearize_position() into linearize_initializer()
* put back the bitfield base type into struct access_data
* fix instruction size & type in linearize_inc_dec()
* fix bad indentation in linearize_inc_dec()
* avoid infinite simplification loops of the second kind
* optim: add a few more testcases for shift & mask
* use multi_users() instead on nbr_users()
* reorg code for shift-shift simplification
* simplify ((x & M') | y ) & M into (y & M) when (M' & M) == 0
* simplify ((x & M) | y) >> S to (y >> S) when (M >> S) == 0
* simplify (x << S) >> S into x & (-1 >> S)
* simplify (x & M) >> S to (x >> S) & (M >> S)
* rename testcase for ((x << S) >> S) simplification
* add testcase for ((x >> S) << S) simplification
* add testcase for TRUNC(TRUNC(x)) simplification
* simpler guard in LSR-SHL simplification
* reorganize shift-shift simplification
* simplify ((x >> S) << S)
* reorganize simplification of ZEXT(TRUNC(x))
* simplify TRUNC(TRUNC(x))
* doc: simplify the creation of the viewlist
* doc: automatically insert the blank line for lists
* doc: convert existing simplify.c doc into ReST autodoc
* doc: reword doc for replace_pseudo()
* doc: add doc for simplification notation
* add testcase for (((x & M') | (y & M'')) & M)
* add testcases for bitfield & AND/OR simplification
* unify simplify_lsr_or() & simplify_and_or_mask()
* add simplify_mask_or()
* use better names for simplify_mask_or_and() vars
* document simplify_mask_or() & simplify_mask_or_and()
* switch return order in simplify_mask_or_and()
* allow simplification of OP(((x & y) | (a & M')), K)
* move opcode test inside simplify_mask_or_and()
* simplify OP(((x & M') | y), K) when (M' & M) == M
* simplify OP(((x & M') | y), K) when (M' & M) != M'
* simplify OP((x | C), K) when (C & M) == 0
* simplify OP((x | C), K) when (C & M) == M
* simplify OP((x | C), K) when (C & M) != C
* simplify SHL((x & M') | y, S)
* add testcases for {LSR,SHL}(AND(x, M), S) with shared AND(x, M)
* use an intermediate mask in simplify_shift()
* simplify ((x & M) >> S) when (M >> S) == 0
* simplify ((x & M) >> S) when (M >> S) == (-1 >> S)
* simplify ((x & M) << S) when (M << S) == 0
* simplify ((x & M) << S) when (M << S) == (-1 << S)
* simplify TRUNC((x & M') | y, N)
* doc: extend simplification notation
* prepare simplification of MASK(SHIFT(a | b, S), M)
* simplify AND(SHIFT(a | b, S), M)
* simplify TRUNC(SHIFT(a | b, S), N)
* mode keywords don't need MOD_{CHAR,LONG,...}
* add support for mode __pointer__
* add support for mode __byte__
* add a testcase for enum using a mode
* remove superfluous newline in 'unknown mode attribute' error message
* testsuite: remove useless test for loop-linearization
* symaddr: s/insn->symbol/insn->src/
* add testcase for accesses to volatile bitfields
* split memops from unops
* add a flag for volatile memops
* fix: do not optimize away accesses to volatile bitfields
* opcode: centralize opcode definition
* opcode: add arity info
* opcode: add OPF_TARGET
* add a function to remove deadborn instructions
* fix missing declarations
* has-attr: add testcase for __has_attribute()
* has-attr: move 'mode' next to '__mode__'
* has-attr: add __designated_init__ & transparent_union
* has-attr: add support for __has_attribute()
* ir-validate: add validation branch to dead BB
* ir-validate: ignore dead phis
* ir-validate: validate return value
* add testcase for unreachable label in switch
* fix linearization of unreachable switch (with reachable label).
* move DEF_OPCODE() to header file
* trivial-phi: add testcase for unneeded trivial phi-nodes
* trivial-phi: make clean_up_phi() more sequential
* trivial-phi: extract trivial_phi() from clean_up_phi()
* trivial-phi: early return
* trivial-phi: use a temp var for the real source
* trivial-phi: directly return the unique value
* trivial-phi: remove more complex trivial phi-nodes
* stricter warning for explicit cast to ulong
* add linearization as a pass
* add testcases for missing return in last block
* use a temp var for function's upper-level statement
* topasm: top-level asm is special
* specialize linearize_compound_statement()
* there is always an active BB after linearize_fn_statement()
* the return BB is never terminated
* extract add_return() from linearize_return()
* use UNDEF for missing returns
* use a temp var for the return type/symbol
* return nothing only in void functions
* add testcases for wrong ordering in phi-nodes
* fix ordering of phi-node operand
* add tests for nested logical expr
* fix linearization of nested logical expr
* add testcase for non-constant switch-case
* fix linearization of non-constant switch-cases
* test: make test Waddress-space-strict succeed on 32-bit
* test: use integers of different sizes, even on 32-bit
* test: make 32-bit version of failed test
* ssa: relax what can be promoted
* doc: is_int_type() returns false for SYM_RESTRICTs
* enum: add testcase for UB in oversized shift
* enum: fix UB when rshifting by full width
* enum: add testcase for type of enum members
* enum: add testcase for base & enumerator type
* enum: fix cast_enum_list()
* enum: use the smallest type that fit
* enum: use the values to determine the base type
* enum: only warn (once) when mixing bitwiseness
* enum: warn when mixing different restricted types
* enum: warn on bad enums
* enum: rewrite bound checking
* enum: keep enumerators as int if they fit
* enum: default to unsigned
* enum: more specific error message for empty enum
* __attribute__((fallthrough)) can't simply be ignored
* ptrlist: add ptr_list_nth_entry()
* add testcase for missing function designator expansion
* fix expansion of function designator
* teach sparse about '-o '
* teach sparse about '-x '
* cgcc: add support to ignore argument(s) of options
* cgcc: teach about '-o '
* cgcc: teach about '-x c'
* dump-macro: break the loop at TOKEN_UNTAINT
* dump-macro: simplify processing of whitespace
* fix implicit K&R argument types
* Use -Wimplicit-int when warning about missing K&R argument types
* Accept comma-separated list for function declarations.
* cgcc: use 'i386' for the arch instead of 'i86'
* add testcase for missing deliminator ' or "
* man: add section about reporting bugs
* man: add AUTHORS section
* man: update maintainer info
* don't allow newlines inside string literals
* multi-buffer for idents
* as-name: add and use show_as()
* as-name: use idents for address spaces
* as-name: allow ident as address_space
* as-name: check for multiple address spaces at parsing time
* as-named: warn on bad address space
* add detection of native platform
* Consolidate 'machine detection' into "machine.h"
* test endianness with __BYTE_ORDER__
* testsuite: test predef macros on LP32/LP64/LLP64
* fix '__SIZE_TYPE__' for LLP64
* allow optional "_T" suffix to __SIZEOF_XXX__
* use bits_mask() for predefined_max()
* add builtin_type_suffix()
* teach sparse about asm inline
* remove duplicates from gcc-attr-list.h
* show-parse: strip do_show_type()'s trailing space
* make predefined_type_size() more generic
* give a type to wchar
* use the type for predefined_max()
* add predefined macros for wint_t
* add predefined macros for [u]intptr
* add predefined macros for [u]intmax
* add predefined macros for [u]int{8,16}_t
* add predefined macros for [u]int64_t
* add predefined macros for [u]int32_t
* add predefined macros for char{16,32}_t
* fix the size of long double
* add predefine for __CHAR_UNSIGNED__
* add predefine_min() and use it for __{WCHAR,WINT}_MIN__
* add a flag to warn on casts to/from bitwise pointers
* show-parse: don't display null ident in show_typename()
* show-parse: do not display base type's redundant specifiers
* show-parse: remove string_ctype from typenames
* VERSION=0.6.0-rc1
* build: only need includedir from llvm-config
* build: check if sparse-llvm needs libc++
* remove unneeded declarations in "compat.h"
* remove unused arg in add_branch()
* allocate BBs after the guards
* remove redundant check of _Bool bitsize
* remove unused regno()
* remove -finline-functions from CFLAGS
* remove self-assignment of base_type
* doc: fix list formatting
* as-name: document that identifiers are OK for address spaces
* add TODO list.
* Sparse v0.6.0
Ramsay Jones (9):
* Makefile: use locally built sparse in the selfcheck target
* sparsec: use a compatible exception model on cygwin
* sparsei: add the --[no-]jit options
* constant: add -Wconstant-suffix warning
* pre-process: suppress trailing space when dumping macros
* pre-process: print macros containing # and ## correctly
* pre-process: don't put spaces in macro parameter list
* pre-process: print variable argument macros correctly
* pre-process: add the -dM option to dump macro definitions
Randy Dunlap (6):
* sparse: minor manpage corrections
* Documentation: make data-structures.txt easier to read
* Documentation: editing fixes in test-suite
* lib.c: early return from handle_onoff_switch()
* sparse: ignore indirect_branch attribute
* sparse: option to print compound global data symbol info
Thiebaud Weksteen (1):
* Add testcases for bitwise cast on pointer
Tycho Andersen (1):
* expression.h: update comment to include other cast types
Uwe Kleine-König (6):
* build: make PREFIX overwritable from the environment
* build: put comment about local.mk to the place where it is included
* build: drop BASIC_CFLAGS and ALL_CFLAGS
* build: drop -g from LDFLAGS
* build: pass CPPFLAGS to compiler
* build: only generate version.h when needed
Vincenzo Frascino (1):
* print address space number for cast-from-AS warnings
sparse-0.6.4/Documentation/release-notes/v0.6.1.rst 0000664 0000000 0000000 00000002434 14115310122 0021750 0 ustar 00root root 0000000 0000000 v0.6.1 (2019-10-14)
===================
It's a small, 74 patches, release containing mainly small
fixes and improvements:
* improve build & test support for distros, mainly Debian
* stop warning on externally_visible functions without a prototype
* accept casts of __user/__iomem/... pointers to/from uintptr_t
* fix the underlying type of some enumeration values
* fix a build problem for sparse-llvm by using 'llvm-config --cppflags'
* conditionals (?:) may now be considered as constants if the condition is
* some error messages are now clearer or more coherent
* add missing expansion of compound literals
* improve parsing & checking of asm operands
* add missing expansion of asm operands
* expand some more builtins with constant operands (ffs, clz, ...)
* fix sparsec with recent version of cygwin
* fix crashes with some tools on toplevel asm.
Many thanks to people who have contributed to this release:
Uwe Kleine-König, Ramsay Jones, Randy Dunlap, Thomas Weißschuh,
Dan Carpenter, Jann Horn, Ben Dooks, Vegard Nossum, Aurelien Aptel,
Oliver Hartkopp, Linus Torvalds and Ilya Maximets.
The source code can be found at its usual repository:
git://git.kernel.org/pub/scm/devel/sparse/sparse.git v0.6.1
The tarballs are found at:
https://www.kernel.org/pub/software/devel/sparse/dist/
sparse-0.6.4/Documentation/release-notes/v0.6.2.rst 0000664 0000000 0000000 00000007761 14115310122 0021761 0 ustar 00root root 0000000 0000000 v0.6.2 (2020-06-21)
===================
* add a new tool: sindex - the semantic utility
Sindex is a simple to use cscope-like tool but understanding
how symbols are used and which can track struct members.
* add support for GCC's __auto_type
* add support for _Generic
* fully propagate declarations downward.
For example, it means that code like::
static int foo(void);
int foo(void) { return 0; }
now behaves as expected: foo() is effectively static.
* multi-arch:
* allow a single sparse executable to be used for multiple architectures
* add support for -mcmodel & -f{pic,PIC,pie,PIE}, mainly for RISC-V
* add new option, --arch=$ARCH, to specify the target architecture
* move all arch-specific code into separate files (target-$ARCH.c)
* try to support the various floating-point ABIs on ARM
* fix wchar_t & wint_t for openbsd
* add missing predefines for PPC
* add missing predefines: __amd64 & __amd64__
* sparc32 on SunOS/Solaris uses 128-bit long double
* fix wchar_t & wint_t on SunOS/Solaris
* teach sparse about -fshort-wchar
* keep cygwin specifics with i386/x86-64 specifics
* keep BSD & Darwin specifics with i386/x86-64 specifics
* fix the signedness of plain chars
* add support for s390 (ILP32)
* add predefine for __mips__
* predefine "i386" if needed
* pre-define __unix__ and friends
* add necessary defined for sunos-derived systems
* improved detection of the native OS
* warnings:
* improve diagnostic message about wrong redeclaration
* conditionally accept { 0 } without warnings
* add -Wexternal-function-has-definition
* display the bitfield name in error messages
* oversized bitfields are now errors
* add an option to suppress warning 'no newline at EOF'
* warn when jumping into statement expressions
* warn when using undefined labels
* warn on defined but unused labels
* attributes:
* allows '____' for all attributes.
* improve handling of function attributes
* separate modifiers into type/declaration
* add support for attributes 'unused' & 'gnu_inline'
* simplify parsing of inline/__tls/__visible
* better handle function-only attributes
* teach sparse about gnu_inline
* parse enum attributes and, for now, ignore them
* cgcc:
* use -fshort-char for Cygwin
* add support for riscv32 & riscv64
* don't define __CYGWIN32__ on 64-bit
* filter-out sparse-specific -msize-long & -msize-llp64
* use -mfloat-abi=hard for armhf
* define _BIG_ENDIAN when needed
* remove definition of _STRING_ARCH_unaligned (defined by glibc)
* removed unneeded predefines for integers (now defined by sparse)
* better multi-arch support by using --arch=$ARCH
* testsuite:
* avoid standard includes in the tests
* fix testcase with non-constant initializer
* IR
* add support for the linearization of builtins
* generate OP_UNREACH from __builtin_unreachable()
* add OP_UNREACH after calls to __noreturn functions
* doc:
* do not use obsolete sphinx's AutodocReporter
* Sphinx's minimal version is now 1.7
* add basic doc about the type system
* doc is now accessible as: https://sparse.docs.kernel.org
* release notes (old and current ones) have been added to the doc
* now using the sphinx_rtd_theme instead of the classic theme
* misc:
* add support for '-std=c17/c18'
* simplify testing of which version of the standard is used
* ensure that typeofs are evaluated before using show_typename()
* use a single way to expand typeofs
* various improvements to the 'dissect' tool
* simplify the parsing of type specifiers
* improve diagnostic messages concerning bitfields
* fix premature examination of dereferenced object
* various fixes for the expansion of constant symbols
* fix type compatibility of _Atomic types
* add support for builtin macros with argument
* add support for __has_feature() & __has_extension()
sparse-0.6.4/Documentation/release-notes/v0.6.3.rst 0000664 0000000 0000000 00000005032 14115310122 0021747 0 ustar 00root root 0000000 0000000 v0.6.3 (2020-10-17)
===================
Bug fixes:
* fix missing inlining of _Generic expression
* fix evaluation error with assignment of qualified arrays
* delay 'empty character constant' warning to phase 5
* simplify & fix parsing of array declarators
* accept whitespace after option -U
* teach dissect about _Generic
* reset locale after gtk_init() to workaround problems with strtold()
* fix linearization of shift-assign
* force to 0 expressions which are erroneously non-constant
* fix evaluate_ptr_add() when sizeof(offset) != sizeof(pointer)
* fix access to defining instruction in simplify_unop()
* fix evaluation of pointer to bool conversions
* fix usual conversion of integers
* fix null pointer deref on return expression with invalid type
New features:
* add support for arch specific asm constraints
* add memory asm constraint for PPC & S390
* prepend diagnostics with source's path and include chain
* add support for h8300, microblaze, nds32, openrisc, sh & xtensa
* add support for '-march=....' and use it for riscv
* add an option to specify the OS: --os=$OS
* add predefines for OS identification
* add predefines for __INT_LEAST${N}_TYPE__ & __INT_FAST${N}_TYPE__
* document the sparse's extensions
* sindex/semind: allow indexing outside the project tree
* rename tool 'sindex' to 'semind'
* add builtin support for __sync_{bool,val}_compare_and_swap()
* add support for wide strings
* union-cast: teach sparse about union casts
* add support for a new instruction: OP_FMADD
* add various warnings for dangerous usage of flexible array members
* add builtin support for __builtin_ia32_pause()
Misc changes:
* cleanup the handling of options flags
* avoid multiple warnings when inlining undeclared calls
* small fixes for alpha, arm, nios2, ppc, sparc & x86
* add missing predefines for endianness on arm, arm64, mips
* add various missing arch-specific predefines
* add the predefines '__cdecl', ... on cygwin
* warn on empty assignments & initializations
* reorganize the keyword parsing table
* the message in _Static_assert() is now optional (C2x)
* small fixes & improvement to the [online] documentation
* allow [*] in array declarators
* do not accept comma expressions in array declarator
* simplify parsing of attributes & storage class
* bad-shift: wait dead code elimination to warn about bad shifts
* fix is_scalar_type(): fouled types are scalars too
* better support for linearization of builtins
* remove definition of removed OP_{AND,OR}_BOOL
sparse-0.6.4/Documentation/release-notes/v0.6.4.rst 0000664 0000000 0000000 00000011102 14115310122 0021743 0 ustar 00root root 0000000 0000000 v0.6.4 (2020-09-06)
===================
Fixes:
* build: fix version.h dependencies
* fix and complete the evaluation of atomic builtins
* fix some testcases related to bitfield manipulation
* llvm: fix crash with llvm-11 / use real phi-nodes
* fix: OP_INLINE should not use the function symbol
* fix testing if a OP_CALL's function is pure
* warn on all missing parameter types
* fix init_linearized_builtins()
* fix usage count in linearize_fma()
* linearize: fix a couple of 'selfcheck' warnings
* cfg: remove phi-sources when merging BBs
* cfg: remove phi-nodes when merging BBs
* cfg: add missing REPEAT_CFG_CLEANUP
* fix: rebuild dominance tree during CFG cleanup
* fix: drop qualifiers of casts and comma or statement expressions
* fix kill_insn(OP_SETVAL)
* fix trivial_phi() when the target is before the single value
* memops: fix wrong killing of stores partially dominated by a load
* memops: kill dead loads before phi-node conversion
* memops: kill more dead stores
* fix rem_usage() when the pseudo has a use list but is not PSEUDO_REG
* shut up a silly -Wmaybe-uninitialized warning
* fix add_join_conditional() when one of the alternative is VOID
* asm: fix killing OP_ASM
* asm: fix a test failure on 32-bit systems
* asm: output *memory* operands need their address as *input*
* asm: teach dominates() about OP_ASM
* fix the type in the assignment of 0 to a restricted variable
* fix SSA conversion of mismatched memops
* fix and improve the check that protects try_to_simplify_bb()
* fix remove_merging_phisrc() with duplicated CFG edges.
* fix null-pointer crash with with ident same as one of the attributes
New:
* improve CFG simplification
* teach sparse about -funsigned-bitfields
* add a symbolic checker
* expand __builtin_object_size()
* let plain bitfields default to signed
* add support for __packed struct
* handle qualified anonymous structures
* move check_access() to late_warnings()
* let phi-sources to directly access to their phi-node
* small improvements to the ptrlist API
* warn when taking the address of a built-in function
* handle more graciously labels with no statement
* give an explicit type to compare's operands
* give a type to OP_SYMADDR
* add some notes about pseudos being typeless
* shrink struct basic_block
* pre-proc: strip leading "./" from include paths
* pre-proc: do some path normalization
* linearize __builtin_isdigit()
IR Simplifications:
* simplify: essential OP_ADD & OP_SUB simplifications
* simplify and canonicalize unsigned compares
* simplify: basic unop simplifications
* simplify SEL(SEL(...), ...)
* simplify SEL(x == y, x, y) and friends
* simplify SEL(x, x, x) and SEL(x, 0, x)
* simplify & canonicalize compares
* simplify CBR-CBR on the same condition
* simplify unrestricted postop
* simplification of computed gotos with 1 or 2 targets
* simplify kill_insn() of unops and unop-ish instructions
* simplify: put PSEUDO_ARGS and PSEUDO_REGs in canonical order too
* simplify (~x {&,|,^} x) --> {0,~0,~0}
* simplify ((x cmp y) {&,|,^} (x !cmp y)) --> {0,1,1}
* simplify LSR + SEXT into ASR
* simplify and canonicalize signed compares
* simplify CMP(AND(x,M), C) and CMP(OR(x,M), C)
* simplify AND(x >= 0, x < C) --> (unsigned)x < C
* simplify TRUNC(x) {==,!=} C --> AND(x,M) {==,!=} C
* simplify of TRUNC(NOT(x)) --> NOT(TRUNC(x))
* factorize (x OP1 z) OP2 (y OP1 z) into (x OP2 y) OP1 z
* factorize SHIFT(x, s) OP SHIFT(y, s) into SHIFT((x OP y), s)
* factorize SEL(x, OP(y,z), y) into OP(SEL(x, z, 0), y)
* convert SEL(x & BIT1, BIT2, 0) into SHIFT(x & BIT1, S)
* canonicalize ((x & M) == M) --> ((x & M) != 0) when M is a power-of-2
Testsuite:
* testsuite: add new tags: check-output-{match,returns}
* testsuite: fix parsing of tags used in the testcases
* testsuite: add option '-r' to 'test-suite format'
Documentation:
* doc: fix: Sphinx's option ':noindex:' renamed into ':noindexentry:'
* doc: fix extracted autodoc when short description ends with a '?'
* doc: add some doc about using NULL or VOID in pointer lists
* doc: add some doc to flowgraph.h
* doc: extract doc related to simplification
Cleanups:
* slice: small reorg of OP_SLICE in preparation for some incoming changes
* cleanup: removed an unused parameter for show_symbol_list()
* cleanup linearize_cond_branch()
* cleanup: remove unneeded REPEAT_SYMBOL_CLEANUP
* cleanup: no needs to use MARK_CURRENT_DELETED() for multi-jumps
* linearize: remove unneeded forward declarations
* linearize: only allocate call instructions when needed
sparse-0.6.4/Documentation/sphinx/ 0000775 0000000 0000000 00000000000 14115310122 0017126 5 ustar 00root root 0000000 0000000 sparse-0.6.4/Documentation/sphinx/cdoc.py 0000775 0000000 0000000 00000017000 14115310122 0020411 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
# SPDX_License-Identifier: MIT
#
# Copyright (C) 2018 Luc Van Oostenryck
#
"""
///
// Sparse source files may contain documentation inside block-comments
// specifically formatted::
//
// ///
// // Here is some doc
// // and here is some more.
//
// More precisely, a doc-block begins with a line containing only ``///``
// and continues with lines beginning by ``//`` followed by either a space,
// a tab or nothing, the first space after ``//`` is ignored.
//
// For functions, some additional syntax must be respected inside the
// block-comment::
//
// ///
// //
// //
// // @<1st parameter's name>:
// // @<2nd parameter's name>: which needs multiple lines>
// // @return: (absent for void functions)
// //
// //
// int somefunction(void *ptr, int count);
//
// Inside the description fields, parameter's names can be referenced
// by using ``@``. A function doc-block must directly precede
// the function it documents. This function can span multiple lines and
// can either be a function prototype (ending with ``;``) or a
// function definition.
//
// Some future versions will also allow to document structures, unions,
// enums, typedefs and variables.
//
// This documentation can be extracted into a .rst document by using
// the *autodoc* directive::
//
// .. c:autodoc:: file.c
//
"""
import re
class Lines:
def __init__(self, lines):
# type: (Iterable[str]) -> None
self.index = 0
self.lines = lines
self.last = None
self.back = False
def __iter__(self):
# type: () -> Lines
return self
def memo(self):
# type: () -> Tuple[int, str]
return (self.index, self.last)
def __next__(self):
# type: () -> Tuple[int, str]
if not self.back:
self.last = next(self.lines).rstrip()
self.index += 1
else:
self.back = False
return self.memo()
def next(self):
return self.__next__()
def undo(self):
# type: () -> None
self.back = True
def readline_multi(lines, line):
# type: (Lines, str) -> str
try:
while True:
(n, l) = next(lines)
if not l.startswith('//\t'):
raise StopIteration
line += '\n' + l[3:]
except:
lines.undo()
return line
def readline_delim(lines, delim):
# type: (Lines, Tuple[str, str]) -> Tuple[int, str]
try:
(lineno, line) = next(lines)
if line == '':
raise StopIteration
while line[-1] not in delim:
(n, l) = next(lines)
line += ' ' + l.lstrip()
except:
line = ''
return (lineno, line)
def process_block(lines):
# type: (Lines) -> Dict[str, Any]
info = { }
tags = []
desc = []
state = 'START'
(n, l) = lines.memo()
#print('processing line ' + str(n) + ': ' + l)
## is it a single line comment ?
m = re.match(r"^///\s+(.+)$", l) # /// ...
if m:
info['type'] = 'single'
info['desc'] = (n, m.group(1).rstrip())
return info
## read the multi line comment
for (n, l) in lines:
#print('state %d: %4d: %s' % (state, n, l))
if l.startswith('// '):
l = l[3:] ## strip leading '// '
elif l.startswith('//\t') or l == '//':
l = l[2:] ## strip leading '//'
else:
lines.undo() ## end of doc-block
break
if state == 'START': ## one-line short description
info['short'] = (n ,l)
state = 'PRE-TAGS'
elif state == 'PRE-TAGS': ## ignore empty line
if l != '':
lines.undo()
state = 'TAGS'
elif state == 'TAGS': ## match the '@tagnames'
m = re.match(r"^@([\w-]*)(:?\s*)(.*)", l)
if m:
tag = m.group(1)
sep = m.group(2)
## FIXME/ warn if sep != ': '
l = m.group(3)
l = readline_multi(lines, l)
tags.append((n, tag, l))
else:
lines.undo()
state = 'PRE-DESC'
elif state == 'PRE-DESC': ## ignore the first empty lines
if l != '': ## or first line of description
desc = [n, l]
state = 'DESC'
elif state == 'DESC': ## remaining lines -> description
desc.append(l)
else:
pass
## fill the info
if len(tags):
info['tags'] = tags
if len(desc):
info['desc'] = desc
## read the item (function only for now)
(n, line) = readline_delim(lines, (')', ';'))
if len(line):
line = line.rstrip(';')
#print('function: %4d: %s' % (n, line))
info['type'] = 'func'
info['func'] = (n, line)
else:
info['type'] = 'bloc'
return info
def process_file(f):
# type: (TextIOWrapper) -> List[Dict[str, Any]]
docs = []
lines = Lines(f)
for (n, l) in lines:
#print("%4d: %s" % (n, l))
if l.startswith('///'):
info = process_block(lines)
docs.append(info)
return docs
def decorate(l):
# type: (str) -> str
l = re.sub(r"@(\w+)", "**\\1**", l)
return l
def convert_to_rst(info):
# type: (Dict[str, Any]) -> List[Tuple[int, str]]
lst = []
#print('info= ' + str(info))
typ = info.get('type', '???')
if typ == '???':
## uh ?
pass
elif typ == 'bloc':
if 'short' in info:
(n, l) = info['short']
lst.append((n, l))
if 'desc' in info:
desc = info['desc']
n = desc[0] - 1
desc.append('')
for i in range(1, len(desc)):
l = desc[i]
lst.append((n+i, l))
# auto add a blank line for a list
if re.search(r":$", desc[i]) and re.search(r"\S", desc[i+1]):
lst.append((n+i, ''))
elif typ == 'func':
(n, l) = info['func']
l = '.. c:function:: ' + l
lst.append((n, l + '\n'))
if 'short' in info:
(n, l) = info['short']
l = l[0].capitalize() + l[1:].strip('.')
if l[-1] != '?':
l = l + '.'
lst.append((n, '\t' + l + '\n'))
if 'tags' in info:
for (n, name, l) in info.get('tags', []):
if name != 'return':
name = 'param ' + name
l = decorate(l)
l = '\t:%s: %s' % (name, l)
l = '\n\t\t'.join(l.split('\n'))
lst.append((n, l))
lst.append((n+1, ''))
if 'desc' in info:
desc = info['desc']
n = desc[0]
r = ''
for l in desc[1:]:
l = decorate(l)
r += '\t' + l + '\n'
lst.append((n, r))
return lst
def extract(f, filename):
# type: (TextIOWrapper, str) -> List[Tuple[int, str]]
res = process_file(f)
res = [ i for r in res for i in convert_to_rst(r) ]
return res
def dump_doc(lst):
# type: (List[Tuple[int, str]]) -> None
for (n, lines) in lst:
for l in lines.split('\n'):
print('%4d: %s' % (n, l))
n += 1
if __name__ == '__main__':
""" extract the doc from stdin """
import sys
dump_doc(extract(sys.stdin, ''))
from sphinx.util.docutils import switch_source_input
import docutils
import os
class CDocDirective(docutils.parsers.rst.Directive):
required_argument = 1
optional_arguments = 1
has_content = False
option_spec = {
}
def run(self):
env = self.state.document.settings.env
filename = os.path.join(env.config.cdoc_srcdir, self.arguments[0])
env.note_dependency(os.path.abspath(filename))
## create a (view) list from the extracted doc
lst = docutils.statemachine.ViewList()
f = open(filename, 'r')
for (lineno, lines) in extract(f, filename):
for l in lines.split('\n'):
lst.append(l.expandtabs(8), filename, lineno)
lineno += 1
## let parse this new reST content
memo = self.state.memo
save = memo.title_styles, memo.section_level
node = docutils.nodes.section()
try:
with switch_source_input(self.state, lst):
self.state.nested_parse(lst, 0, node, match_titles=1)
finally:
memo.title_styles, memo.section_level = save
return node.children
def setup(app):
app.add_config_value('cdoc_srcdir', None, 'env')
app.add_directive_to_domain('c', 'autodoc', CDocDirective)
return {
'version': '1.0',
'parallel_read_safe': True,
}
# vim: tabstop=4
sparse-0.6.4/Documentation/sphinx/ir.py 0000775 0000000 0000000 00000003341 14115310122 0020116 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
# SPDX_License-Identifier: MIT
#
# Copyright (C) 2018 Luc Van Oostenryck
#
"""
///
// To document the instructions used in the intermediate representation
// a new domain is defined: 'ir' with a directive::
//
// .. op:
//
// ...
//
// This is equivalent to using a definition list but with the name
// also placed in the index (with 'IR instruction' as descriptions).
"""
import docutils
import sphinx
class IROpDirective(docutils.parsers.rst.Directive):
# use the first line of content as the argument, this allow
# to not have to write a blanck line after the directive
final_argument_whitespace = True
required_argument = 0
#optional_arguments = 0
has_content = True
objtype = None
def run(self):
self.env = self.state.document.settings.env
source = self.state.document
lineno = self.lineno
text = self.content
name = text[0]
node = docutils.nodes.section()
node['ids'].append(name)
node.document = source
index = '.. index:: pair: %s; IR instruction' % name
content = docutils.statemachine.ViewList()
content.append(index, source, lineno)
content.append('' , source, lineno)
content.append(name , source, lineno)
content.append('' , source, lineno)
self.state.nested_parse(content, self.content_offset, node)
defnode = docutils.nodes.definition()
self.state.nested_parse(text[1:], self.content_offset, defnode)
node.append(defnode)
return [node]
class IRDomain(sphinx.domains.Domain):
"""IR domain."""
name = 'ir'
def setup(app):
app.add_domain(IRDomain)
app.add_directive_to_domain('ir', 'op', IROpDirective)
return {
'version': '1.0',
'parallel_read_safe': True,
}
# vim: tabstop=4
sparse-0.6.4/Documentation/sphinx/static/ 0000775 0000000 0000000 00000000000 14115310122 0020415 5 ustar 00root root 0000000 0000000 sparse-0.6.4/Documentation/sphinx/static/theme_overrides.css 0000664 0000000 0000000 00000000467 14115310122 0024322 0 ustar 00root root 0000000 0000000 p {
margin-bottom: 0.6em;
}
ul.simple {
margin-top: -0.5em;
margin-bottom: 0.5em;
}
.rst-content .toctree-wrapper ul {
margin-bottom: 0.5em;
}
.wy-menu-vertical a, .wy-menu-vertical li.current > a, .wy-menu-vertical p.caption {
padding: 0.2em 1.2em;
}
.wy-side-nav-search > a img.logo {
width: 60%;
}
sparse-0.6.4/Documentation/submitting-patches.md 0000664 0000000 0000000 00000001427 14115310122 0021755 0 ustar 00root root 0000000 0000000 Submitting patches
==================
Sparse uses a patch submit process similar to the Linux Kernel
[Submitting Patches](https://www.kernel.org/doc/html/v4.12/process/submitting-patches.html)
This document mostly focuses on the parts that might be different from the Linux
Kernel submitting process.
1. Git clone a sparse repository:
git clone git://git.kernel.org/pub/scm/devel/sparse/sparse.git
2. [Coding Style](https://www.kernel.org/doc/html/v4.12/process/coding-style.html) remains the same.
3. Sign off the patch.
The usage of the Signed-off-by tag is the same as [Linux Kernel Sign your work](https://www.kernel.org/doc/html/v4.12/process/submitting-patches.html#sign-your-work-the-developer-s-certificate-of-origin).
Notice that sparse uses the MIT License.
sparse-0.6.4/Documentation/templates/ 0000775 0000000 0000000 00000000000 14115310122 0017613 5 ustar 00root root 0000000 0000000 sparse-0.6.4/Documentation/templates/breadcrumbs.html 0000664 0000000 0000000 00000000561 14115310122 0022774 0 ustar 00root root 0000000 0000000 {%- extends "sphinx_rtd_theme/breadcrumbs.html" %}
{% block breadcrumbs_aside %}
{% if hasdoc(pagename) %}
{% if show_source and has_source and sourcename %}
{{ _('View page source') }}
{% endif %}
{% endif %}
{% endblock %}
sparse-0.6.4/Documentation/templates/layout.html 0000664 0000000 0000000 00000000415 14115310122 0022016 0 ustar 00root root 0000000 0000000 {% extends "!layout.html" %}
{% block menu %}
{{ super() }}
Index
{% endblock %}
sparse-0.6.4/Documentation/test-suite.rst 0000664 0000000 0000000 00000011653 14115310122 0020463 0 ustar 00root root 0000000 0000000 Test suite
##########
Sparse has a number of test cases in its validation directory. The test-suite
script aims at making automated checking of these tests possible. It works by
embedding tags in C comments in the test cases.
Tag's syntax
============
``check-name:`` *name*
Name of the test. This is the only mandatory tag.
``check-description:`` *description ...*
A description of what the test checks.
``check-command:`` *command arg ...*
There are different kinds of tests. Some can validate the sparse
preprocessor, while others will use sparse, cgcc, or even other backends
of the library. check-command allows you to give a custom command to
run the test-case.
The ``$file`` string is special. It will be expanded to the file name at
run time.
It defaults to ``sparse $file``.
``check-arch-ignore:`` *arch[|...]*
``check-arch-only:`` *arch[|...]*
Ignore the test if the current architecture (as returned by ``uname -m``)
matches or not one of the archs given in the pattern.
``check-assert:`` *condition*
Ignore the test if the given condition is false when evaluated as a
static assertion (``_Static_assert``).
``check-cpp-if:`` *condition*
Ignore the test if the given condition is false when evaluated
by sparse's pre-processor.
``check-exit-value:`` *value*
The expected exit value of check-command. It defaults to 0.
``check-timeout:`` *timeout*
The maximum expected duration of check-command, in seconds.
It defaults to 1.
``check-output-start`` / ``check-output-end``
The expected output (stdout and stderr) of check-command lies between
those two tags. It defaults to no output.
``check-output-ignore`` / ``check-error-ignore``
Don't check the expected output (stdout or stderr) of check-command
(useful when this output is not comparable or if you're only interested
in the exit value). By default this check is done.
``check-known-to-fail``
Mark the test as being known to fail.
``check-output-contains:`` *pattern*
Check that the output (stdout) contains the given pattern.
Several such tags can be given, in which case the output
must contains all the patterns.
``check-output-excludes:`` *pattern*
Similar than the above one, but with opposite logic.
Check that the output (stdout) doesn't contain the given pattern.
Several such tags can be given, in which case the output
must contains none of the patterns.
``check-output-pattern(``\ *nbr*\ ``):`` *pattern*
``check-output-pattern(``\ *min*\ ``,``\ *max*\ ``):`` *pattern*
Similar to the contains/excludes above, but with full control
of the number of times the pattern should occur in the output.
If *min* or *max* is ``-`` the corresponding check is ignored.
``check-output-match(``\ *start*\ ``):`` *pattern*
Check that in the output (stdout) all lines starting with the
first pattern also contains the second pattern. This should be
reserved for matching IR instructions since the '.$size' suffix
is ignored in the first pattern but is expected to be followed
by a space character.
``check-output-returns:`` *value*
Check that in the output (stdout) all IR return instructions
have the given value.
Using test-suite
================
The test-suite script is called through the check target of the Makefile. It
will try to check every test case it finds (``find validation -name '*.c'``).
It can be called to check a single test with::
$ cd validation
$ ./test-suite single preprocessor/preprocessor1.c
TEST Preprocessor #1 (preprocessor/preprocessor1.c)
preprocessor/preprocessor1.c passed !
Writing a test
==============
The test-suite comes with a format command to make a test easier to write::
test-suite format [-a] [-l] [-f] file [name [cmd]]
`name:` check-name value
If no name is provided, it defaults to the file name.
`cmd:` check-command value
If no cmd is provided, it defaults to ``sparse $file``.
The output of the test-suite format command can be redirected into the
test case to create a test-suite formatted file.::
$ ./test-suite format bad-assignment.c Assignment >> bad-assignment.c
$ cat !$
cat bad-assignment.c
/*
* check-name: bad assignment
*
* check-command: sparse $file
* check-exit-value: 1
*
* check-output-start
bad-assignment.c:3:6: error: Expected ; at end of statement
bad-assignment.c:3:6: error: got \
* check-output-end
*/
The same effect without the redirection can be achieved by using the ``-a``
option.
You can define the check-command you want to use for the test.::
$ ./test-suite format -a validation/preprocessor2.c "Preprocessor #2" \
"sparse -E \$file"
$ cat !$
cat validation/preprocessor2.c
/*
* This one we happen to get right.
*
* It should result in a simple
*
* a + b
*
* for a proper preprocessor.
*/
#define TWO a, b
#define UNARY(x) BINARY(x)
#define BINARY(x, y) x + y
UNARY(TWO)
/*
* check-name: Preprocessor #2
*
* check-command: sparse -E $file
* check-exit-value: 0
*
* check-output-start
a + b
* check-output-end
*/
sparse-0.6.4/Documentation/types.rst 0000664 0000000 0000000 00000010627 14115310122 0017521 0 ustar 00root root 0000000 0000000 ***********
Type System
***********
struct symbol is used to represent symbols & types but
most parts pertaining to the types are in the field 'ctype'.
For the purpose of this document, things can be simplified into:
.. code-block:: c
struct symbol {
enum type type; // SYM_...
struct ctype {
struct symbol *base_type;
unsigned long modifiers;
unsigned long alignment;
struct context_list *contexts;
struct indent *as;
};
};
Some bits, also related to the type, are in struct symbol itself:
* type
* size_bits
* rank
* variadic
* string
* designated_init
* forced_arg
* accessed
* transparent_union
* ``base_type`` is used for the associated base type.
* ``modifiers`` is a bit mask for type specifiers (MOD_UNSIGNED, ...),
type qualifiers (MOD_CONST, MOD_VOLATILE),
storage classes (MOD_STATIC, MOD_EXTERN, ...), as well for various
attributes. It's also used internally to keep track of some states
(MOD_ACCESS or MOD_ADDRESSABLE).
* ``alignment`` is used for the alignment, in bytes.
* ``contexts`` is used to store the informations associated with the
attribute ``context()``.
* ``as`` is used to hold the identifier of the attribute ``address_space()``.
Kind of types
=============
SYM_BASETYPE
------------
Used by integer, floating-point, void, 'type', 'incomplete' & bad types.
For integer types:
* .ctype.base_type points to ``int_ctype``, the generic/abstract integer type
* .ctype.modifiers has MOD_UNSIGNED/SIGNED/EXPLICITLY_SIGNED set accordingly.
For floating-point types:
* .ctype.base_type points to ``fp_ctype``, the generic/abstract float type
* .ctype.modifiers is zero.
For the other base types:
* .ctype.base_type is NULL
* .ctype.modifiers is zero.
SYM_NODE
--------
It's used to make variants of existing types. For example,
it's used as a top node for all declarations which can then
have their own modifiers, address_space, contexts or alignment
as well as the declaration's identifier.
Usage:
* .ctype.base_type points to the unmodified type (which must not
be a SYM_NODE itself)
* .ctype.modifiers, .as, .alignment, .contexts will contains
the 'variation' (MOD_CONST, the attributes, ...).
SYM_PTR
-------
For pointers:
* .ctype.base_type points to the pointee type
* .ctype.modifiers & .as are about the pointee too!
SYM_FN
------
For functions:
* .ctype.base_type points to the return type
* .ctype.modifiers & .as should be about the function itself
but some return type's modifiers creep here (for example, in
int foo(void), MOD_SIGNED will be set for the function).
SYM_ARRAY
---------
For arrays:
* .ctype.base_type points to the underlying type
* .ctype.modifiers & .as are a copy of the parent type (and unused)?
* for literal strings, the modifier also contains MOD_STATIC
* sym->array_size is *expression* for the array size.
SYM_STRUCT
----------
For structs:
* .ctype.base_type is NULL
* .ctype.modifiers & .as are not used?
* .ident is the name tag.
SYM_UNION
---------
Same as for structs.
SYM_ENUM
--------
For enums:
* .ctype.base_type points to the underlying type (integer)
* .ctype.modifiers contains the enum signedness
* .ident is the name tag.
SYM_BITFIELD
------------
For bitfields:
* .ctype.base_type points to the underlying type (integer)
* .ctype.modifiers & .as are a copy of the parent type (and unused)?
* .bit_size is the size of the bitfield.
SYM_RESTRICT
------------
Used for bitwise types (aka 'restricted' types):
* .ctype.base_type points to the underlying type (integer)
* .ctype.modifiers & .as are like for SYM_NODE and the modifiers
are inherited from the base type with MOD_SPECIFIER removed
* .ident is the typedef name (if any).
SYM_FOULED
----------
Used for bitwise types when the negation op (~) is
used and the bit_size is smaller than an ``int``.
There is a 1-to-1 mapping between a fouled type and
its parent bitwise type.
Usage:
* .ctype.base_type points to the parent type
* .ctype.modifiers & .as are the same as for the parent type
* .bit_size is bits_in_int.
SYM_TYPEOF
----------
Should not be present after evaluation:
* .initializer points to the expression representing the type
* .ctype is not used.
Typeofs with a type as argument are directly evaluated during parsing.
SYM_LABEL
---------
Used for labels only.
SYM_KEYWORD
-----------
Used for parsing only.
SYM_BAD
-------
Should not be used.
SYM_UNINTIALIZED
----------------
Should not be used.
sparse-0.6.4/FAQ 0000664 0000000 0000000 00000006477 14115310122 0013354 0 ustar 00root root 0000000 0000000 FAQ - Why sparse?
Q. Why not just use gcc?
A. Gcc is big, complex, and the gcc maintainers are not interested in
other uses of the gcc front-end. In fact, gcc has explicitly
resisted splitting up the front and back ends and having some common
intermediate language because of religious license issues - you can
have multiple front ends and back ends, but they all have to be part
of gcc and licensed under the GPL.
This all (in my opinion) makes gcc development harder than it should
be, and makes the end result very ungainly. With "sparse", the
front-end is very explicitly separated into its own independent
project, and is totally independent from the users. I don't want to
know what you do in the back-end, because I don't think I _should_
know or care.
Q. Why not GPL?
A. See the previous question: I personally think that the front end
must be a totally separate project from the back end: any other
approach just leads to insanity. However, at the same time clearly
we cannot write intermediate files etc crud (since then the back end
would have to re-parse the whole thing and would have to have its
own front end and just do a lot of things that do not make any sense
from a technical standpoint).
I like the GPL, but as rms says, "Linus is just an engineer". I
refuse to use a license if that license causes bad engineering
decisions. I want the front-end to be considered a separate
project, yet the GPL considers the required linking to make the
combined thing a derived work. Which is against the whole point
of 'sparse'.
I'm not interested in code generation. I'm not interested in what
other people do with their back-ends. I _am_ interested in making a
good front-end, and "good" means that people find it usable. And
they shouldn't be scared away by politics or licenses. If they want
to make their back-end be BSD/MIT licensed, that's great. And if
they want to have a proprietary back-end, that's ok by me too. It's
their loss, not mine.
Q. Does it really parse C?
A. Yeah, well... It parses a fairly complete subset of "extended C" as
defined by gcc. HOWEVER, since I don't believe in K&R syntax for
function declarations or in giving automatic integer types, it
doesn't do that. If you don't give types to your variables, they
won't have any types, and you can't use them.
Similarly, it will be very unhappy about undeclared functions,
rather than just assuming they have type "int".
Note that a large rationale for me doing this project is for type
following, which to some degree explains why the thing is type-anal
and refuses to touch the old-style pre-ANSI non-typed (or weakly
typed) constructs. Maybe somebody else who is working on projects
where pre-ANSI C makes sense might be more inclined to care about
ancient C. It's open source, after all. Go wild.
Q. What other sparse resources are available?
A. Wiki: http://sparse.wiki.kernel.org/index.php/Main_Page
Mailing list: linux-sparse@vger.kernel.org
See http://vger.kernel.org/vger-lists.html#linux-sparse for subscription
instructions and links to archives
Git repo: git://git.kernel.org/pub/scm/devel/sparse/sparse.git
gitweb: http://git.kernel.org/?p=devel/sparse/sparse.git
sparse-0.6.4/LICENSE 0000664 0000000 0000000 00000002726 14115310122 0014020 0 ustar 00root root 0000000 0000000 The 'sparse' C parser front-end library is copyrighted by Transmeta Corp
and other authors and licensed under the "MIT License" as
obtained from www.opensource.org (and included here-in for easy
reference).
[ This copy of the license is the flat-text version of original,
available in its full glory at
http://opensource.org/licenses/MIT
please refer to there for the authoritative and slightly more
pretty-printed version ]
------
The MIT License (MIT)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
sparse-0.6.4/Makefile 0000664 0000000 0000000 00000020211 14115310122 0014440 0 ustar 00root root 0000000 0000000 VERSION=0.6.4
########################################################################
# The following variables can be overwritten from the command line
OS = linux
CC = gcc
CXX = g++
LD = $(CC)
AR = ar
CFLAGS ?= -O2 -g
DESTDIR ?=
PREFIX ?= $(HOME)
BINDIR ?= $(PREFIX)/bin
MANDIR ?= $(PREFIX)/share/man
PKG_CONFIG ?= pkg-config
CHECKER_FLAGS ?= -Wno-vla
# Allow users to override build settings without dirtying their trees
# For debugging, put this in local.mk:
#
# CFLAGS += -O0 -DDEBUG -g3 -gdwarf-2
#
SPARSE_LOCAL_CONFIG ?= local.mk
-include ${SPARSE_LOCAL_CONFIG}
########################################################################
LIB_OBJS :=
LIB_OBJS += allocate.o
LIB_OBJS += builtin.o
LIB_OBJS += char.o
LIB_OBJS += compat-$(OS).o
LIB_OBJS += cse.o
LIB_OBJS += dissect.o
LIB_OBJS += dominate.o
LIB_OBJS += evaluate.o
LIB_OBJS += expand.o
LIB_OBJS += expression.o
LIB_OBJS += flow.o
LIB_OBJS += flowgraph.o
LIB_OBJS += inline.o
LIB_OBJS += ir.o
LIB_OBJS += lib.o
LIB_OBJS += linearize.o
LIB_OBJS += liveness.o
LIB_OBJS += memops.o
LIB_OBJS += opcode.o
LIB_OBJS += optimize.o
LIB_OBJS += options.o
LIB_OBJS += parse.o
LIB_OBJS += predefine.o
LIB_OBJS += pre-process.o
LIB_OBJS += ptrlist.o
LIB_OBJS += ptrmap.o
LIB_OBJS += scope.o
LIB_OBJS += show-parse.o
LIB_OBJS += simplify.o
LIB_OBJS += sort.o
LIB_OBJS += ssa.o
LIB_OBJS += stats.o
LIB_OBJS += storage.o
LIB_OBJS += symbol.o
LIB_OBJS += target.o
LIB_OBJS += target-alpha.o
LIB_OBJS += target-arm.o
LIB_OBJS += target-arm64.o
LIB_OBJS += target-bfin.o
LIB_OBJS += target-default.o
LIB_OBJS += target-h8300.o
LIB_OBJS += target-m68k.o
LIB_OBJS += target-microblaze.o
LIB_OBJS += target-mips.o
LIB_OBJS += target-nds32.o
LIB_OBJS += target-nios2.o
LIB_OBJS += target-openrisc.o
LIB_OBJS += target-ppc.o
LIB_OBJS += target-riscv.o
LIB_OBJS += target-s390.o
LIB_OBJS += target-sh.o
LIB_OBJS += target-sparc.o
LIB_OBJS += target-x86.o
LIB_OBJS += target-xtensa.o
LIB_OBJS += tokenize.o
LIB_OBJS += unssa.o
LIB_OBJS += utils.o
LIB_OBJS += version.o
PROGRAMS :=
PROGRAMS += compile
PROGRAMS += ctags
PROGRAMS += example
PROGRAMS += graph
PROGRAMS += obfuscate
PROGRAMS += sparse
PROGRAMS += test-dissect
PROGRAMS += test-lexing
PROGRAMS += test-linearize
PROGRAMS += test-parsing
PROGRAMS += test-show-type
PROGRAMS += test-unssa
INST_PROGRAMS=sparse cgcc
INST_MAN1=sparse.1 cgcc.1
all:
########################################################################
# common flags/options/...
cflags = -fno-strict-aliasing
cflags += -Wall -Wwrite-strings
GCC_BASE := $(shell $(CC) --print-file-name=)
cflags += -DGCC_BASE=\"$(GCC_BASE)\"
MULTIARCH_TRIPLET := $(shell $(CC) -print-multiarch 2>/dev/null)
cflags += -DMULTIARCH_TRIPLET=\"$(MULTIARCH_TRIPLET)\"
bindir := $(DESTDIR)$(BINDIR)
man1dir := $(DESTDIR)$(MANDIR)/man1
########################################################################
# target specificities
compile: compile-i386.o
EXTRA_OBJS += compile-i386.o
# Can we use GCC's generated dependencies?
HAVE_GCC_DEP:=$(shell touch .gcc-test.c && \
$(CC) -c -Wp,-MP,-MMD,.gcc-test.d .gcc-test.c 2>/dev/null && \
echo 'yes'; rm -f .gcc-test.d .gcc-test.o .gcc-test.c)
ifeq ($(HAVE_GCC_DEP),yes)
cflags += -Wp,-MP,-MMD,$(@D)/.$(@F).d
endif
# Can we use libxml (needed for c2xml)?
HAVE_LIBXML:=$(shell $(PKG_CONFIG) --exists libxml-2.0 2>/dev/null && echo 'yes')
ifeq ($(HAVE_LIBXML),yes)
PROGRAMS+=c2xml
INST_PROGRAMS+=c2xml
c2xml-ldlibs := $(shell $(PKG_CONFIG) --libs libxml-2.0)
c2xml-cflags := $(shell $(PKG_CONFIG) --cflags libxml-2.0)
else
$(warning Your system does not have libxml, disabling c2xml)
endif
HAVE_SQLITE := $(shell $(PKG_CONFIG) --exists sqlite3 2>/dev/null && echo 'yes')
ifeq ($(HAVE_SQLITE),yes)
SQLITE_VERSION:=$(shell $(PKG_CONFIG) --modversion sqlite3)
SQLITE_VNUMBER:=$(shell printf '%d%02d%02d' $(subst ., ,$(SQLITE_VERSION)))
ifeq ($(shell expr "$(SQLITE_VNUMBER)" '>=' 32400),1)
PROGRAMS += semind
INST_PROGRAMS += semind
INST_MAN1 += semind.1
semind-ldlibs := $(shell $(PKG_CONFIG) --libs sqlite3)
semind-cflags := $(shell $(PKG_CONFIG) --cflags sqlite3)
semind-cflags += -std=gnu99
else
$(warning Your SQLite3 version ($(SQLITE_VERSION)) is too old, 3.24.0 or later is required.)
endif
else
$(warning Your system does not have sqlite3, disabling semind)
endif
# Can we use gtk (needed for test-inspect)
GTK_VERSION:=3.0
HAVE_GTK:=$(shell $(PKG_CONFIG) --exists gtk+-$(GTK_VERSION) 2>/dev/null && echo 'yes')
ifneq ($(HAVE_GTK),yes)
GTK_VERSION:=2.0
HAVE_GTK:=$(shell $(PKG_CONFIG) --exists gtk+-$(GTK_VERSION) 2>/dev/null && echo 'yes')
endif
ifeq ($(HAVE_GTK),yes)
GTK_CFLAGS := $(shell $(PKG_CONFIG) --cflags gtk+-$(GTK_VERSION))
ast-view-cflags := $(GTK_CFLAGS)
ast-model-cflags := $(GTK_CFLAGS)
ast-inspect-cflags := $(GTK_CFLAGS)
test-inspect-cflags := $(GTK_CFLAGS)
test-inspect-ldlibs := $(shell $(PKG_CONFIG) --libs gtk+-$(GTK_VERSION))
test-inspect: ast-model.o ast-view.o ast-inspect.o
EXTRA_OBJS += ast-model.o ast-view.o ast-inspect.o
PROGRAMS += test-inspect
INST_PROGRAMS += test-inspect
else
$(warning Your system does not have gtk3/gtk2, disabling test-inspect)
endif
# Can we use LLVM (needed for ... sparse-llvm)?
LLVM_CONFIG:=llvm-config
HAVE_LLVM:=$(shell $(LLVM_CONFIG) --version >/dev/null 2>&1 && echo 'yes')
ifeq ($(HAVE_LLVM),yes)
arch := $(shell uname -m)
ifeq (${MULTIARCH_TRIPLET},x86_64-linux-gnux32)
arch := x32
endif
ifneq ($(filter ${arch},i386 i486 i586 i686 x86_64 amd64),)
LLVM_VERSION:=$(shell $(LLVM_CONFIG) --version)
LLVM_VERSION_MAJOR:=$(firstword $(subst ., ,$(LLVM_VERSION)))
ifeq ($(shell expr "$(LLVM_VERSION_MAJOR)" '>=' 3),1)
LLVM_PROGS := sparse-llvm
$(LLVM_PROGS): LD := $(CXX)
LLVM_LDFLAGS := $(shell $(LLVM_CONFIG) --ldflags)
LLVM_CFLAGS := $(shell $(LLVM_CONFIG) --cppflags)
LLVM_LIBS := $(shell $(LLVM_CONFIG) --libs)
LLVM_LIBS += $(shell $(LLVM_CONFIG) --system-libs 2>/dev/null)
LLVM_LIBS += $(shell $(LLVM_CONFIG) --cxxflags | grep -F -q -e '-stdlib=libc++' && echo -lc++)
PROGRAMS += $(LLVM_PROGS)
INST_PROGRAMS += sparse-llvm sparsec
sparse-llvm-cflags := $(LLVM_CFLAGS)
sparse-llvm-ldflags := $(LLVM_LDFLAGS)
sparse-llvm-ldlibs := $(LLVM_LIBS)
else
$(warning LLVM 3.0 or later required. Your system has version $(LLVM_VERSION) installed.)
endif
else
$(warning sparse-llvm disabled on ${arch})
endif
else
$(warning Your system does not have llvm, disabling sparse-llvm)
endif
ifeq ($(HAVE_BOOLECTOR),yes)
PROGRAMS += scheck
scheck-cflags := -I${BOOLECTORDIR}/include/boolector
scheck-ldflags := -L${BOOLECTORDIR}/lib
scheck-ldlibs := -lboolector -llgl -lbtor2parser
endif
########################################################################
LIBS := libsparse.a
OBJS := $(LIB_OBJS) $(EXTRA_OBJS) $(PROGRAMS:%=%.o)
# Pretty print
V := @
Q := $(V:1=)
########################################################################
all: $(PROGRAMS)
ldflags += $($(@)-ldflags) $(LDFLAGS)
ldlibs += $($(@)-ldlibs) $(LDLIBS)
$(PROGRAMS): % : %.o $(LIBS)
@echo " LD $@"
$(Q)$(LD) $(ldflags) $^ $(ldlibs) -o $@
libsparse.a: $(LIB_OBJS)
@echo " AR $@"
$(Q)$(AR) rcs $@ $^
cflags += $($(*)-cflags) $(CPPFLAGS) $(CFLAGS)
%.o: %.c
@echo " CC $@"
$(Q)$(CC) $(cflags) -c -o $@ $<
%.sc: %.c sparse
@echo " CHECK $<"
$(Q)CHECK=./sparse ./cgcc -no-compile $(CHECKER_FLAGS) $(cflags) -c $<
selfcheck: $(OBJS:.o=.sc)
SPARSE_VERSION:=$(shell git describe --dirty 2>/dev/null || echo '$(VERSION)')
version.o: version.h
version.h: FORCE
@echo '#define SPARSE_VERSION "$(SPARSE_VERSION)"' > version.h.tmp
@if cmp -s version.h version.h.tmp; then \
rm version.h.tmp; \
else \
echo " GEN $@"; \
mv version.h.tmp version.h; \
fi
check: all
$(Q)cd validation && ./test-suite
validation/%: $(PROGRAMS) FORCE
$(Q)validation/test-suite $*
clean: clean-check
@rm -f *.[oa] .*.d $(PROGRAMS) version.h
clean-check:
@echo " CLEAN"
@find validation/ \( -name "*.c.output.*" \
-o -name "*.c.error.*" \
-o -name "*.o" \
\) -exec rm {} \;
install: install-bin install-man
install-bin: $(INST_PROGRAMS:%=$(bindir)/%)
install-man: $(INST_MAN1:%=$(man1dir)/%)
$(bindir)/%: %
@echo " INSTALL $@"
$(Q)install -D $< $@ || exit 1;
$(man1dir)/%: %
@echo " INSTALL $@"
$(Q)install -D -m 644 $< $@ || exit 1;
.PHONY: FORCE
# GCC's dependencies
-include $(OBJS:%.o=.%.o.d)
sparse-0.6.4/README 0000664 0000000 0000000 00000005677 14115310122 0013703 0 ustar 00root root 0000000 0000000
sparse (spärs), adj,., spars-er, spars-est.
1. thinly scattered or distributed; "a sparse population"
2. thin; not thick or dense: "sparse hair"
3. scanty; meager.
4. semantic parse
[ from Latin: spars(us) scattered, past participle of
spargere 'to sparge' ]
Antonym: abundant
Sparse is a semantic parser of source files: it's neither a compiler
(although it could be used as a front-end for one) nor is it a
preprocessor (although it contains as a part of it a preprocessing
phase).
It is meant to be a small - and simple - library. Scanty and meager,
and partly because of that easy to use. It has one mission in life:
create a semantic parse tree for some arbitrary user for further
analysis. It's not a tokenizer, nor is it some generic context-free
parser. In fact, context (semantics) is what it's all about - figuring
out not just what the grouping of tokens are, but what the _types_ are
that the grouping implies.
And no, it doesn't use lex and yacc (or flex and bison). In my personal
opinion, the result of using lex/yacc tends to end up just having to
fight the assumptions the tools make.
The parsing is done in five phases:
- full-file tokenization
- pre-processing (which can cause another tokenization phase of another
file)
- semantic parsing.
- lazy type evaluation
- inline function expansion and tree simplification
Note the "full file" part. Partly for efficiency, but mostly for ease of
use, there are no "partial results". The library completely parses one
whole source file, and builds up the _complete_ parse tree in memory.
Also note the "lazy" in the type evaluation. The semantic parsing
itself will know which symbols are typedefines (required for parsing C
correctly), but it will not have calculated what the details of the
different types are. That will be done only on demand, as the back-end
requires the information.
This means that a user of the library will literally just need to do
struct string_list *filelist = NULL;
char *file;
action(sparse_initialize(argc, argv, filelist));
FOR_EACH_PTR(filelist, file) {
action(sparse(file));
} END_FOR_EACH_PTR(file);
and he is now done - having a full C parse of the file he opened. The
library doesn't need any more setup, and once done does not impose any
more requirements. The user is free to do whatever he wants with the
parse tree that got built up, and needs not worry about the library ever
again. There is no extra state, there are no parser callbacks, there is
only the parse tree that is described by the header files. The action
funtion takes a pointer to a symbol_list and does whatever it likes with it.
The library also contains (as an example user) a few clients that do the
preprocessing, parsing and type evaluation and just print out the
results. These clients were done to verify and debug the library, and
also as trivial examples of what you can do with the parse tree once it
is formed, so that users can see how the tree is organized.
sparse-0.6.4/allocate.c 0000664 0000000 0000000 00000011250 14115310122 0014733 0 ustar 00root root 0000000 0000000 /*
* allocate.c - simple space-efficient blob allocator.
*
* Copyright (C) 2003 Transmeta Corp.
* 2003-2004 Linus Torvalds
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* Simple allocator for data that doesn't get partially free'd.
* The tokenizer and parser allocate a _lot_ of small data structures
* (often just two-three bytes for things like small integers),
* and since they all depend on each other you can't free them
* individually _anyway_. So do something that is very space-
* efficient: allocate larger "blobs", and give out individual
* small bits and pieces of it with no maintenance overhead.
*/
#include
#include
#include
#include "lib.h"
#include "allocate.h"
#include "compat.h"
#include "token.h"
#include "symbol.h"
#include "scope.h"
#include "expression.h"
#include "linearize.h"
void protect_allocations(struct allocator_struct *desc)
{
desc->blobs = NULL;
}
void drop_all_allocations(struct allocator_struct *desc)
{
struct allocation_blob *blob = desc->blobs;
desc->blobs = NULL;
desc->allocations = 0;
desc->total_bytes = 0;
desc->useful_bytes = 0;
desc->freelist = NULL;
while (blob) {
struct allocation_blob *next = blob->next;
blob_free(blob, desc->chunking);
blob = next;
}
}
void free_one_entry(struct allocator_struct *desc, void *entry)
{
void **p = entry;
*p = desc->freelist;
desc->freelist = p;
}
void *allocate(struct allocator_struct *desc, unsigned int size)
{
unsigned long alignment = desc->alignment;
struct allocation_blob *blob = desc->blobs;
void *retval;
/*
* NOTE! The freelist only works with things that are
* (a) sufficiently aligned
* (b) use a constant size
* Don't try to free allocators that don't follow
* these rules.
*/
if (desc->freelist) {
void **p = desc->freelist;
retval = p;
desc->freelist = *p;
do {
*p = NULL;
p++;
} while ((size -= sizeof(void *)) > 0);
return retval;
}
desc->allocations++;
desc->useful_bytes += size;
size = (size + alignment - 1) & ~(alignment-1);
if (!blob || blob->left < size) {
unsigned int offset, chunking = desc->chunking;
struct allocation_blob *newblob = blob_alloc(chunking);
if (!newblob)
die("out of memory");
if (size > chunking)
die("alloc too big");
desc->total_bytes += chunking;
newblob->next = blob;
blob = newblob;
desc->blobs = newblob;
offset = offsetof(struct allocation_blob, data);
offset = (offset + alignment - 1) & ~(alignment-1);
blob->left = chunking - offset;
blob->offset = offset - offsetof(struct allocation_blob, data);
}
retval = blob->data + blob->offset;
blob->offset += size;
blob->left -= size;
return retval;
}
void show_allocations(struct allocator_struct *x)
{
fprintf(stderr, "%s: %d allocations, %lu bytes (%lu total bytes, "
"%6.2f%% usage, %6.2f average size)\n",
x->name, x->allocations, x->useful_bytes, x->total_bytes,
100 * (double) x->useful_bytes / x->total_bytes,
(double) x->useful_bytes / x->allocations);
}
void get_allocator_stats(struct allocator_struct *x, struct allocator_stats *s)
{
s->name = x->name;
s->allocations = x->allocations;
s->useful_bytes = x->useful_bytes;
s->total_bytes = x->total_bytes;
}
ALLOCATOR(ident, "identifiers");
ALLOCATOR(token, "tokens");
ALLOCATOR(context, "contexts");
ALLOCATOR(symbol, "symbols");
ALLOCATOR(asm_operand, "asmops");
ALLOCATOR(expression, "expressions");
ALLOCATOR(statement, "statements");
ALLOCATOR(string, "strings");
ALLOCATOR(scope, "scopes");
__DO_ALLOCATOR(void, 0, 1, "bytes", bytes);
ALLOCATOR(basic_block, "basic_block");
ALLOCATOR(entrypoint, "entrypoint");
ALLOCATOR(instruction, "instruction");
ALLOCATOR(multijmp, "multijmp");
ALLOCATOR(pseudo, "pseudo");
sparse-0.6.4/allocate.h 0000664 0000000 0000000 00000005257 14115310122 0014752 0 ustar 00root root 0000000 0000000 #ifndef ALLOCATE_H
#define ALLOCATE_H
#include "compat.h"
struct allocation_blob {
struct allocation_blob *next;
unsigned int left, offset;
unsigned char data[];
};
struct allocator_struct {
const char *name;
struct allocation_blob *blobs;
unsigned int alignment;
unsigned int chunking;
void *freelist;
/* statistics */
unsigned int allocations;
unsigned long total_bytes, useful_bytes;
};
struct allocator_stats {
const char *name;
unsigned int allocations;
unsigned long total_bytes, useful_bytes;
};
extern void protect_allocations(struct allocator_struct *desc);
extern void drop_all_allocations(struct allocator_struct *desc);
extern void *allocate(struct allocator_struct *desc, unsigned int size);
extern void free_one_entry(struct allocator_struct *desc, void *entry);
extern void show_allocations(struct allocator_struct *);
extern void get_allocator_stats(struct allocator_struct *, struct allocator_stats *);
extern void show_allocation_stats(void);
#define __DECLARE_ALLOCATOR(type, x) \
extern type *__alloc_##x(int); \
extern void __free_##x(type *); \
extern void show_##x##_alloc(void); \
extern void get_##x##_stats(struct allocator_stats *); \
extern void clear_##x##_alloc(void); \
extern void protect_##x##_alloc(void);
#define DECLARE_ALLOCATOR(x) __DECLARE_ALLOCATOR(struct x, x)
#define __DO_ALLOCATOR(type, objsize, objalign, objname, x) \
static struct allocator_struct x##_allocator = { \
.name = objname, \
.alignment = objalign, \
.chunking = CHUNK }; \
type *__alloc_##x(int extra) \
{ \
return allocate(&x##_allocator, objsize+extra); \
} \
void __free_##x(type *entry) \
{ \
free_one_entry(&x##_allocator, entry); \
} \
void show_##x##_alloc(void) \
{ \
show_allocations(&x##_allocator); \
} \
void get_##x##_stats(struct allocator_stats *s) \
{ \
get_allocator_stats(&x##_allocator, s); \
} \
void clear_##x##_alloc(void) \
{ \
drop_all_allocations(&x##_allocator); \
} \
void protect_##x##_alloc(void) \
{ \
protect_allocations(&x##_allocator); \
}
#define __ALLOCATOR(t, n, x) \
__DO_ALLOCATOR(t, sizeof(t), __alignof__(t), n, x)
#define ALLOCATOR(x, n) __ALLOCATOR(struct x, n, x)
DECLARE_ALLOCATOR(ident);
DECLARE_ALLOCATOR(token);
DECLARE_ALLOCATOR(context);
DECLARE_ALLOCATOR(symbol);
DECLARE_ALLOCATOR(asm_operand);
DECLARE_ALLOCATOR(expression);
DECLARE_ALLOCATOR(statement);
DECLARE_ALLOCATOR(string);
DECLARE_ALLOCATOR(scope);
__DECLARE_ALLOCATOR(void, bytes);
DECLARE_ALLOCATOR(basic_block);
DECLARE_ALLOCATOR(entrypoint);
DECLARE_ALLOCATOR(instruction);
DECLARE_ALLOCATOR(multijmp);
DECLARE_ALLOCATOR(pseudo);
#endif
sparse-0.6.4/ast-inspect.c 0000664 0000000 0000000 00000015314 14115310122 0015406 0 ustar 00root root 0000000 0000000
#include "token.h"
#include "parse.h"
#include "symbol.h"
#include "ast-inspect.h"
#include "expression.h"
static inline void inspect_ptr_list(AstNode *node, const char *name, void (*inspect)(AstNode *))
{
struct ptr_list *ptrlist = node->ptr;
void *ptr;
int i = 0;
node->text = g_strdup_printf("%s %s:", node->text, name);
FOR_EACH_PTR(ptrlist, ptr) {
char *index = g_strdup_printf("%d: ", i++);
ast_append_child(node, index, ptr, inspect);
} END_FOR_EACH_PTR(ptr);
}
static const char *statement_type_name(enum statement_type type)
{
static const char *statement_type_name[] = {
[STMT_NONE] = "STMT_NONE",
[STMT_DECLARATION] = "STMT_DECLARATION",
[STMT_EXPRESSION] = "STMT_EXPRESSION",
[STMT_COMPOUND] = "STMT_COMPOUND",
[STMT_IF] = "STMT_IF",
[STMT_RETURN] = "STMT_RETURN",
[STMT_CASE] = "STMT_CASE",
[STMT_SWITCH] = "STMT_SWITCH",
[STMT_ITERATOR] = "STMT_ITERATOR",
[STMT_LABEL] = "STMT_LABEL",
[STMT_GOTO] = "STMT_GOTO",
[STMT_ASM] = "STMT_ASM",
[STMT_CONTEXT] = "STMT_CONTEXT",
[STMT_RANGE] = "STMT_RANGE",
};
return statement_type_name[type] ?: "UNKNOWN_STATEMENT_TYPE";
}
void inspect_statement(AstNode *node)
{
struct statement *stmt = node->ptr;
node->text = g_strdup_printf("%s %s:", node->text, statement_type_name(stmt->type));
switch (stmt->type) {
case STMT_COMPOUND:
ast_append_child(node, "stmts:", stmt->stmts, inspect_statement_list);
break;
case STMT_EXPRESSION:
ast_append_child(node, "expression:", stmt->expression, inspect_expression);
break;
case STMT_IF:
ast_append_child(node, "conditional:", stmt->if_conditional, inspect_expression);
ast_append_child(node, "if_true:", stmt->if_true, inspect_statement);
ast_append_child(node, "if_false:", stmt->if_false, inspect_statement);
break;
case STMT_ITERATOR:
ast_append_child(node, "break:", stmt->iterator_break, inspect_symbol);
ast_append_child(node, "continue:", stmt->iterator_continue, inspect_symbol);
ast_append_child(node, "pre_statement:", stmt->iterator_pre_statement,
inspect_statement);
ast_append_child(node, "statement:", stmt->iterator_statement,
inspect_statement);
ast_append_child(node, "post_statement:", stmt->iterator_post_statement,
inspect_statement);
break;
case STMT_SWITCH:
ast_append_child(node, "switch_expression:", stmt->switch_expression, inspect_expression);
ast_append_child(node, "switch_statement:", stmt->switch_statement, inspect_statement);
ast_append_child(node, "switch_break:", stmt->switch_break, inspect_symbol);
ast_append_child(node, "switch_case:", stmt->switch_case, inspect_symbol);
break;
case STMT_CASE:
ast_append_child(node, "case_expression:", stmt->case_expression, inspect_expression);
ast_append_child(node, "case_to:", stmt->case_to, inspect_expression);
ast_append_child(node, "case_statement:", stmt->case_statement, inspect_statement);
ast_append_child(node, "case_label:", stmt->case_label, inspect_symbol);
break;
case STMT_RETURN:
ast_append_child(node, "ret_value:", stmt->ret_value, inspect_expression);
ast_append_child(node, "ret_target:", stmt->ret_target, inspect_symbol);
break;
default:
break;
}
}
void inspect_statement_list(AstNode *node)
{
inspect_ptr_list(node, "statement_list", inspect_statement);
}
static const char *symbol_type_name(enum type type)
{
static const char *type_name[] = {
[SYM_UNINITIALIZED] = "SYM_UNINITIALIZED",
[SYM_PREPROCESSOR] = "SYM_PREPROCESSOR",
[SYM_BASETYPE] = "SYM_BASETYPE",
[SYM_NODE] = "SYM_NODE",
[SYM_PTR] = "SYM_PTR",
[SYM_FN] = "SYM_FN",
[SYM_ARRAY] = "SYM_ARRAY",
[SYM_STRUCT] = "SYM_STRUCT",
[SYM_UNION] = "SYM_UNION",
[SYM_ENUM] = "SYM_ENUM",
[SYM_TYPEOF] = "SYM_TYPEOF",
[SYM_BITFIELD] = "SYM_BITFIELD",
[SYM_LABEL] = "SYM_LABEL",
[SYM_RESTRICT] = "SYM_RESTRICT",
[SYM_FOULED] = "SYM_FOULED",
[SYM_KEYWORD] = "SYM_KEYWORD",
[SYM_BAD] = "SYM_BAD",
};
return type_name[type] ?: "UNKNOWN_TYPE";
}
void inspect_symbol(AstNode *node)
{
struct symbol *sym = node->ptr;
node->text = g_strdup_printf("%s %s: %s", node->text, symbol_type_name(sym->type),
builtin_typename(sym) ?: show_ident(sym->ident));
ast_append_child(node, "ctype.base_type:", sym->ctype.base_type,inspect_symbol);
switch (sym->namespace) {
case NS_PREPROCESSOR:
break;
default:
ast_append_child(node, "arguments:", sym->arguments, inspect_symbol_list);
ast_append_child(node, "symbol_list:", sym->symbol_list, inspect_symbol_list);
ast_append_child(node, "stmt:", sym->stmt, inspect_statement);
break;
}
}
void inspect_symbol_list(AstNode *node)
{
inspect_ptr_list(node, "symbol_list", inspect_symbol);
}
static const char *expression_type_name(enum expression_type type)
{
static const char *expression_type_name[] = {
[EXPR_VALUE] = "EXPR_VALUE",
[EXPR_STRING] = "EXPR_STRING",
[EXPR_SYMBOL] = "EXPR_SYMBOL",
[EXPR_TYPE] = "EXPR_TYPE",
[EXPR_BINOP] = "EXPR_BINOP",
[EXPR_ASSIGNMENT] = "EXPR_ASSIGNMENT",
[EXPR_LOGICAL] = "EXPR_LOGICAL",
[EXPR_DEREF] = "EXPR_DEREF",
[EXPR_PREOP] = "EXPR_PREOP",
[EXPR_POSTOP] = "EXPR_POSTOP",
[EXPR_CAST] = "EXPR_CAST",
[EXPR_FORCE_CAST] = "EXPR_FORCE_CAST",
[EXPR_IMPLIED_CAST] = "EXPR_IMPLIED_CAST",
[EXPR_SIZEOF] = "EXPR_SIZEOF",
[EXPR_ALIGNOF] = "EXPR_ALIGNOF",
[EXPR_PTRSIZEOF] = "EXPR_PTRSIZEOF",
[EXPR_CONDITIONAL] = "EXPR_CONDITIONAL",
[EXPR_SELECT] = "EXPR_SELECT",
[EXPR_STATEMENT] = "EXPR_STATEMENT",
[EXPR_CALL] = "EXPR_CALL",
[EXPR_COMMA] = "EXPR_COMMA",
[EXPR_COMPARE] = "EXPR_COMPARE",
[EXPR_LABEL] = "EXPR_LABEL",
[EXPR_INITIALIZER] = "EXPR_INITIALIZER",
[EXPR_IDENTIFIER] = "EXPR_IDENTIFIER",
[EXPR_INDEX] = "EXPR_INDEX",
[EXPR_POS] = "EXPR_POS",
[EXPR_FVALUE] = "EXPR_FVALUE",
[EXPR_SLICE] = "EXPR_SLICE",
[EXPR_OFFSETOF] = "EXPR_OFFSETOF",
};
return expression_type_name[type] ?: "UNKNOWN_EXPRESSION_TYPE";
}
void inspect_expression(AstNode *node)
{
struct expression *expr = node->ptr;
node->text = g_strdup_printf("%s %s", node->text, expression_type_name(expr->type));
switch (expr->type) {
case EXPR_STATEMENT:
ast_append_child(node, "statement:", expr->statement, inspect_statement);
break;
case EXPR_BINOP:
case EXPR_COMMA:
case EXPR_COMPARE:
case EXPR_LOGICAL:
case EXPR_ASSIGNMENT:
ast_append_child(node, "left:", expr->left, inspect_expression);
ast_append_child(node, "right:", expr->right, inspect_expression);
break;
case EXPR_CAST:
case EXPR_FORCE_CAST:
case EXPR_IMPLIED_CAST:
ast_append_child(node, "cast_type:", expr->cast_type, inspect_symbol);
ast_append_child(node, "cast_expression:", expr->cast_expression, inspect_expression);
break;
case EXPR_PREOP:
ast_append_child(node, "unop:", expr->unop, inspect_expression);
break;
default:
break;
}
}
sparse-0.6.4/ast-inspect.h 0000664 0000000 0000000 00000000512 14115310122 0015405 0 ustar 00root root 0000000 0000000
#ifndef _AST_INSPECT_H_
#define _AST_INSPECT_H_
#include "ast-model.h"
void inspect_symbol(AstNode *node);
void inspect_symbol_list(AstNode *node);
void inspect_statement(AstNode *node);
void inspect_statement_list(AstNode *node);
void inspect_expression(AstNode *node);
void inspect_expression_list(AstNode *node);
#endif
sparse-0.6.4/ast-model.c 0000664 0000000 0000000 00000034602 14115310122 0015042 0 ustar 00root root 0000000 0000000 /*
* ast-model.c
*
* A custom tree model to simplify viewing of AST objects.
* Modify from the Gtk+ tree view tutorial, custom-list.c
* by Tim-Philipp Mueller < tim at centricular dot net >
*
* Copyright (C) 2010 Christopher Li
*/
#include "ast-model.h"
#include "stdint.h"
/* boring declarations of local functions */
static void ast_init(AstNode *pkg_tree);
static void ast_class_init(AstNodeClass *klass);
static void ast_tree_model_init(GtkTreeModelIface *iface);
static void ast_finalize(GObject *object);
static GtkTreeModelFlags ast_get_flags(GtkTreeModel *tree_model);
static gint ast_get_n_columns(GtkTreeModel *tree_model);
static GType ast_get_column_type(GtkTreeModel *tree_model, gint index);
static gboolean ast_get_iter(GtkTreeModel *tree_model, GtkTreeIter *iter,
GtkTreePath *path);
static GtkTreePath *ast_get_path(GtkTreeModel *tree_model, GtkTreeIter *iter);
static void ast_get_value(GtkTreeModel *tree_model, GtkTreeIter *iter,
gint column, GValue *value);
static gboolean ast_iter_next(GtkTreeModel *tree_model, GtkTreeIter *iter);
static gboolean ast_iter_children(GtkTreeModel *tree_model,
GtkTreeIter *iter,
GtkTreeIter *parent);
static gboolean ast_iter_has_child(GtkTreeModel *tree_model, GtkTreeIter *iter);
static gint ast_iter_n_children (GtkTreeModel *tree_model, GtkTreeIter *iter);
static gboolean ast_iter_nth_child(GtkTreeModel *tree_model, GtkTreeIter *iter,
GtkTreeIter *parent, gint n);
static gboolean ast_iter_parent(GtkTreeModel *tree_model,
GtkTreeIter *iter,
GtkTreeIter *child);
static GObjectClass *parent_class = NULL; /* GObject stuff - nothing to worry about */
static inline
void inspect_child_node(AstNode *node)
{
if (node->inspect) {
node->inspect(node);
node->inspect = NULL;
}
}
static inline
AstNode* ast_nth_child(AstNode *node, int n)
{
if (!node)
return NULL;
inspect_child_node(node);
if (n >= node->childnodes->len)
return NULL;
return g_array_index(node->childnodes, AstNode *, n);
}
static inline
gboolean ast_set_iter(GtkTreeIter *iter, AstNode *node)
{
iter->user_data = node;
iter->user_data2 = iter->user_data3 = NULL;
return node != NULL;
}
/*****************************************************************************
*
* ast_get_type: here we register our new type and its interfaces
* with the type system. If you want to implement
* additional interfaces like GtkTreeSortable, you
* will need to do it here.
*
*****************************************************************************/
GType
ast_get_type (void)
{
static GType ast_type = 0;
static const GTypeInfo ast_info = {
sizeof (AstNodeClass),
NULL, /* base_init */
NULL, /* base_finalize */
(GClassInitFunc) ast_class_init,
NULL, /* class finalize */
NULL, /* class_data */
sizeof (AstNode),
0, /* n_preallocs */
(GInstanceInitFunc) ast_init
};
static const GInterfaceInfo tree_model_info = {
(GInterfaceInitFunc) ast_tree_model_init,
NULL,
NULL
};
if (ast_type)
return ast_type;
/* Some boilerplate type registration stuff */
ast_type = g_type_register_static(G_TYPE_OBJECT, "AstNode",
&ast_info, (GTypeFlags)0);
/* Here we register our GtkTreeModel interface with the type system */
g_type_add_interface_static(ast_type, GTK_TYPE_TREE_MODEL, &tree_model_info);
return ast_type;
}
/*****************************************************************************
*
* ast_class_init: more boilerplate GObject/GType stuff.
* Init callback for the type system,
* called once when our new class is created.
*
*****************************************************************************/
static void
ast_class_init (AstNodeClass *klass)
{
GObjectClass *object_class;
parent_class = (GObjectClass*) g_type_class_peek_parent (klass);
object_class = (GObjectClass*) klass;
object_class->finalize = ast_finalize;
}
/*****************************************************************************
*
* ast_tree_model_init: init callback for the interface registration
* in ast_get_type. Here we override
* the GtkTreeModel interface functions that
* we implement.
*
*****************************************************************************/
static void
ast_tree_model_init (GtkTreeModelIface *iface)
{
iface->get_flags = ast_get_flags;
iface->get_n_columns = ast_get_n_columns;
iface->get_column_type = ast_get_column_type;
iface->get_iter = ast_get_iter;
iface->get_path = ast_get_path;
iface->get_value = ast_get_value;
iface->iter_next = ast_iter_next;
iface->iter_children = ast_iter_children;
iface->iter_has_child = ast_iter_has_child;
iface->iter_n_children = ast_iter_n_children;
iface->iter_nth_child = ast_iter_nth_child;
iface->iter_parent = ast_iter_parent;
}
/*****************************************************************************
*
* ast_init: this is called every time a new ast node object
* instance is created (we do that in ast_new).
* Initialise the list structure's fields here.
*
*****************************************************************************/
static void
ast_init (AstNode *node)
{
node->childnodes = g_array_new(FALSE, TRUE, sizeof(AstNode *));
node->stamp = g_random_int(); /* Random int to check whether iters belong to out model */
}
/*****************************************************************************
*
* ast_finalize: this is called just before an ast node is
* destroyed. Free dynamically allocated memory here.
*
*****************************************************************************/
static void
ast_finalize (GObject *object)
{
/* AstNode *node = AST_NODE(object); */
/* FIXME: free all node memory */
/* must chain up - finalize parent */
(* parent_class->finalize) (object);
}
/*****************************************************************************
*
* ast_get_flags: tells the rest of the world whether our tree model
* has any special characteristics. In our case,
* we have a list model (instead of a tree), and each
* tree iter is valid as long as the row in question
* exists, as it only contains a pointer to our struct.
*
*****************************************************************************/
static GtkTreeModelFlags
ast_get_flags(GtkTreeModel *tree_model)
{
return (GTK_TREE_MODEL_ITERS_PERSIST);
}
/*****************************************************************************
*
* ast_get_n_columns: tells the rest of the world how many data
* columns we export via the tree model interface
*
*****************************************************************************/
static gint
ast_get_n_columns(GtkTreeModel *tree_model)
{
return 1;
}
/*****************************************************************************
*
* ast_get_column_type: tells the rest of the world which type of
* data an exported model column contains
*
*****************************************************************************/
static GType
ast_get_column_type(GtkTreeModel *tree_model,
gint index)
{
return G_TYPE_STRING;
}
/*****************************************************************************
*
* ast_get_iter: converts a tree path (physical position) into a
* tree iter structure (the content of the iter
* fields will only be used internally by our model).
* We simply store a pointer to our AstNodeItem
* structure that represents that row in the tree iter.
*
*****************************************************************************/
static gboolean
ast_get_iter(GtkTreeModel *tree_model,
GtkTreeIter *iter,
GtkTreePath *path)
{
AstNode *node;
gint *indices, depth;
int i;
node = AST_NODE(tree_model);
indices = gtk_tree_path_get_indices(path);
depth = gtk_tree_path_get_depth(path);
for (i = 0; i < depth; i++)
node = ast_nth_child(node, indices[i]);
return ast_set_iter(iter, node);
}
/*****************************************************************************
*
* ast_get_path: converts a tree iter into a tree path (ie. the
* physical position of that row in the list).
*
*****************************************************************************/
static GtkTreePath *
ast_get_path(GtkTreeModel *tree_model,
GtkTreeIter *iter)
{
GtkTreePath *path;
AstNode *root = AST_NODE(tree_model);
AstNode *node = AST_NODE(iter->user_data);
path = gtk_tree_path_new();
while (node != root) {
gtk_tree_path_prepend_index(path, node->index);
node = node->parent;
}
return path;
}
/*****************************************************************************
*
* ast_get_value: Returns a row's exported data columns
* (_get_value is what gtk_tree_model_get uses)
*
*****************************************************************************/
static void
ast_get_value(GtkTreeModel *tree_model,
GtkTreeIter *iter,
gint column,
GValue *value)
{
AstNode *node = iter->user_data;
g_assert(AST_IS_NODE(tree_model));
if (column != 1)
return;
inspect_child_node(node);
g_value_init(value, G_TYPE_STRING);
g_value_set_string(value, node->text);
return;
}
/*****************************************************************************
*
* ast_iter_next: Takes an iter structure and sets it to point
* to the next row.
*
*****************************************************************************/
static gboolean
ast_iter_next(GtkTreeModel *tree_model,
GtkTreeIter *iter)
{
AstNode *node = iter->user_data;
g_assert(AST_IS_NODE (tree_model));
node = ast_nth_child(node->parent, node->index + 1);
return ast_set_iter(iter, node);
}
/*****************************************************************************
*
* ast_iter_children: Returns TRUE or FALSE depending on whether
* the row specified by 'parent' has any children.
* If it has children, then 'iter' is set to
* point to the first child. Special case: if
* 'parent' is NULL, then the first top-level
* row should be returned if it exists.
*
*****************************************************************************/
static gboolean
ast_iter_children(GtkTreeModel *tree_model,
GtkTreeIter *iter,
GtkTreeIter *parent)
{
return ast_iter_nth_child(tree_model, iter, parent, 0);
}
/*****************************************************************************
*
* ast_iter_has_child: Returns TRUE or FALSE depending on whether
* the row specified by 'iter' has any children.
* We only have a list and thus no children.
*
*****************************************************************************/
static gboolean
ast_iter_has_child (GtkTreeModel *tree_model,
GtkTreeIter *iter)
{
AstNode *node = iter->user_data;
inspect_child_node(node);
return node->childnodes->len > 0;
}
/*****************************************************************************
*
* ast_iter_n_children: Returns the number of children the row
* specified by 'iter' has. This is usually 0,
* as we only have a list and thus do not have
* any children to any rows. A special case is
* when 'iter' is NULL, in which case we need
* to return the number of top-level node,
* ie. the number of rows in our list.
*
*****************************************************************************/
static gint
ast_iter_n_children (GtkTreeModel *tree_model,
GtkTreeIter *iter)
{
AstNode *node = iter ? iter->user_data
: AST_NODE(tree_model);
inspect_child_node(node);
return node->childnodes->len;
}
/*****************************************************************************
*
* ast_iter_nth_child: If the row specified by 'parent' has any
* children, set 'iter' to the n-th child and
* return TRUE if it exists, otherwise FALSE.
* A special case is when 'parent' is NULL, in
* which case we need to set 'iter' to the n-th
* row if it exists.
*
*****************************************************************************/
static gboolean
ast_iter_nth_child(GtkTreeModel *tree_model,
GtkTreeIter *iter,
GtkTreeIter *parent,
gint n)
{
AstNode *node = parent ? parent->user_data : (AstNode*) tree_model;
GArray *array = node->childnodes;
if (n >= array->len)
return FALSE;
iter->user_data = g_array_index(array, AstNode *, n);
return TRUE;
}
/*****************************************************************************
*
* ast_iter_parent: Point 'iter' to the parent node of 'child'. As
* we have a list and thus no children and no
* parents of children, we can just return FALSE.
*
*****************************************************************************/
static gboolean
ast_iter_parent (GtkTreeModel *tree_model,
GtkTreeIter *iter,
GtkTreeIter *child)
{
AstNode *node = (AstNode *) child->user_data;
iter->user_data = node->parent;
return node->parent != NULL;
}
AstNode *
ast_new (AstNode *parent, int index, const char *text, void *ptr, void (*inspect)(AstNode*))
{
AstNode *node = (AstNode*) g_object_new (AST_TYPE_NODE, NULL);
g_assert(node != NULL);
node->parent = parent;
node->index = index;
node->text = text;
node->inspect = inspect;
node->ptr = ptr;
return node;
}
sparse-0.6.4/ast-model.h 0000664 0000000 0000000 00000004306 14115310122 0015045 0 ustar 00root root 0000000 0000000
/*
* ast-model.h
*
* Copyright (C) 2010 Christopher Li.
*
*/
#ifndef _ast_model_h_
#define _ast_model_h_
#include
#include
#include "lib.h"
#define AST_TYPE_NODE (ast_get_type ())
#define AST_NODE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), AST_TYPE_NODE, AstNode))
#define AST_NODE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), AST_TYPE_NODE, AstNodeClass))
#define AST_IS_NODE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), AST_TYPE_NODE))
#define AST_IS_NODE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), AST_TYPE_NODE))
#define AST_NODE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), AST_TYPE_NODE, AstNodeClass))
enum
{
AST_COL_RECORD = 0,
AST_COL_NAME,
AST_N_COLUMNS,
} ;
typedef struct AstNode AstNode;
typedef struct AstNodeClass AstNodeClass;
/* AstNode: this structure contains everything we need for our
* model implementation. You can add extra fields to
* this structure, e.g. hashtables to quickly lookup
* rows or whatever else you might need, but it is
* crucial that 'parent' is the first member of the
* structure. */
struct AstNode
{
GObject base; /* this MUST be the first member */
AstNode *parent;
int index;
const gchar *text;
void (*inspect)(struct AstNode* node);
void *ptr;
GArray *childnodes;
gint stamp;
};
/* AstNodeClass: more boilerplate GObject stuff */
struct AstNodeClass
{
GObjectClass base_class;
};
GType ast_get_type(void);
AstNode* ast_new(AstNode *parent, int index, const char *prefix, void *ptr, void (*expand)(AstNode*));
static inline
AstNode* ast_append_child(AstNode *parent, const char *text,
void *ptr, void (*inspect)(AstNode*))
{
if (ptr) {
AstNode *child = ast_new(parent, parent->childnodes->len,
text, ptr, inspect);
g_array_append_val(parent->childnodes, child);
return child;
}
return NULL;
}
static inline
void ast_append_attribute(AstNode *parent, const char *text)
{
AstNode *child = ast_new(parent, parent->childnodes->len, text, NULL, NULL);
g_array_append_val(parent->childnodes, child);
}
#endif /* _ast_h_*/
sparse-0.6.4/ast-view.c 0000664 0000000 0000000 00000002215 14115310122 0014707 0 ustar 00root root 0000000 0000000
#include
#include "ast-model.h"
#include "ast-inspect.h"
#include "ast-view.h"
static GtkWidget *
create_view_and_model (void *ptr)
{
GtkTreeViewColumn *text;
GtkCellRenderer *renderer;
AstNode *root;
GtkWidget *view;
root = ast_new(NULL, 0, "", ptr, inspect_symbol_list);
view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(root));
g_object_unref(root); /* destroy store automatically with view */
renderer = gtk_cell_renderer_text_new();
text = gtk_tree_view_column_new_with_attributes("Node", renderer,
"text", AST_COL_NAME,
NULL);
gtk_tree_view_append_column(GTK_TREE_VIEW(view), text);
return view;
}
void
treeview_main (struct symbol_list *syms)
{
GtkWidget *window, *view, *scrollwin;
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_default_size (GTK_WINDOW(window), 600, 800);
g_signal_connect(window, "delete_event", gtk_main_quit, NULL);
scrollwin = gtk_scrolled_window_new(NULL,NULL);
view = create_view_and_model(syms);
gtk_container_add(GTK_CONTAINER(scrollwin), view);
gtk_container_add(GTK_CONTAINER(window), scrollwin);
gtk_widget_show_all(window);
gtk_main();
}
sparse-0.6.4/ast-view.h 0000664 0000000 0000000 00000000137 14115310122 0014715 0 ustar 00root root 0000000 0000000
#include
#include "lib.h"
extern void treeview_main(struct symbol_list *syms);
sparse-0.6.4/bitmap.h 0000664 0000000 0000000 00000002634 14115310122 0014436 0 ustar 00root root 0000000 0000000 #ifndef BITMAP_H
#define BITMAP_H
#define BITS_IN_LONG (sizeof(unsigned long)*8)
#define LONGS(x) ((x + BITS_IN_LONG - 1) & -BITS_IN_LONG)
/* Every bitmap gets its own type */
#define DECLARE_BITMAP(name, x) unsigned long name[LONGS(x)]
static inline int test_bit(unsigned int nr, unsigned long *bitmap)
{
unsigned long offset = nr / BITS_IN_LONG;
unsigned long bit = nr & (BITS_IN_LONG-1);
return (bitmap[offset] >> bit) & 1;
}
static inline void set_bit(unsigned int nr, unsigned long *bitmap)
{
unsigned long offset = nr / BITS_IN_LONG;
unsigned long bit = nr & (BITS_IN_LONG-1);
bitmap[offset] |= 1UL << bit;
}
static inline void clear_bit(unsigned int nr, unsigned long *bitmap)
{
unsigned long offset = nr / BITS_IN_LONG;
unsigned long bit = nr & (BITS_IN_LONG-1);
bitmap[offset] &= ~(1UL << bit);
}
static inline int test_and_set_bit(unsigned int nr, unsigned long *bitmap)
{
unsigned long offset = nr / BITS_IN_LONG;
unsigned long bit = nr & (BITS_IN_LONG-1);
unsigned long old = bitmap[offset];
unsigned long mask = 1UL << bit;
bitmap[offset] = old | mask;
return (old & mask) != 0;
}
static inline int test_and_clear_bit(unsigned int nr, unsigned long *bitmap)
{
unsigned long offset = nr / BITS_IN_LONG;
unsigned long bit = nr & (BITS_IN_LONG-1);
unsigned long old = bitmap[offset];
unsigned long mask = 1UL << bit;
bitmap[offset] = old & ~mask;
return (old & mask) != 0;
}
#endif /* BITMAP_H */
sparse-0.6.4/bits.h 0000664 0000000 0000000 00000002701 14115310122 0014116 0 ustar 00root root 0000000 0000000 /* SPDX-License-Identifier: MIT */
/*
* Helper functions for manipulation & testing of integer values
* like zero or sign-extensions.
*
* Copyright (C) 2017 Luc Van Oostenryck
*
*/
#ifndef BITS_H
#define BITS_H
static inline unsigned long long sign_bit(unsigned size)
{
return 1ULL << (size - 1);
}
static inline unsigned long long sign_mask(unsigned size)
{
unsigned long long sbit = sign_bit(size);
return sbit - 1;
}
static inline unsigned long long bits_mask(unsigned size)
{
unsigned long long sbit = sign_bit(size);
return sbit | (sbit - 1);
}
static inline long long zero_extend(long long val, unsigned size)
{
return val & bits_mask(size);
}
static inline long long sign_extend(long long val, unsigned size)
{
if (val & sign_bit(size))
val |= ~sign_mask(size);
return val;
}
///
// sign extend @val but only if exactly representable
static inline long long sign_extend_safe(long long val, unsigned size)
{
unsigned long long mask = bits_mask(size);
if (!(val & ~mask))
val = sign_extend(val, size);
return val;
}
static inline long long bits_extend(long long val, unsigned size, int is_signed)
{
val = zero_extend(val, size);
if (is_signed)
val = sign_extend(val, size);
return val;
}
static inline int is_power_of_2(long long val)
{
return val && !(val & (val - 1));
}
///
// log base 2 of an exact power-of-2
static inline int log2_exact(unsigned long long val)
{
return 8 * sizeof(val) - __builtin_clzl(val) - 1;
}
#endif
sparse-0.6.4/builtin.c 0000664 0000000 0000000 00000074005 14115310122 0014624 0 ustar 00root root 0000000 0000000 /*
* builtin evaluation & expansion.
*
* Copyright (C) 2003 Transmeta Corp.
* 2003-2004 Linus Torvalds
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "builtin.h"
#include "expression.h"
#include "evaluate.h"
#include "expand.h"
#include "symbol.h"
#include "compat/bswap.h"
#include
#define dyntype incomplete_ctype
static bool is_dynamic_type(struct symbol *t)
{
if (t->type == SYM_NODE)
t = t->ctype.base_type;
return t == &dyntype;
}
static int evaluate_to_int_const_expr(struct expression *expr)
{
expr->ctype = &int_ctype;
expr->flags |= CEF_SET_ICE;
return 1;
}
static int evaluate_pure_unop(struct expression *expr)
{
struct expression *arg = first_expression(expr->args);
int flags = arg->flags;
/*
* Allow such functions with a constant integer expression
* argument to be treated as a *constant* integer.
* This allow us to use them in switch() { case ...:
*/
flags |= (flags & CEF_ICE) ? CEF_SET_INT : 0;
expr->flags = flags;
return 1;
}
/*
* eval_args - check the number of arguments and evaluate them.
*/
static int eval_args(struct expression *expr, int n)
{
struct expression *arg;
struct symbol *sym;
const char *msg;
int rc = 1;
FOR_EACH_PTR(expr->args, arg) {
if (n-- == 0) {
msg = "too many arguments";
goto error;
}
if (!evaluate_expression(arg))
rc = 0;
} END_FOR_EACH_PTR(arg);
if (n > 0) {
msg = "not enough arguments";
goto error;
}
return rc;
error:
sym = expr->fn->ctype;
expression_error(expr, "%s for %s", msg, show_ident(sym->ident));
return 0;
}
static int args_prototype(struct expression *expr)
{
struct symbol *fntype = expr->fn->ctype->ctype.base_type;
int n = symbol_list_size(fntype->arguments);
return eval_args(expr, n);
}
static int args_triadic(struct expression *expr)
{
return eval_args(expr, 3);
}
static int evaluate_choose(struct expression *expr)
{
struct expression_list *list = expr->args;
struct expression *arg, *args[3];
int n = 0;
/* there will be exactly 3; we'd already verified that */
FOR_EACH_PTR(list, arg) {
args[n++] = arg;
} END_FOR_EACH_PTR(arg);
*expr = get_expression_value(args[0]) ? *args[1] : *args[2];
return 1;
}
static int expand_expect(struct expression *expr, int cost)
{
struct expression *arg = first_ptr_list((struct ptr_list *) expr->args);
if (arg)
*expr = *arg;
return 0;
}
/*
* __builtin_warning() has type "int" and always returns 1,
* so that you can use it in conditionals or whatever
*/
static int expand_warning(struct expression *expr, int cost)
{
struct expression *arg;
struct expression_list *arglist = expr->args;
FOR_EACH_PTR (arglist, arg) {
/*
* Constant strings get printed out as a warning. By the
* time we get here, the EXPR_STRING has been fully
* evaluated, so by now it's an anonymous symbol with a
* string initializer.
*
* Just for the heck of it, allow any constant string
* symbol.
*/
if (arg->type == EXPR_SYMBOL) {
struct symbol *sym = arg->symbol;
if (sym->initializer && sym->initializer->type == EXPR_STRING) {
struct string *string = sym->initializer->string;
warning(expr->pos, "%*s", string->length-1, string->data);
}
continue;
}
/*
* Any other argument is a conditional. If it's
* non-constant, or it is false, we exit and do
* not print any warning.
*/
if (arg->type != EXPR_VALUE)
goto out;
if (!arg->value)
goto out;
} END_FOR_EACH_PTR(arg);
out:
expr->type = EXPR_VALUE;
expr->value = 1;
expr->taint = 0;
return 0;
}
/* The arguments are constant if the cost of all of them is zero */
static int expand_constant_p(struct expression *expr, int cost)
{
expr->type = EXPR_VALUE;
expr->value = !cost;
expr->taint = 0;
return 0;
}
/* The arguments are safe, if their cost is less than SIDE_EFFECTS */
static int expand_safe_p(struct expression *expr, int cost)
{
expr->type = EXPR_VALUE;
expr->value = (cost < SIDE_EFFECTS);
expr->taint = 0;
return 0;
}
static struct symbol_op constant_p_op = {
.evaluate = evaluate_to_int_const_expr,
.expand = expand_constant_p
};
static struct symbol_op safe_p_op = {
.evaluate = evaluate_to_int_const_expr,
.expand = expand_safe_p
};
static struct symbol_op warning_op = {
.evaluate = evaluate_to_int_const_expr,
.expand = expand_warning
};
static struct symbol_op expect_op = {
.expand = expand_expect
};
static struct symbol_op choose_op = {
.args = args_triadic,
.evaluate = evaluate_choose,
};
/* The argument is constant and valid if the cost is zero */
static int expand_bswap(struct expression *expr, int cost)
{
struct expression *arg;
long long val;
if (cost)
return cost;
/* the arguments number & type have already been checked */
arg = first_expression(expr->args);
val = get_expression_value_silent(arg);
switch (expr->ctype->bit_size) {
case 16: expr->value = bswap16(val); break;
case 32: expr->value = bswap32(val); break;
case 64: expr->value = bswap64(val); break;
default: /* impossible error */
return SIDE_EFFECTS;
}
expr->type = EXPR_VALUE;
expr->taint = 0;
return 0;
}
static struct symbol_op bswap_op = {
.evaluate = evaluate_pure_unop,
.expand = expand_bswap,
};
#define EXPAND_FINDBIT(name) \
static int expand_##name(struct expression *expr, int cost) \
{ \
struct expression *arg; \
long long val; \
\
if (cost) \
return cost; \
\
arg = first_expression(expr->args); \
val = get_expression_value_silent(arg); \
switch (arg->ctype->bit_size) { \
case sizeof(int) * 8: \
val = __builtin_##name(val); break; \
case sizeof(long long) * 8: \
val = __builtin_##name##ll(val); break; \
default: /* impossible error */ \
return SIDE_EFFECTS; \
} \
\
expr->value = val; \
expr->type = EXPR_VALUE; \
expr->taint = 0; \
return 0; \
} \
\
static struct symbol_op name##_op = { \
.evaluate = evaluate_pure_unop, \
.expand = expand_##name, \
}
EXPAND_FINDBIT(clz);
EXPAND_FINDBIT(ctz);
EXPAND_FINDBIT(clrsb);
EXPAND_FINDBIT(ffs);
EXPAND_FINDBIT(parity);
EXPAND_FINDBIT(popcount);
static int evaluate_fp_unop(struct expression *expr)
{
struct expression *arg;
if (!eval_args(expr, 1))
return 0;
arg = first_expression(expr->args);
if (!is_float_type(arg->ctype)) {
expression_error(expr, "non-floating-point argument in call to %s()",
show_ident(expr->fn->ctype->ident));
return 0;
}
return 1;
}
static struct symbol_op fp_unop_op = {
.evaluate = evaluate_fp_unop,
};
static int expand_isdigit(struct expression *expr, int cost)
{
struct expression *arg = first_expression(expr->args);
long long val = get_expression_value_silent(arg);
if (cost)
return cost;
expr->value = (val >= '0') && (val <= '9');
expr->type = EXPR_VALUE;
expr->taint = 0;
return 0;
}
static struct symbol_op isdigit_op = {
.evaluate = evaluate_pure_unop,
.expand = expand_isdigit,
};
static int evaluate_overflow_gen(struct expression *expr, int ptr)
{
struct expression *arg;
int n = 0;
/* there will be exactly 3; we'd already verified that */
FOR_EACH_PTR(expr->args, arg) {
struct symbol *type;
n++;
if (!arg || !(type = arg->ctype))
return 0;
// 1st & 2nd args must be a basic integer type
// 3rd arg must be a pointer to such a type.
if (n == 3 && ptr) {
if (type->type == SYM_NODE)
type = type->ctype.base_type;
if (!type)
return 0;
if (type->type != SYM_PTR)
goto err;
type = type->ctype.base_type;
if (!type)
return 0;
}
if (type->type == SYM_NODE)
type = type->ctype.base_type;
if (!type)
return 0;
if (type->ctype.base_type != &int_type || type == &bool_ctype)
goto err;
} END_FOR_EACH_PTR(arg);
// the builtin returns a bool
expr->ctype = &bool_ctype;
return 1;
err:
sparse_error(arg->pos, "invalid type for argument %d:", n);
info(arg->pos, " %s", show_typename(arg->ctype));
expr->ctype = &bad_ctype;
return 0;
}
static int evaluate_overflow(struct expression *expr)
{
return evaluate_overflow_gen(expr, 1);
}
static struct symbol_op overflow_op = {
.args = args_triadic,
.evaluate = evaluate_overflow,
};
static int evaluate_overflow_p(struct expression *expr)
{
return evaluate_overflow_gen(expr, 0);
}
static struct symbol_op overflow_p_op = {
.args = args_triadic,
.evaluate = evaluate_overflow_p,
};
///
// Evaluate the arguments of 'generic' integer operators.
//
// Parameters with a complete type are used like in a normal prototype.
// The first parameter with a 'dynamic' type will be consider
// as polymorphic and for each calls will be instancied with the type
// of its effective argument.
// The next dynamic parameters will the use this polymorphic type.
// This allows to declare functions with some parameters having
// a type variably defined at call time:
// int foo(int, T, T);
static int evaluate_generic_int_op(struct expression *expr)
{
struct symbol *fntype = expr->fn->ctype->ctype.base_type;
struct symbol_list *types = NULL;
struct symbol *ctype = NULL;
struct expression *arg;
struct symbol *t;
int n = 0;
PREPARE_PTR_LIST(fntype->arguments, t);
FOR_EACH_PTR(expr->args, arg) {
n++;
if (!is_dynamic_type(t)) {
;
} else if (!ctype) {
// first 'dynamic' type, check that it's an integer
t = arg->ctype;
if (!t)
return 0;
if (t->type == SYM_NODE)
t = t->ctype.base_type;
if (!t)
return 0;
if (t->ctype.base_type != &int_type)
goto err;
// next 'dynamic' arguments will use this type
ctype = t;
} else {
// use the previous 'dynamic' type
t = ctype;
}
add_ptr_list(&types, t);
NEXT_PTR_LIST(t);
} END_FOR_EACH_PTR(arg);
FINISH_PTR_LIST(t);
return evaluate_arguments(types, expr->args);
err:
sparse_error(arg->pos, "non-integer type for argument %d:", n);
info(arg->pos, " %s", show_typename(arg->ctype));
expr->ctype = &bad_ctype;
return 0;
}
struct symbol_op generic_int_op = {
.args = args_prototype,
.evaluate = evaluate_generic_int_op,
};
static int eval_atomic_common(struct expression *expr)
{
struct symbol *fntype = expr->fn->ctype->ctype.base_type;
struct symbol_list *types = NULL;
struct symbol *ctype = NULL;
struct symbol *t;
struct expression *arg;
int n = 0;
// The number of arguments has already be verified.
// The first arg must be a pointer to an integral type.
PREPARE_PTR_LIST(fntype->arguments, t);
FOR_EACH_PTR(expr->args, arg) {
struct symbol *ptrtype = NULL;
if (++n == 1) {
t = arg->ctype;
if (!t)
return 0;
if (t->type == SYM_NODE)
t = t->ctype.base_type;
if (!t)
return 0;
if (t->type != SYM_PTR)
goto err;
ptrtype = t;
t = t->ctype.base_type;
if (!t)
return 0;
if (t->type == SYM_NODE)
t = t->ctype.base_type;
if (!t)
return 0;
if (t->type != SYM_PTR && t->ctype.base_type != &int_type)
goto err;
ctype = t;
t = ptrtype;
} else if (is_dynamic_type(t)) {
t = ctype;
} else if (t == &ptr_ctype) {
t = ptrtype;
}
add_ptr_list(&types, t);
NEXT_PTR_LIST(t);
} END_FOR_EACH_PTR(arg);
FINISH_PTR_LIST(t);
if (!expr->ctype) // set the return type, if needed
expr->ctype = ctype;
return evaluate_arguments(types, expr->args);
err:
sparse_error(arg->pos, "invalid type for argument %d:", n);
info(arg->pos, " %s", show_typename(arg->ctype));
expr->ctype = &bad_ctype;
return 0;
}
static struct symbol_op atomic_op = {
.args = args_prototype,
.evaluate = eval_atomic_common,
};
///
// expand __builtin_object_size()
//
// :note: type 1 and type 3 are not supported because the
// needed information isn't available after evaluation.
static int expand_object_size(struct expression *expr, int cost)
{
struct expression *arg = first_expression(expr->args);
int type = get_expression_value_silent(ptr_list_nth(expr->args, 1));
unsigned long val = -1, off = 0;
while (arg) {
switch (arg->type) {
case EXPR_IMPLIED_CAST:
case EXPR_CAST:
// ignore those
arg = arg->cast_expression;
continue;
case EXPR_BINOP:
// a constant add is (maybe) an offset
if (!arg->right || arg->op != '+' || arg->right->type != EXPR_VALUE)
break;
off += arg->right->value;
arg = arg->left;
continue;
case EXPR_PREOP:
// a deref is just intermediate variable
// and so the offset needs to be zeroed.
if (arg->op == '*') {
arg = arg->unop;
off = 0;
switch (arg->type) {
case EXPR_SYMBOL:
arg = arg->symbol->initializer;
continue;
default:
break;
}
}
break;
case EXPR_SYMBOL:
// the symbol we're looking after
val = bits_to_bytes(arg->symbol->bit_size);
break;
case EXPR_CALL:
// use alloc_size() attribute but only after linearization.
return UNSAFE;
default:
break;
}
break;
}
if (val == -1)
val = (type & 2) ? 0 : val;
else if (type & 1)
return UNSAFE;
else
val -= off;
expr->flags |= CEF_SET_ICE;
expr->type = EXPR_VALUE;
expr->value = val;
expr->taint = 0;
return 0;
}
static struct symbol_op object_size_op = {
.expand = expand_object_size,
};
/*
* Builtin functions
*/
static struct symbol size_t_alias;
static struct symbol *get_ctype(struct symbol *sym)
{
if (sym == &size_t_alias)
return size_t_ctype;
return sym;
}
static void declare_builtin(int stream, const struct builtin_fn *entry)
{
struct symbol *sym = create_symbol(stream, entry->name, SYM_NODE, NS_SYMBOL);
struct symbol *fun = alloc_symbol(sym->pos, SYM_FN);
struct symbol *arg;
int i;
sym->ctype.base_type = fun;
sym->ctype.modifiers = MOD_TOPLEVEL;
sym->builtin = 1;
sym->op = entry->op;
fun->ctype.base_type = get_ctype(entry->ret_type);
fun->variadic = entry->variadic;
for (i = 0; (arg = entry->args[i]); i++) {
struct symbol *anode = alloc_symbol(sym->pos, SYM_NODE);
anode->ctype.base_type = get_ctype(arg);
add_symbol(&fun->arguments, anode);
}
}
void declare_builtins(int stream, const struct builtin_fn tbl[])
{
if (!tbl)
return;
while (tbl->name)
declare_builtin(stream, tbl++);
}
static const struct builtin_fn builtins_common[] = {
#define size_t_ctype &size_t_alias
#define va_list_ctype &ptr_ctype
#define vol_ptr &volatile_ptr_ctype
{ "__atomic_add_fetch", NULL, 0, { vol_ptr, &dyntype, &int_ctype }, .op = &atomic_op },
{ "__atomic_always_lock_free", &bool_ctype, 0, { size_t_ctype, vol_ptr }},
{ "__atomic_and_fetch", NULL, 0, { vol_ptr, &dyntype, &int_ctype }, .op = &atomic_op },
{ "__atomic_clear", &void_ctype, 0, { &volatile_bool_ptr_ctype, &int_ctype }},
{ "__atomic_compare_exchange", &bool_ctype, 0, { vol_ptr, &ptr_ctype, &ptr_ctype, &bool_ctype, &int_ctype, &int_ctype }, .op = &atomic_op },
{ "__atomic_compare_exchange_n", &bool_ctype, 0, { vol_ptr, &ptr_ctype, &dyntype, &bool_ctype, &int_ctype, &int_ctype }, .op = &atomic_op },
{ "__atomic_exchange", &void_ctype, 0, { vol_ptr, &ptr_ctype, &ptr_ctype, &int_ctype }, .op = &atomic_op },
{ "__atomic_exchange_n", NULL, 0, { vol_ptr, &dyntype, &int_ctype }, .op = &atomic_op },
{ "__atomic_fetch_add", NULL, 0, { vol_ptr, &dyntype, &int_ctype }, .op = &atomic_op },
{ "__atomic_fetch_and", NULL, 0, { vol_ptr, &dyntype, &int_ctype }, .op = &atomic_op },
{ "__atomic_fetch_nand",NULL, 0, { vol_ptr, &dyntype, &int_ctype }, .op = &atomic_op },
{ "__atomic_fetch_or", NULL, 0, { vol_ptr, &dyntype, &int_ctype }, .op = &atomic_op },
{ "__atomic_fetch_sub", NULL, 0, { vol_ptr, &dyntype, &int_ctype }, .op = &atomic_op },
{ "__atomic_fetch_xor", NULL, 0, { vol_ptr, &dyntype, &int_ctype }, .op = &atomic_op },
{ "__atomic_is_lock_free", &bool_ctype, 0, { size_t_ctype, vol_ptr }},
{ "__atomic_load", &void_ctype, 0, { vol_ptr, &ptr_ctype, &int_ctype }, .op = &atomic_op },
{ "__atomic_load_n", NULL, 0, { vol_ptr, &int_ctype }, .op = &atomic_op },
{ "__atomic_nand_fetch",NULL, 0, { vol_ptr, &dyntype, &int_ctype }, .op = &atomic_op },
{ "__atomic_or_fetch", NULL, 0, { vol_ptr, &dyntype, &int_ctype }, .op = &atomic_op },
{ "__atomic_signal_fence", &void_ctype, 0, { &int_ctype }},
{ "__atomic_store", &void_ctype, 0, { vol_ptr, &ptr_ctype, &int_ctype }, .op = &atomic_op },
{ "__atomic_store_n", &void_ctype, 0, { vol_ptr, &dyntype, &int_ctype }, .op = &atomic_op },
{ "__atomic_sub_fetch", NULL, 0, { vol_ptr, &dyntype, &int_ctype }, .op = &atomic_op },
{ "__atomic_test_and_set", &bool_ctype, 0, { vol_ptr, &int_ctype }},
{ "__atomic_thread_fence", &void_ctype, 0, { &int_ctype }},
{ "__atomic_xor_fetch", NULL, 0, { vol_ptr, &dyntype, &int_ctype }, .op = &atomic_op },
{ "__builtin_choose_expr", NULL, 1, .op = &choose_op },
{ "__builtin_constant_p", NULL, 1, .op = &constant_p_op },
{ "__builtin_expect", &long_ctype, 0, { &long_ctype ,&long_ctype }, .op = &expect_op },
{ "__builtin_safe_p", NULL, 1, .op = &safe_p_op },
{ "__builtin_warning", NULL, 1, .op = &warning_op },
{ "__builtin_abort", &void_ctype, 0 },
{ "__builtin_abs", &int_ctype , 0, { &int_ctype }},
{ "__builtin_add_overflow", &bool_ctype, 1, .op = &overflow_op },
{ "__builtin_add_overflow_p", &bool_ctype, 1, .op = &overflow_p_op },
{ "__builtin_alloca", &ptr_ctype, 0, { size_t_ctype }},
{ "__builtin_bcmp", &int_ctype , 0, { &const_ptr_ctype, &const_ptr_ctype, size_t_ctype }},
{ "__builtin_bcopy", &void_ctype, 0, { &const_ptr_ctype, &ptr_ctype, size_t_ctype }},
{ "__builtin_bswap16", &ushort_ctype, 0, { &ushort_ctype }, .op = &bswap_op },
{ "__builtin_bswap32", &uint_ctype, 0, { &uint_ctype }, .op = &bswap_op },
{ "__builtin_bswap64", &ullong_ctype, 0, { &ullong_ctype }, .op = &bswap_op },
{ "__builtin_bzero", &void_ctype, 0, { &ptr_ctype, size_t_ctype }},
{ "__builtin_calloc", &ptr_ctype, 0, { size_t_ctype, size_t_ctype }},
{ "__builtin_clrsb", &int_ctype, 0, { &int_ctype }, .op = &clrsb_op },
{ "__builtin_clrsbl", &int_ctype, 0, { &long_ctype }, .op = &clrsb_op },
{ "__builtin_clrsbll", &int_ctype, 0, { &llong_ctype }, .op = &clrsb_op },
{ "__builtin_clz", &int_ctype, 0, { &int_ctype }, .op = &clz_op },
{ "__builtin_clzl", &int_ctype, 0, { &long_ctype }, .op = &clz_op },
{ "__builtin_clzll", &int_ctype, 0, { &llong_ctype }, .op = &clz_op },
{ "__builtin_ctz", &int_ctype, 0, { &int_ctype }, .op = &ctz_op },
{ "__builtin_ctzl", &int_ctype, 0, { &long_ctype }, .op = &ctz_op },
{ "__builtin_ctzll", &int_ctype, 0, { &llong_ctype }, .op = &ctz_op },
{ "__builtin_exit", &void_ctype, 0, { &int_ctype }},
{ "__builtin_extract_return_addr", &ptr_ctype, 0, { &ptr_ctype }},
{ "__builtin_fabs", &double_ctype, 0, { &double_ctype }},
{ "__builtin_ffs", &int_ctype, 0, { &int_ctype }, .op = &ffs_op },
{ "__builtin_ffsl", &int_ctype, 0, { &long_ctype }, .op = &ffs_op },
{ "__builtin_ffsll", &int_ctype, 0, { &llong_ctype }, .op = &ffs_op },
{ "__builtin_fma", &double_ctype, 0, { &double_ctype, &double_ctype, &double_ctype }},
{ "__builtin_fmaf", &float_ctype, 0, { &float_ctype, &float_ctype, &float_ctype }},
{ "__builtin_fmal", &ldouble_ctype, 0, { &ldouble_ctype, &ldouble_ctype, &ldouble_ctype }},
{ "__builtin_frame_address", &ptr_ctype, 0, { &uint_ctype }},
{ "__builtin_free", &void_ctype, 0, { &ptr_ctype }},
{ "__builtin_huge_val", &double_ctype, 0 },
{ "__builtin_huge_valf", &float_ctype, 0 },
{ "__builtin_huge_vall", &ldouble_ctype, 0 },
{ "__builtin_index", &string_ctype, 0, { &const_string_ctype, &int_ctype }},
{ "__builtin_inf", &double_ctype, 0 },
{ "__builtin_inff", &float_ctype, 0 },
{ "__builtin_infl", &ldouble_ctype, 0 },
{ "__builtin_isdigit", &int_ctype, 0, { &int_ctype }, .op = &isdigit_op },
{ "__builtin_isfinite", &int_ctype, 1, .op = &fp_unop_op },
{ "__builtin_isgreater", &int_ctype, 0, { &float_ctype, &float_ctype }},
{ "__builtin_isgreaterequal", &int_ctype, 0, { &float_ctype, &float_ctype }},
{ "__builtin_isinf", &int_ctype, 1, .op = &fp_unop_op },
{ "__builtin_isinf_sign", &int_ctype, 1, .op = &fp_unop_op },
{ "__builtin_isless", &int_ctype, 0, { &float_ctype, &float_ctype }},
{ "__builtin_islessequal", &int_ctype, 0, { &float_ctype, &float_ctype }},
{ "__builtin_islessgreater", &int_ctype, 0, { &float_ctype, &float_ctype }},
{ "__builtin_isnan", &int_ctype, 1, .op = &fp_unop_op },
{ "__builtin_isnormal", &int_ctype, 1, .op = &fp_unop_op },
{ "__builtin_isunordered", &int_ctype, 0, { &float_ctype, &float_ctype }},
{ "__builtin_labs", &long_ctype, 0, { &long_ctype }},
{ "__builtin_llabs", &llong_ctype, 0, { &llong_ctype }},
{ "__builtin_malloc", &ptr_ctype, 0, { size_t_ctype }},
{ "__builtin_memchr", &ptr_ctype, 0, { &const_ptr_ctype, &int_ctype, size_t_ctype }},
{ "__builtin_memcmp", &int_ctype, 0, { &const_ptr_ctype, &const_ptr_ctype, size_t_ctype }},
{ "__builtin_memcpy", &ptr_ctype, 0, { &ptr_ctype, &const_ptr_ctype, size_t_ctype }},
{ "__builtin_memmove", &ptr_ctype, 0, { &ptr_ctype, &const_ptr_ctype, size_t_ctype }},
{ "__builtin_mempcpy", &ptr_ctype, 0, { &ptr_ctype, &const_ptr_ctype, size_t_ctype }},
{ "__builtin_memset", &ptr_ctype, 0, { &ptr_ctype, &int_ctype, size_t_ctype }},
{ "__builtin_mul_overflow", &bool_ctype, 1, .op = &overflow_op },
{ "__builtin_mul_overflow_p", &bool_ctype, 1, .op = &overflow_p_op },
{ "__builtin_nan", &double_ctype, 0, { &const_string_ctype }},
{ "__builtin_nanf", &float_ctype, 0, { &const_string_ctype }},
{ "__builtin_nanl", &ldouble_ctype, 0, { &const_string_ctype }},
{ "__builtin_object_size", size_t_ctype, 0, { &const_ptr_ctype, &int_ctype }, .op = &object_size_op},
{ "__builtin_parity", &int_ctype, 0, { &uint_ctype }, .op = &parity_op },
{ "__builtin_parityl", &int_ctype, 0, { &ulong_ctype }, .op = &parity_op },
{ "__builtin_parityll", &int_ctype, 0, { &ullong_ctype }, .op = &parity_op },
{ "__builtin_popcount", &int_ctype, 0, { &uint_ctype }, .op = &popcount_op },
{ "__builtin_popcountl", &int_ctype, 0, { &ulong_ctype }, .op = &popcount_op },
{ "__builtin_popcountll", &int_ctype, 0, { &ullong_ctype }, .op = &popcount_op },
{ "__builtin_prefetch", &void_ctype, 1, { &const_ptr_ctype }},
{ "__builtin_printf", &int_ctype, 1, { &const_string_ctype }},
{ "__builtin_puts", &int_ctype, 0, { &const_string_ctype }},
{ "__builtin_realloc", &ptr_ctype, 0, { &ptr_ctype, size_t_ctype }},
{ "__builtin_return_address", &ptr_ctype, 0, { &uint_ctype }},
{ "__builtin_rindex", &string_ctype, 0, { &const_string_ctype, &int_ctype }},
{ "__builtin_sadd_overflow", &bool_ctype, 0, { &int_ctype, &int_ctype, &int_ptr_ctype }},
{ "__builtin_saddl_overflow", &bool_ctype, 0, { &long_ctype, &long_ctype, &long_ptr_ctype }},
{ "__builtin_saddll_overflow", &bool_ctype, 0, { &llong_ctype, &llong_ctype, &llong_ptr_ctype }},
{ "__builtin_signbit", &int_ctype, 1 , .op = &fp_unop_op },
{ "__builtin_smul_overflow", &bool_ctype, 0, { &int_ctype, &int_ctype, &int_ptr_ctype }},
{ "__builtin_smull_overflow", &bool_ctype, 0, { &long_ctype, &long_ctype, &long_ptr_ctype }},
{ "__builtin_smulll_overflow", &bool_ctype, 0, { &llong_ctype, &llong_ctype, &llong_ptr_ctype }},
{ "__builtin_snprintf", &int_ctype, 1, { &string_ctype, size_t_ctype, &const_string_ctype }},
{ "__builtin_sprintf", &int_ctype, 1, { &string_ctype, &const_string_ctype }},
{ "__builtin_ssub_overflow", &bool_ctype, 0, { &int_ctype, &int_ctype, &int_ptr_ctype }},
{ "__builtin_ssubl_overflow", &bool_ctype, 0, { &long_ctype, &long_ctype, &long_ptr_ctype }},
{ "__builtin_ssubll_overflow", &bool_ctype, 0, { &llong_ctype, &llong_ctype, &llong_ptr_ctype }},
{ "__builtin_stpcpy", &string_ctype, 0, { &const_string_ctype, &const_string_ctype }},
{ "__builtin_stpncpy", &string_ctype, 0, { &const_string_ctype, &const_string_ctype, size_t_ctype }},
{ "__builtin_strcasecmp", &int_ctype, 0, { &const_string_ctype, &const_string_ctype }},
{ "__builtin_strcasestr", &string_ctype, 0, { &const_string_ctype, &const_string_ctype }},
{ "__builtin_strcat", &string_ctype, 0, { &string_ctype, &const_string_ctype }},
{ "__builtin_strchr", &string_ctype, 0, { &const_string_ctype, &int_ctype }},
{ "__builtin_strcmp", &int_ctype, 0, { &const_string_ctype, &const_string_ctype }},
{ "__builtin_strcpy", &string_ctype, 0, { &string_ctype, &const_string_ctype }},
{ "__builtin_strcspn", size_t_ctype, 0, { &const_string_ctype, &const_string_ctype }},
{ "__builtin_strdup", &string_ctype, 0, { &const_string_ctype }},
{ "__builtin_strlen", size_t_ctype, 0, { &const_string_ctype }},
{ "__builtin_strncasecmp", &int_ctype, 0, { &const_string_ctype, &const_string_ctype, size_t_ctype }},
{ "__builtin_strncat", &string_ctype, 0, { &string_ctype, &const_string_ctype, size_t_ctype }},
{ "__builtin_strncmp", &int_ctype, 0, { &const_string_ctype, &const_string_ctype, size_t_ctype }},
{ "__builtin_strncpy", &string_ctype, 0, { &string_ctype, &const_string_ctype, size_t_ctype }},
{ "__builtin_strndup", &string_ctype, 0, { &const_string_ctype, size_t_ctype }},
{ "__builtin_strnstr", &string_ctype, 0, { &const_string_ctype, &const_string_ctype, size_t_ctype }},
{ "__builtin_strpbrk", &string_ctype, 0, { &const_string_ctype, &const_string_ctype }},
{ "__builtin_strrchr", &string_ctype, 0, { &const_string_ctype, &int_ctype }},
{ "__builtin_strspn", size_t_ctype, 0, { &const_string_ctype, &const_string_ctype }},
{ "__builtin_strstr", &string_ctype, 0, { &const_string_ctype, &const_string_ctype }},
{ "__builtin_sub_overflow", &bool_ctype, 1, .op = &overflow_op },
{ "__builtin_sub_overflow_p", &bool_ctype, 1, .op = &overflow_p_op },
{ "__builtin_trap", &void_ctype, 0 },
{ "__builtin_uadd_overflow", &bool_ctype, 0, { &uint_ctype, &uint_ctype, &uint_ptr_ctype }},
{ "__builtin_uaddl_overflow", &bool_ctype, 0, { &ulong_ctype, &ulong_ctype, &ulong_ptr_ctype }},
{ "__builtin_uaddll_overflow", &bool_ctype, 0, { &ullong_ctype, &ullong_ctype, &ullong_ptr_ctype }},
{ "__builtin_umul_overflow", &bool_ctype, 0, { &uint_ctype, &uint_ctype, &uint_ptr_ctype }},
{ "__builtin_umull_overflow", &bool_ctype, 0, { &ulong_ctype, &ulong_ctype, &ulong_ptr_ctype }},
{ "__builtin_umulll_overflow", &bool_ctype, 0, { &ullong_ctype, &ullong_ctype, &ullong_ptr_ctype }},
{ "__builtin_unreachable", &void_ctype, 0 },
{ "__builtin_usub_overflow", &bool_ctype, 0, { &uint_ctype, &uint_ctype, &uint_ptr_ctype }},
{ "__builtin_usubl_overflow", &bool_ctype, 0, { &ulong_ctype, &ulong_ctype, &ulong_ptr_ctype }},
{ "__builtin_usubll_overflow", &bool_ctype, 0, { &ullong_ctype, &ullong_ctype, &ullong_ptr_ctype }},
{ "__builtin_va_arg_pack_len", size_t_ctype, 0 },
{ "__builtin_vprintf", &int_ctype, 0, { &const_string_ctype, va_list_ctype }},
{ "__builtin_vsnprintf", &int_ctype, 0, { &string_ctype, size_t_ctype, &const_string_ctype, va_list_ctype }},
{ "__builtin_vsprintf", &int_ctype, 0, { &string_ctype, &const_string_ctype, va_list_ctype }},
{ "__builtin___memcpy_chk", &ptr_ctype, 0, { &ptr_ctype, &const_ptr_ctype, size_t_ctype, size_t_ctype }},
{ "__builtin___memmove_chk", &ptr_ctype, 0, { &ptr_ctype, &const_ptr_ctype, size_t_ctype, size_t_ctype }},
{ "__builtin___mempcpy_chk", &ptr_ctype, 0, { &ptr_ctype, &const_ptr_ctype, size_t_ctype, size_t_ctype }},
{ "__builtin___memset_chk", &ptr_ctype, 0, { &ptr_ctype, &int_ctype, size_t_ctype, size_t_ctype }},
{ "__builtin___snprintf_chk", &int_ctype, 1, { &string_ctype, size_t_ctype, &int_ctype , size_t_ctype, &const_string_ctype }},
{ "__builtin___sprintf_chk", &int_ctype, 1, { &string_ctype, &int_ctype, size_t_ctype, &const_string_ctype }},
{ "__builtin___stpcpy_chk", &string_ctype, 0, { &string_ctype, &const_string_ctype, size_t_ctype }},
{ "__builtin___strcat_chk", &string_ctype, 0, { &string_ctype, &const_string_ctype, size_t_ctype }},
{ "__builtin___strcpy_chk", &string_ctype, 0, { &string_ctype, &const_string_ctype, size_t_ctype }},
{ "__builtin___strncat_chk", &string_ctype, 0, { &string_ctype, &const_string_ctype, size_t_ctype, size_t_ctype }},
{ "__builtin___strncpy_chk", &string_ctype, 0, { &string_ctype, &const_string_ctype, size_t_ctype, size_t_ctype }},
{ "__builtin___vsnprintf_chk", &int_ctype, 0, { &string_ctype, size_t_ctype, &int_ctype, size_t_ctype, &const_string_ctype, va_list_ctype }},
{ "__builtin___vsprintf_chk", &int_ctype, 0, { &string_ctype, &int_ctype, size_t_ctype, &const_string_ctype, va_list_ctype }},
{ "__sync_add_and_fetch", NULL, 1, { vol_ptr, &dyntype }, .op = &atomic_op },
{ "__sync_and_and_fetch", NULL, 1, { vol_ptr, &dyntype }, .op = &atomic_op },
{ "__sync_bool_compare_and_swap", &bool_ctype, 1, { vol_ptr, &dyntype, &dyntype }, .op = &atomic_op},
{ "__sync_fetch_and_add", NULL, 1, { vol_ptr, &dyntype }, .op = &atomic_op },
{ "__sync_fetch_and_and", NULL, 1, { vol_ptr, &dyntype }, .op = &atomic_op },
{ "__sync_fetch_and_nand", NULL, 1, { vol_ptr, &dyntype }, .op = &atomic_op },
{ "__sync_fetch_and_or", NULL, 1, { vol_ptr, &dyntype }, .op = &atomic_op },
{ "__sync_fetch_and_sub", NULL, 1, { vol_ptr, &dyntype }, .op = &atomic_op },
{ "__sync_fetch_and_xor", NULL, 1, { vol_ptr, &dyntype }, .op = &atomic_op },
{ "__sync_lock_release", &void_ctype, 1, { vol_ptr }, .op = &atomic_op },
{ "__sync_lock_test_and_set", NULL, 1, { vol_ptr, &dyntype }, .op = &atomic_op },
{ "__sync_nand_and_fetch", NULL, 1, { vol_ptr, &dyntype }, .op = &atomic_op },
{ "__sync_or_and_fetch", NULL, 1, { vol_ptr, &dyntype }, .op = &atomic_op },
{ "__sync_sub_and_fetch", NULL, 1, { vol_ptr, &dyntype }, .op = &atomic_op },
{ "__sync_synchronize", &void_ctype, 1 },
{ "__sync_val_compare_and_swap", NULL, 1, { vol_ptr, &dyntype, &dyntype }, .op = &atomic_op },
{ "__sync_xor_and_fetch", NULL, 1, { vol_ptr, &dyntype }, .op = &atomic_op },
{ }
};
void init_builtins(int stream)
{
declare_builtins(stream, builtins_common);
declare_builtins(stream, arch_target->builtins);
init_linearized_builtins(stream);
}
sparse-0.6.4/builtin.h 0000664 0000000 0000000 00000000541 14115310122 0014623 0 ustar 00root root 0000000 0000000 #ifndef _BUILTIN_H_
#define _BUILTIN_H_
#include "symbol.h"
struct builtin_fn {
const char *name;
struct symbol *ret_type;
unsigned int variadic:1;
struct symbol *args[6];
struct symbol *_args_null_tail;
struct symbol_op *op;
};
void declare_builtins(int stream, const struct builtin_fn tbl[]);
extern struct symbol_op generic_int_op;
#endif
sparse-0.6.4/c2xml.c 0000664 0000000 0000000 00000017242 14115310122 0014203 0 ustar 00root root 0000000 0000000 /*
* Sparse c2xml
*
* Dumps the parse tree as an xml document
*
* Copyright (C) 2007 Rob Taylor
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include "expression.h"
#include "parse.h"
#include "scope.h"
#include "symbol.h"
static xmlDocPtr doc = NULL; /* document pointer */
static xmlNodePtr root_node = NULL;/* root node pointer */
static int idcount = 0;
static void examine_symbol(struct symbol *sym, xmlNodePtr node);
static xmlAttrPtr newProp(xmlNodePtr node, const char *name, const char *value)
{
return xmlNewProp(node, BAD_CAST name, BAD_CAST value);
}
static xmlAttrPtr newNumProp(xmlNodePtr node, const char *name, int value)
{
char buf[256];
snprintf(buf, 256, "%d", value);
return newProp(node, name, buf);
}
static xmlAttrPtr newIdProp(xmlNodePtr node, const char *name, unsigned int id)
{
char buf[256];
snprintf(buf, 256, "_%d", id);
return newProp(node, name, buf);
}
static xmlNodePtr new_sym_node(struct symbol *sym, const char *name, xmlNodePtr parent)
{
xmlNodePtr node;
const char *ident = show_ident(sym->ident);
assert(name != NULL);
assert(sym != NULL);
assert(parent != NULL);
node = xmlNewChild(parent, NULL, BAD_CAST "symbol", NULL);
newProp(node, "type", name);
newIdProp(node, "id", idcount);
if (sym->ident && ident)
newProp(node, "ident", ident);
newProp(node, "file", stream_name(sym->pos.stream));
newNumProp(node, "start-line", sym->pos.line);
newNumProp(node, "start-col", sym->pos.pos);
if (sym->endpos.type) {
newNumProp(node, "end-line", sym->endpos.line);
newNumProp(node, "end-col", sym->endpos.pos);
if (sym->pos.stream != sym->endpos.stream)
newProp(node, "end-file", stream_name(sym->endpos.stream));
}
sym->aux = node;
idcount++;
return node;
}
static inline void examine_members(struct symbol_list *list, xmlNodePtr node)
{
struct symbol *sym;
FOR_EACH_PTR(list, sym) {
examine_symbol(sym, node);
} END_FOR_EACH_PTR(sym);
}
static void examine_modifiers(struct symbol *sym, xmlNodePtr node)
{
const char *modifiers[] = {
"auto",
"register",
"static",
"extern",
"const",
"volatile",
"signed",
"unsigned",
"char",
"short",
"long",
"long-long",
"typedef",
NULL,
NULL,
NULL,
NULL,
NULL,
"inline",
"addressable",
"nocast",
"noderef",
"accessed",
"toplevel",
"label",
"assigned",
"type-type",
"safe",
"user-type",
"force",
"explicitly-signed",
"bitwise"};
int i;
if (sym->namespace != NS_SYMBOL)
return;
/*iterate over the 32 bit bitfield*/
for (i=0; i < 32; i++) {
if ((sym->ctype.modifiers & 1<bit_size);
newNumProp(node, "alignment", sym->ctype.alignment);
newNumProp(node, "offset", sym->offset);
if (is_bitfield_type(sym)) {
newNumProp(node, "bit-offset", sym->bit_offset);
}
}
static void examine_symbol(struct symbol *sym, xmlNodePtr node)
{
xmlNodePtr child = NULL;
const char *base;
int array_size;
if (!sym)
return;
if (sym->aux) /*already visited */
return;
if (sym->ident && sym->ident->reserved)
return;
child = new_sym_node(sym, get_type_name(sym->type), node);
examine_modifiers(sym, child);
examine_layout(sym, child);
if (sym->ctype.base_type) {
if ((base = builtin_typename(sym->ctype.base_type)) == NULL) {
if (!sym->ctype.base_type->aux) {
examine_symbol(sym->ctype.base_type, root_node);
}
xmlNewProp(child, BAD_CAST "base-type",
xmlGetProp((xmlNodePtr)sym->ctype.base_type->aux, BAD_CAST "id"));
} else {
newProp(child, "base-type-builtin", base);
}
}
if (sym->array_size) {
/* TODO: modify get_expression_value to give error return */
array_size = get_expression_value(sym->array_size);
newNumProp(child, "array-size", array_size);
}
switch (sym->type) {
case SYM_STRUCT:
case SYM_UNION:
examine_members(sym->symbol_list, child);
break;
case SYM_FN:
examine_members(sym->arguments, child);
break;
case SYM_UNINITIALIZED:
newProp(child, "base-type-builtin", builtin_typename(sym));
break;
default:
break;
}
return;
}
static struct position *get_expansion_end (struct token *token)
{
struct token *p1, *p2;
for (p1=NULL, p2=NULL;
!eof_token(token);
p2 = p1, p1 = token, token = token->next);
if (p2)
return &(p2->pos);
else
return NULL;
}
static void examine_macro(struct symbol *sym, xmlNodePtr node)
{
struct position *pos;
/* this should probably go in the main codebase*/
pos = get_expansion_end(sym->expansion);
if (pos)
sym->endpos = *pos;
else
sym->endpos = sym->pos;
new_sym_node(sym, "macro", node);
}
static void examine_namespace(struct symbol *sym)
{
if (sym->ident && sym->ident->reserved)
return;
switch(sym->namespace) {
case NS_MACRO:
examine_macro(sym, root_node);
break;
case NS_TYPEDEF:
case NS_STRUCT:
case NS_SYMBOL:
examine_symbol(sym, root_node);
break;
case NS_NONE:
case NS_LABEL:
case NS_ITERATOR:
case NS_UNDEF:
case NS_PREPROCESSOR:
case NS_KEYWORD:
break;
default:
die("Unrecognised namespace type %d",sym->namespace);
}
}
static int get_stream_id (const char *name)
{
int i;
for (i=0; ipos.stream == stream_id)
examine_namespace(sym);
} END_FOR_EACH_PTR(sym);
}
int main(int argc, char **argv)
{
struct string_list *filelist = NULL;
struct symbol_list *symlist = NULL;
char *file;
doc = xmlNewDoc(BAD_CAST "1.0");
root_node = xmlNewNode(NULL, BAD_CAST "parse");
xmlDocSetRootElement(doc, root_node);
/* - A DTD is probably unnecessary for something like this
dtd = xmlCreateIntSubset(doc, "parse", "http://www.kernel.org/pub/software/devel/sparse/parse.dtd" NULL, "parse.dtd");
ns = xmlNewNs (root_node, "http://www.kernel.org/pub/software/devel/sparse/parse.dtd", NULL);
xmlSetNs(root_node, ns);
*/
symlist = sparse_initialize(argc, argv, &filelist);
FOR_EACH_PTR(filelist, file) {
examine_symbol_list(file, symlist);
sparse_keep_tokens(file);
examine_symbol_list(file, file_scope->symbols);
examine_symbol_list(file, global_scope->symbols);
} END_FOR_EACH_PTR(file);
xmlSaveFormatFileEnc("-", doc, "UTF-8", 1);
xmlFreeDoc(doc);
xmlCleanupParser();
return 0;
}
sparse-0.6.4/cgcc 0000775 0000000 0000000 00000024763 14115310122 0013645 0 ustar 00root root 0000000 0000000 #!/usr/bin/perl -w
# -----------------------------------------------------------------------------
use strict;
use warnings;
my $cc = $ENV{'REAL_CC'} || 'cc';
my $check = $ENV{'CHECK'} || 'sparse';
my $ccom = $cc;
my $m32 = 0;
my $m64 = 0;
my $has_specs = 0;
my $gendeps = 0;
my $do_check = 0;
my $do_compile = 1;
my $gcc_base_dir;
my $multiarch_dir;
my $verbose = 0;
my $nargs = 0;
while (@ARGV) {
$_ = shift(@ARGV);
if ($nargs) {
$nargs--;
goto add_option;
}
# Look for a .c file. We don't want to run the checker on .o or .so files
# in the link run.
$do_check = 1 if /^[^-].*\.c$/;
# Ditto for stdin.
$do_check = 1 if $_ eq '-';
if (/^-(o|MF|MT|MQ)$/) {
# Need to be checked explicitly since otherwise
# the argument would be processed as a
# (non-existant) source file or as an option.
die ("$0: missing argument for $_") if !@ARGV;
$nargs = 1;
}
# Ignore the extension if '-x c' is given.
if ($_ eq '-x') {
die ("$0: missing argument for $_") if !@ARGV;
die ("$0: invalid argument for $_") if $ARGV[0] ne 'c';
$do_check = 1;
$nargs = 1;
}
$m32 = 1 if /^-m32$/;
$m64 = 1 if /^-m64$/;
$gendeps = 1 if /^-(M|MM)$/;
if (/^-target=(.*)$/) {
$check .= &add_specs ($1);
$has_specs = 1;
next;
}
if ($_ eq '-no-compile') {
$do_compile = 0;
next;
}
if (/^-gcc-base-dir$/) {
$gcc_base_dir = shift @ARGV;
die ("$0: missing argument for -gcc-base-dir option") if !$gcc_base_dir;
next;
}
if (/^-multiarch-dir$/) {
$multiarch_dir = shift @ARGV;
die ("$0: missing argument for -multiarch-dir option") if !$multiarch_dir;
next;
}
# If someone adds "-E", don't pre-process twice.
$do_compile = 0 if $_ eq '-E';
$verbose = 1 if $_ eq '-v';
add_option:
my $this_arg = ' ' . "e_arg ($_);
$cc .= $this_arg unless &check_only_option ($_);
$check .= $this_arg;
}
if ($gendeps) {
$do_compile = 1;
$do_check = 0;
}
if ($do_check) {
if (!$has_specs) {
$check .= &add_specs ('host_arch_specs');
$check .= &add_specs ('host_os_specs');
}
$gcc_base_dir = qx($ccom -print-file-name=) if !$gcc_base_dir;
chomp($gcc_base_dir); # possibly remove '\n' from compiler
$check .= " -gcc-base-dir " . $gcc_base_dir if $gcc_base_dir;
$multiarch_dir = qx($ccom -print-multiarch) if ! defined $multiarch_dir;
chomp($multiarch_dir); # possibly remove '\n' from compiler
$check .= " -multiarch-dir " . $multiarch_dir if $multiarch_dir;
print "$check\n" if $verbose;
if ($do_compile) {
system ($check) == 0 or exit 1;
} else {
exec ($check);
}
}
if ($do_compile) {
print "$cc\n" if $verbose;
exec ($cc);
}
exit 0;
# -----------------------------------------------------------------------------
# Check if an option is for "check" only.
sub check_only_option {
my ($arg) = @_;
return 1 if $arg =~ /^-W(no-?)?(address-space|bitwise|cast-to-as|cast-truncate|constant-suffix|context|decl|default-bitfield-sign|designated-init|do-while|enum-mismatch|external-function-has-definition|init-cstring|memcpy-max-count|non-pointer-null|old-initializer|one-bit-signed-bitfield|override-init-all|paren-string|ptr-subtraction-blows|return-void|sizeof-bool|sparse-all|sparse-error|transparent-union|typesign|undef|unknown-attribute)$/;
return 1 if $arg =~ /^-v(no-?)?(entry|dead)$/;
return 1 if $arg =~ /^-f(dump-ir|memcpy-max-count|diagnostic-prefix)(=\S*)?$/;
return 1 if $arg =~ /^-f(mem2reg|optim)(-enable|-disable|=last)?$/;
return 1 if $arg =~ /^-msize-(long|llp64)$/;
return 0;
}
# -----------------------------------------------------------------------------
# Simple arg-quoting function. Just adds backslashes when needed.
sub quote_arg {
my ($arg) = @_;
return "''" if $arg eq '';
return join ('',
map {
m|^[-a-zA-Z0-9._/,=]+$| ? $_ : "\\" . $_;
} (split (//, $arg)));
}
# -----------------------------------------------------------------------------
sub float_types {
my ($has_inf,$has_qnan,$dec_dig,@bitsizes) = @_;
my $result = " -D__FLT_RADIX__=2";
$result .= " -D__FINITE_MATH_ONLY__=" . ($has_inf || $has_qnan ? '0' : '1');
$result .= " -D__DECIMAL_DIG__=$dec_dig";
my %constants =
(24 =>
{
'MIN' => '1.17549435e-38',
'MAX' => '3.40282347e+38',
'EPSILON' => '1.19209290e-7',
'DENORM_MIN' => '1.40129846e-45',
},
53 =>
{
'MIN' => '2.2250738585072014e-308',
'MAX' => '1.7976931348623157e+308',
'EPSILON' => '2.2204460492503131e-16',
'DENORM_MIN' => '4.9406564584124654e-324',
},
64 =>
{
'MIN' => '3.36210314311209350626e-4932',
'MAX' => '1.18973149535723176502e+4932',
'EPSILON' => '1.08420217248550443401e-19',
'DENORM_MIN' => '3.64519953188247460253e-4951',
},
113 =>
{
'MIN' => '3.36210314311209350626267781732175260e-4932',
'MAX' => '1.18973149535723176508575932662800702e+4932',
'EPSILON' => '1.92592994438723585305597794258492732e-34',
'DENORM_MIN' => '6.47517511943802511092443895822764655e-4966',
},
);
my @types = (['FLT','F'], ['DBL',''], ['LDBL','L']);
while (@types) {
my ($mant_bits,$exp_bits) = @{ shift @bitsizes };
my ($name,$suffix) = @{ shift @types };
my $h = $constants{$mant_bits};
die "$0: weird number of mantissa bits." unless $h;
my $mant_dig = int (($mant_bits - 1) * log (2) / log (10));
my $max_exp = 1 << ($exp_bits - 1);
my $min_exp = 3 - $max_exp;
my $max_10_exp = int ($max_exp * log (2) / log (10));
my $min_10_exp = -int (-$min_exp * log (2) / log (10));
$result .= " -D__${name}_MANT_DIG__=$mant_bits";
$result .= " -D__${name}_DIG__=$mant_dig";
$result .= " -D__${name}_MIN_EXP__='($min_exp)'";
$result .= " -D__${name}_MAX_EXP__=$max_exp";
$result .= " -D__${name}_MIN_10_EXP__='($min_10_exp)'";
$result .= " -D__${name}_MAX_10_EXP__=$max_10_exp";
$result .= " -D__${name}_HAS_INFINITY__=" . ($has_inf ? '1' : '0');
$result .= " -D__${name}_HAS_QUIET_NAN__=" . ($has_qnan ? '1' : '0');;
foreach my $inf (sort keys %$h) {
$result .= " -D__${name}_${inf}__=" . $h->{$inf} . $suffix;
}
}
return $result;
}
# -----------------------------------------------------------------------------
sub add_specs {
my ($spec) = @_;
if ($spec eq 'sunos') {
return " --os=$spec" .
' -DSVR4=1' .
' -D__STDC__=0' .
' -D_REENTRANT' .
' -D_SOLARIS_THREADS' .
' -DNULL="((void *)0)"';
} elsif ($spec eq 'linux') {
return " --os=$spec";
} elsif ($spec eq 'gnu/kfreebsd') {
return &add_specs ('unix') .
' -D__FreeBSD_kernel__=1';
} elsif ($spec eq 'openbsd') {
return " --os=$spec";
} elsif ($spec eq 'freebsd') {
return " --os=$spec";
} elsif ($spec eq 'netbsd') {
return " --os=$spec";
} elsif ($spec eq 'darwin') {
return " --os=$spec";
} elsif ($spec eq 'gnu') { # Hurd
return &add_specs ('unix') . # So, GNU is Unix, uh?
' -D__GNU__=1 -D__gnu_hurd__=1 -D__MACH__=1';
} elsif ($spec eq 'unix') {
return ' -Dunix=1 -D__unix=1 -D__unix__=1';
} elsif ( $spec =~ /^cygwin/) {
return ' --os=cygwin';
} elsif ($spec eq 'i386') {
$m32 = 1;
return (
' --arch=i386' .
&float_types (1, 1, 21, [24,8], [53,11], [64,15]));
} elsif ($spec eq 'sparc') {
return (
' --arch=sparc' .
&float_types (1, 1, 33, [24,8], [53,11], [113,15]));
} elsif ($spec eq 'sparc64') {
return (
' --arch=sparc64' .
&float_types (1, 1, 33, [24,8], [53,11], [113,15]));
} elsif ($spec eq 'x86_64') {
return (' --arch=x86_64' .
&float_types (1, 1, 33, [24,8], [53,11], [113,15]));
} elsif ($spec eq 'ppc') {
return (' --arch=ppc' .
&float_types (1, 1, 21, [24,8], [53,11], [113,15]));
} elsif ($spec eq 'ppc64') {
return (
' --arch=ppc64' .
&float_types (1, 1, 21, [24,8], [53,11], [113,15]));
} elsif ($spec eq 'ppc64be') {
return &add_specs ('ppc64') . ' -mbig-endian -D_CALL_ELF=1';
} elsif ($spec eq 'ppc64le') {
return &add_specs ('ppc64') . ' -mlittle-endian -D_CALL_ELF=2';
} elsif ($spec eq 's390x') {
return (' -D_BIG_ENDIAN' .
' --arch=s390x' .
&float_types (1, 1, 36, [24,8], [53,11], [113,15]));
} elsif ($spec eq 'riscv32') {
return (' --arch=riscv32' .
&float_types (1, 1, 33, [24,8], [53,11], [53,11]));
} elsif ($spec eq 'riscv64') {
return (' --arch=riscv64' .
&float_types (1, 1, 33, [24,8], [53,11], [113,15]));
} elsif ($spec eq 'arm') {
return (' --arch=arm' .
&float_types (1, 1, 36, [24,8], [53,11], [53, 11]));
} elsif ($spec eq 'arm+hf') {
return &add_specs ('arm') . ' -mfloat-abi=hard';
} elsif ($spec eq 'aarch64') {
return (' --arch=aarch64' .
&float_types (1, 1, 36, [24,8], [53,11], [113,15]));
} elsif ($spec eq 'host_os_specs') {
my $os = `uname -s`;
chomp $os;
return &add_specs (lc $os);
} elsif ($spec eq 'host_arch_specs') {
my $gccmachine;
my $arch;
$gccmachine = `$ccom -dumpmachine`;
chomp $gccmachine;
if ($gccmachine =~ '^aarch64-') {
return &add_specs ('aarch64');
} elsif ($gccmachine =~ '^arm-.*eabihf$') {
return &add_specs ('arm+hf');
} elsif ($gccmachine =~ '^arm-') {
return &add_specs ('arm');
} elsif ($gccmachine =~ '^i[23456]86-') {
return &add_specs ('i386');
} elsif ($gccmachine =~ '^(powerpc|ppc)64le-') {
return &add_specs ('ppc64le');
} elsif ($gccmachine =~ '^s390x-') {
return &add_specs ('s390x');
} elsif ($gccmachine eq 'x86_64-linux-gnux32') {
return &add_specs ('x86_64') . ' -mx32';
} elsif ($gccmachine =~ '^x86_64-') {
return &add_specs ('x86_64');
}
# fall back to uname -m to determine the specifics.
# Note: this is only meaningful when using natively
# since information about the host is used to
# guess characteristics of the target.
$arch = `uname -m`;
chomp $arch;
if ($arch =~ /^(i.?86|athlon)$/i) {
return &add_specs ('i386');
} elsif ($arch =~ /^(sun4u)$/i) {
return &add_specs ('sparc');
} elsif ($arch =~ /^(x86_64)$/i) {
return &add_specs ('x86_64');
} elsif ($arch =~ /^(ppc)$/i) {
return &add_specs ('ppc');
} elsif ($arch =~ /^(ppc64)$/i) {
return &add_specs ('ppc64be');
} elsif ($arch =~ /^(ppc64le)$/i) {
return &add_specs ('ppc64le');
} elsif ($arch =~ /^(s390x)$/i) {
return &add_specs ('s390x');
} elsif ($arch =~ /^(sparc64)$/i) {
return &add_specs ('sparc64');
} elsif ($arch =~ /^arm(?:v[78]l)?$/i) {
return &add_specs ('arm');
} elsif ($arch =~ /^(aarch64)$/i) {
return &add_specs ('aarch64');
}
} else {
die "$0: invalid specs: $spec\n";
}
}
# -----------------------------------------------------------------------------
sparse-0.6.4/cgcc.1 0000664 0000000 0000000 00000002046 14115310122 0013767 0 ustar 00root root 0000000 0000000 .\" cgcc manpage by Josh Triplett
.TH cgcc "1"
.
.SH NAME
cgcc \- Compiler wrapper to run Sparse after compiling
.
.SH SYNOPSIS
.B cgcc
[\fISPARSE OPTIONS\fR]... [\fICOMPILER OPTIONS\fR]... [\fIINPUT FILES\fR]...
.br
.B make CC=cgcc
.
.SH DESCRIPTION
\fBcgcc\fR provides a wrapper around a C compiler (\fBcc\fR by
default) which also invokes the Sparse static analysis tool.
.P
\fBcgcc\fR accepts all Sparse command-line options, such as warning
options, and passes all other options through to the compiler.
.P
By providing the same interface as the C compiler, \fBcgcc\fR allows
projects to run Sparse as part of their build without modifying their
build system, by using \fBcgcc\fR as the compiler. For many projects,
setting \fBCC=cgcc\fR on the \fBmake\fR command-line will work.
.
.SH ENVIRONMENT
.TP
.B REAL_CC
If set, \fBcgcc\fR will use this as the compiler to invoke, rather
than the default \fBcc\fR.
.
.TP
.B CHECK
If set, \fBcgcc\fR will use this as the Sparse program to invoke,
rather than the default \fBsparse\fR.
.
.SH SEE ALSO
.BR sparse (1)
sparse-0.6.4/char.c 0000664 0000000 0000000 00000006544 14115310122 0014076 0 ustar 00root root 0000000 0000000 #include
#include "target.h"
#include "lib.h"
#include "allocate.h"
#include "token.h"
#include "expression.h"
#include "char.h"
static const char *parse_escape(const char *p, unsigned *val, const char *end, int bits, struct position pos)
{
unsigned c = *p++;
unsigned d;
if (c != '\\') {
*val = c;
return p;
}
c = *p++;
switch (c) {
case 'a': c = '\a'; break;
case 'b': c = '\b'; break;
case 't': c = '\t'; break;
case 'n': c = '\n'; break;
case 'v': c = '\v'; break;
case 'f': c = '\f'; break;
case 'r': c = '\r'; break;
case 'e': c = '\e'; break;
case 'x': {
unsigned mask = -(1U << (bits - 4));
for (c = 0; p < end; c = (c << 4) + d) {
d = hexval(*p);
if (d > 16)
break;
p++;
if (c & mask) {
warning(pos,
"hex escape sequence out of range");
mask = 0;
}
}
break;
}
case '0'...'7': {
if (p + 2 < end)
end = p + 2;
c -= '0';
while (p < end && (d = *p - '0') < 8) {
c = (c << 3) + d;
p++;
}
if ((c & 0400) && bits < 9)
warning(pos,
"octal escape sequence out of range");
break;
}
default: /* everything else is left as is */
warning(pos, "unknown escape sequence: '\\%c'", c);
break;
case '\\':
case '\'':
case '"':
case '?':
break; /* those are legal, so no warnings */
}
*val = c & ~((~0U << (bits - 1)) << 1);
return p;
}
void get_char_constant(struct token *token, unsigned long long *val)
{
const char *p = token->embedded, *end;
unsigned v;
int type = token_type(token);
switch (type) {
case TOKEN_CHAR:
case TOKEN_WIDE_CHAR:
p = token->string->data;
end = p + token->string->length - 1;
if (end == p) {
sparse_error(token->pos, "empty character constant");
*val = 0;
return;
}
break;
case TOKEN_CHAR_EMBEDDED_0 ... TOKEN_CHAR_EMBEDDED_3:
end = p + type - TOKEN_CHAR;
break;
default:
end = p + type - TOKEN_WIDE_CHAR;
}
p = parse_escape(p, &v, end,
type < TOKEN_WIDE_CHAR ? bits_in_char : wchar_ctype->bit_size, token->pos);
if (p != end)
warning(token->pos,
"multi-character character constant");
*val = v;
}
struct token *get_string_constant(struct token *token, struct expression *expr)
{
struct string *string = token->string;
struct token *next = token->next, *done = NULL;
int stringtype = token_type(token);
int is_wide = stringtype == TOKEN_WIDE_STRING;
static char buffer[MAX_STRING];
int len = 0;
int bits;
int esc_count = 0;
while (!done) {
switch (token_type(next)) {
case TOKEN_WIDE_STRING:
is_wide = 1;
case TOKEN_STRING:
next = next->next;
break;
default:
done = next;
}
}
bits = is_wide ? wchar_ctype->bit_size: bits_in_char;
while (token != done) {
unsigned v;
const char *p = token->string->data;
const char *end = p + token->string->length - 1;
while (p < end) {
if (*p == '\\')
esc_count++;
p = parse_escape(p, &v, end, bits, token->pos);
if (len < MAX_STRING)
buffer[len] = v;
len++;
}
token = token->next;
}
if (len > MAX_STRING) {
warning(token->pos, "trying to concatenate %d-character string (%d bytes max)", len, MAX_STRING);
len = MAX_STRING;
}
if (esc_count || len >= string->length) {
if (string->immutable || len >= string->length) /* can't cannibalize */
string = __alloc_string(len+1);
string->length = len+1;
memcpy(string->data, buffer, len);
string->data[len] = '\0';
}
expr->string = string;
expr->wide = is_wide;
return token;
}
sparse-0.6.4/char.h 0000664 0000000 0000000 00000000224 14115310122 0014070 0 ustar 00root root 0000000 0000000 extern void get_char_constant(struct token *, unsigned long long *);
extern struct token *get_string_constant(struct token *, struct expression *);
sparse-0.6.4/compat-bsd.c 0000664 0000000 0000000 00000002167 14115310122 0015207 0 ustar 00root root 0000000 0000000 /*
* BSD Compatibility functions
*
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "compat-linux.c"
sparse-0.6.4/compat-cygwin.c 0000664 0000000 0000000 00000003323 14115310122 0015732 0 ustar 00root root 0000000 0000000 /*
* Cygwin Compatibility functions
*
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include
#include
#include
#include
#include "lib.h"
#include "allocate.h"
#include "token.h"
void *blob_alloc(unsigned long size)
{
void *ptr;
size = (size + 4095) & ~4095;
ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (ptr == MAP_FAILED)
ptr = NULL;
else
memset(ptr, 0, size);
return ptr;
}
void blob_free(void *addr, unsigned long size)
{
size = (size + 4095) & ~4095;
munmap(addr, size);
}
long double string_to_ld(const char *nptr, char **endptr)
{
return strtod(nptr, endptr);
}
sparse-0.6.4/compat-linux.c 0000664 0000000 0000000 00000000167 14115310122 0015574 0 ustar 00root root 0000000 0000000 #define _GNU_SOURCE
#include "lib.h"
#include "allocate.h"
#include "compat/mmap-blob.c"
#include "compat/strtold.c"
sparse-0.6.4/compat-mingw.c 0000664 0000000 0000000 00000003102 14115310122 0015546 0 ustar 00root root 0000000 0000000 /*
* MinGW Compatibility functions
*
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include
#include
#include
#include
#include
#include "lib.h"
#include "allocate.h"
#include "token.h"
void *blob_alloc(unsigned long size)
{
void *ptr;
ptr = malloc(size);
if (ptr != NULL)
memset(ptr, 0, size);
return ptr;
}
void blob_free(void *addr, unsigned long size)
{
free(addr);
}
long double string_to_ld(const char *nptr, char **endptr)
{
return strtod(nptr, endptr);
}
sparse-0.6.4/compat-solaris.c 0000664 0000000 0000000 00000000032 14115310122 0016100 0 ustar 00root root 0000000 0000000 #include "compat-linux.c"
sparse-0.6.4/compat.h 0000664 0000000 0000000 00000001255 14115310122 0014443 0 ustar 00root root 0000000 0000000 #ifndef COMPAT_H
#define COMPAT_H
/*
* Various systems get these things wrong. So
* we create a small compat library for them.
*
* - zeroed anonymous mmap
* Missing in MinGW
* - "string to long double" (C99 strtold())
* Missing in Solaris and MinGW
*/
/*
* Our "blob" allocator works on chunks that are multiples
* of this size (the underlying allocator may be a mmap that
* cannot handle smaller chunks, for example, so trying to
* allocate blobs that aren't aligned is not going to work).
*/
#define CHUNK 32768
void *blob_alloc(unsigned long size);
void blob_free(void *addr, unsigned long size);
long double string_to_ld(const char *nptr, char **endptr);
#endif
sparse-0.6.4/compat/ 0000775 0000000 0000000 00000000000 14115310122 0014267 5 ustar 00root root 0000000 0000000 sparse-0.6.4/compat/bswap.h 0000664 0000000 0000000 00000002302 14115310122 0015551 0 ustar 00root root 0000000 0000000 #ifndef _COMPAT_BSWAP_H_
#define _COMPAT_BSWAP_H_
#if defined(__GNUC__)
#if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 8))
#define __HAS_BUILTIN_BSWAP16
#endif
#if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4))
#define __HAS_BUILTIN_BSWAP32
#define __HAS_BUILTIN_BSWAP64
#endif
#endif
#if defined(__clang__)
#if (__clang_major__ > 3) || ((__clang_major__ == 3) && (__clang_minor__ >= 2))
#define __HAS_BUILTIN_BSWAP16
#endif
#if (__clang_major__ > 3) || ((__clang_major__ == 3) && (__clang_minor__ >= 0))
#define __HAS_BUILTIN_BSWAP32
#define __HAS_BUILTIN_BSWAP64
#endif
#endif
#ifdef __HAS_BUILTIN_BSWAP16
#define bswap16(x) __builtin_bswap16(x)
#else
#include
static inline uint16_t bswap16(uint16_t x)
{
return x << 8 | x >> 8;
}
#endif
#ifdef __HAS_BUILTIN_BSWAP32
#define bswap32(x) __builtin_bswap32(x)
#else
#include
static inline uint32_t bswap32(uint32_t x)
{
return x >> 24 | (x >> 8 & 0xff00) | (x << 8 & 0xff0000) | x << 24;
}
#endif
#ifdef __HAS_BUILTIN_BSWAP64
#define bswap64(x) __builtin_bswap64(x)
#else
#include
static inline uint64_t bswap64(uint64_t x)
{
return ((uint64_t)bswap32(x)) << 32 | bswap32(x >> 32);
}
#endif
#endif
sparse-0.6.4/compat/mmap-blob.c 0000664 0000000 0000000 00000001525 14115310122 0016304 0 ustar 00root root 0000000 0000000 #include
#include
/*
* Allow old BSD naming too, it would be a pity to have to make a
* separate file just for this.
*/
#ifndef MAP_ANONYMOUS
#define MAP_ANONYMOUS MAP_ANON
#endif
/*
* Our blob allocator enforces the strict CHUNK size
* requirement, as a portability check.
*/
void *blob_alloc(unsigned long size)
{
void *ptr;
if (size & ~CHUNK)
die("internal error: bad allocation size (%lu bytes)", size);
ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (ptr == MAP_FAILED)
ptr = NULL;
return ptr;
}
void blob_free(void *addr, unsigned long size)
{
if (!size || (size & ~CHUNK) || ((unsigned long) addr & 512))
die("internal error: bad blob free (%lu bytes at %p)", size, addr);
#ifndef DEBUG
munmap(addr, size);
#else
mprotect(addr, size, PROT_NONE);
#endif
}
sparse-0.6.4/compat/strtold.c 0000664 0000000 0000000 00000000162 14115310122 0016125 0 ustar 00root root 0000000 0000000 #include
long double string_to_ld(const char *nptr, char **endptr)
{
return strtold(nptr, endptr);
}
sparse-0.6.4/compile-i386.c 0000664 0000000 0000000 00000155420 14115310122 0015276 0 ustar 00root root 0000000 0000000 /*
* sparse/compile-i386.c
*
* Copyright (C) 2003 Transmeta Corp.
* 2003 Linus Torvalds
* Copyright 2003 Jeff Garzik
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* x86 backend
*
* TODO list:
* in general, any non-32bit SYM_BASETYPE is unlikely to work.
* complex initializers
* bitfields
* global struct/union variables
* addressing structures, and members of structures (as opposed to
* scalars) on the stack. Requires smarter stack frame allocation.
* labels / goto
* any function argument that isn't 32 bits (or promoted to such)
* inline asm
* floating point
*
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include "lib.h"
#include "allocate.h"
#include "token.h"
#include "parse.h"
#include "symbol.h"
#include "scope.h"
#include "expression.h"
#include "target.h"
#include "compile.h"
#include "bitmap.h"
struct textbuf {
unsigned int len; /* does NOT include terminating null */
char *text;
struct textbuf *next;
struct textbuf *prev;
};
struct loop_stack {
int continue_lbl;
int loop_bottom_lbl;
struct loop_stack *next;
};
struct atom;
struct storage;
DECLARE_PTR_LIST(str_list, struct atom);
DECLARE_PTR_LIST(atom_list, struct atom);
DECLARE_PTR_LIST(storage_list, struct storage);
struct function {
int stack_size;
int pseudo_nr;
struct storage_list *pseudo_list;
struct atom_list *atom_list;
struct str_list *str_list;
struct loop_stack *loop_stack;
struct symbol **argv;
unsigned int argc;
int ret_target;
};
enum storage_type {
STOR_PSEUDO, /* variable stored on the stack */
STOR_ARG, /* function argument */
STOR_SYM, /* a symbol we can directly ref in the asm */
STOR_REG, /* scratch register */
STOR_VALUE, /* integer constant */
STOR_LABEL, /* label / jump target */
STOR_LABELSYM, /* label generated from symbol's pointer value */
};
struct reg_info {
const char *name;
struct storage *contains;
const unsigned char aliases[12];
#define own_regno aliases[0]
};
struct storage {
enum storage_type type;
unsigned long flags;
/* STOR_REG */
struct reg_info *reg;
struct symbol *ctype;
union {
/* STOR_PSEUDO */
struct {
int pseudo;
int offset;
int size;
};
/* STOR_ARG */
struct {
int idx;
};
/* STOR_SYM */
struct {
struct symbol *sym;
};
/* STOR_VALUE */
struct {
long long value;
};
/* STOR_LABEL */
struct {
int label;
};
/* STOR_LABELSYM */
struct {
struct symbol *labelsym;
};
};
};
enum {
STOR_LABEL_VAL = (1 << 0),
STOR_WANTS_FREE = (1 << 1),
};
struct symbol_private {
struct storage *addr;
};
enum atom_type {
ATOM_TEXT,
ATOM_INSN,
ATOM_CSTR,
};
struct atom {
enum atom_type type;
union {
/* stuff for text */
struct {
char *text;
unsigned int text_len; /* w/o terminating null */
};
/* stuff for insns */
struct {
char insn[32];
char comment[40];
struct storage *op1;
struct storage *op2;
};
/* stuff for C strings */
struct {
struct string *string;
int label;
};
};
};
static struct function *current_func = NULL;
static struct textbuf *unit_post_text = NULL;
static const char *current_section;
static void emit_comment(const char * fmt, ...) FORMAT_ATTR(1);
static void emit_move(struct storage *src, struct storage *dest,
struct symbol *ctype, const char *comment);
static struct storage *x86_address_gen(struct expression *expr);
static struct storage *x86_symbol_expr(struct symbol *sym);
static void x86_symbol(struct symbol *sym);
static struct storage *x86_statement(struct statement *stmt);
static struct storage *x86_expression(struct expression *expr);
enum registers {
NOREG,
AL, DL, CL, BL, AH, DH, CH, BH, // 8-bit
AX, DX, CX, BX, SI, DI, BP, SP, // 16-bit
EAX, EDX, ECX, EBX, ESI, EDI, EBP, ESP, // 32-bit
EAX_EDX, ECX_EBX, ESI_EDI, // 64-bit
};
/* This works on regno's, reg_info's and hardreg_storage's */
#define byte_reg(reg) ((reg) - 16)
#define highbyte_reg(reg) ((reg)-12)
#define word_reg(reg) ((reg)-8)
#define REGINFO(nr, str, conflicts...) [nr] = { .name = str, .aliases = { nr , conflicts } }
static struct reg_info reg_info_table[] = {
REGINFO( AL, "%al", AX, EAX, EAX_EDX),
REGINFO( DL, "%dl", DX, EDX, EAX_EDX),
REGINFO( CL, "%cl", CX, ECX, ECX_EBX),
REGINFO( BL, "%bl", BX, EBX, ECX_EBX),
REGINFO( AH, "%ah", AX, EAX, EAX_EDX),
REGINFO( DH, "%dh", DX, EDX, EAX_EDX),
REGINFO( CH, "%ch", CX, ECX, ECX_EBX),
REGINFO( BH, "%bh", BX, EBX, ECX_EBX),
REGINFO( AX, "%ax", AL, AH, EAX, EAX_EDX),
REGINFO( DX, "%dx", DL, DH, EDX, EAX_EDX),
REGINFO( CX, "%cx", CL, CH, ECX, ECX_EBX),
REGINFO( BX, "%bx", BL, BH, EBX, ECX_EBX),
REGINFO( SI, "%si", ESI, ESI_EDI),
REGINFO( DI, "%di", EDI, ESI_EDI),
REGINFO( BP, "%bp", EBP),
REGINFO( SP, "%sp", ESP),
REGINFO(EAX, "%eax", AL, AH, AX, EAX_EDX),
REGINFO(EDX, "%edx", DL, DH, DX, EAX_EDX),
REGINFO(ECX, "%ecx", CL, CH, CX, ECX_EBX),
REGINFO(EBX, "%ebx", BL, BH, BX, ECX_EBX),
REGINFO(ESI, "%esi", SI, ESI_EDI),
REGINFO(EDI, "%edi", DI, ESI_EDI),
REGINFO(EBP, "%ebp", BP),
REGINFO(ESP, "%esp", SP),
REGINFO(EAX_EDX, "%eax:%edx", AL, AH, AX, EAX, DL, DH, DX, EDX),
REGINFO(ECX_EBX, "%ecx:%ebx", CL, CH, CX, ECX, BL, BH, BX, EBX),
REGINFO(ESI_EDI, "%esi:%edi", SI, ESI, DI, EDI),
};
#define REGSTORAGE(nr) [nr] = { .type = STOR_REG, .reg = reg_info_table + (nr) }
static struct storage hardreg_storage_table[] = {
REGSTORAGE(AL), REGSTORAGE(DL), REGSTORAGE(CL), REGSTORAGE(BL),
REGSTORAGE(AH), REGSTORAGE(DH), REGSTORAGE(CH), REGSTORAGE(BH),
REGSTORAGE(AX), REGSTORAGE(DX), REGSTORAGE(CX), REGSTORAGE(BX),
REGSTORAGE(SI), REGSTORAGE(DI), REGSTORAGE(BP), REGSTORAGE(SP),
REGSTORAGE(EAX), REGSTORAGE(EDX), REGSTORAGE(ECX), REGSTORAGE(EBX),
REGSTORAGE(ESI), REGSTORAGE(EDI), REGSTORAGE(EBP), REGSTORAGE(ESP),
REGSTORAGE(EAX_EDX), REGSTORAGE(ECX_EBX), REGSTORAGE(ESI_EDI),
};
#define REG_EAX (&hardreg_storage_table[EAX])
#define REG_ECX (&hardreg_storage_table[ECX])
#define REG_EDX (&hardreg_storage_table[EDX])
#define REG_ESP (&hardreg_storage_table[ESP])
#define REG_DL (&hardreg_storage_table[DL])
#define REG_DX (&hardreg_storage_table[DX])
#define REG_AL (&hardreg_storage_table[AL])
#define REG_AX (&hardreg_storage_table[AX])
static DECLARE_BITMAP(regs_in_use, 256);
static inline struct storage * reginfo_reg(struct reg_info *info)
{
return hardreg_storage_table + info->own_regno;
}
static struct storage * get_hardreg(struct storage *reg, int clear)
{
struct reg_info *info = reg->reg;
const unsigned char *aliases;
int regno;
aliases = info->aliases;
while ((regno = *aliases++) != NOREG) {
if (test_bit(regno, regs_in_use))
goto busy;
if (clear)
reg_info_table[regno].contains = NULL;
}
set_bit(info->own_regno, regs_in_use);
return reg;
busy:
fprintf(stderr, "register %s is busy\n", info->name);
if (regno + reg_info_table != info)
fprintf(stderr, " conflicts with %s\n", reg_info_table[regno].name);
exit(1);
}
static void put_reg(struct storage *reg)
{
struct reg_info *info = reg->reg;
int regno = info->own_regno;
if (test_and_clear_bit(regno, regs_in_use))
return;
fprintf(stderr, "freeing already free'd register %s\n", reg_info_table[regno].name);
}
struct regclass {
const char *name;
const unsigned char regs[30];
};
static struct regclass regclass_8 = { "8-bit", { AL, DL, CL, BL, AH, DH, CH, BH }};
static struct regclass regclass_16 = { "16-bit", { AX, DX, CX, BX, SI, DI, BP }};
static struct regclass regclass_32 = { "32-bit", { EAX, EDX, ECX, EBX, ESI, EDI, EBP }};
static struct regclass regclass_64 = { "64-bit", { EAX_EDX, ECX_EBX, ESI_EDI }};
static struct regclass regclass_32_8 = { "32-bit bytes", { EAX, EDX, ECX, EBX }};
static struct regclass *get_regclass_bits(int bits)
{
switch (bits) {
case 8: return ®class_8;
case 16: return ®class_16;
case 64: return ®class_64;
default: return ®class_32;
}
}
static struct regclass *get_regclass(struct expression *expr)
{
return get_regclass_bits(expr->ctype->bit_size);
}
static int register_busy(int regno)
{
if (!test_bit(regno, regs_in_use)) {
struct reg_info *info = reg_info_table + regno;
const unsigned char *regs = info->aliases+1;
while ((regno = *regs) != NOREG) {
regs++;
if (test_bit(regno, regs_in_use))
goto busy;
}
return 0;
}
busy:
return 1;
}
static struct storage *get_reg(struct regclass *class)
{
const unsigned char *regs = class->regs;
int regno;
while ((regno = *regs) != NOREG) {
regs++;
if (register_busy(regno))
continue;
return get_hardreg(hardreg_storage_table + regno, 1);
}
fprintf(stderr, "Ran out of %s registers\n", class->name);
exit(1);
}
static struct storage *get_reg_value(struct storage *value, struct regclass *class)
{
struct reg_info *info;
struct storage *reg;
/* Do we already have it somewhere */
info = value->reg;
if (info && info->contains == value) {
emit_comment("already have register %s", info->name);
return get_hardreg(hardreg_storage_table + info->own_regno, 0);
}
reg = get_reg(class);
emit_move(value, reg, value->ctype, "reload register");
info = reg->reg;
info->contains = value;
value->reg = info;
return reg;
}
static struct storage *temp_from_bits(unsigned int bit_size)
{
return get_reg(get_regclass_bits(bit_size));
}
static inline unsigned int pseudo_offset(struct storage *s)
{
if (s->type != STOR_PSEUDO)
return 123456; /* intentionally bogus value */
return s->offset;
}
static inline unsigned int arg_offset(struct storage *s)
{
if (s->type != STOR_ARG)
return 123456; /* intentionally bogus value */
/* FIXME: this is wrong wrong wrong */
return current_func->stack_size + ((1 + s->idx) * 4);
}
static const char *pretty_offset(int ofs)
{
static char esp_buf[64];
if (ofs)
sprintf(esp_buf, "%d(%%esp)", ofs);
else
strcpy(esp_buf, "(%esp)");
return esp_buf;
}
static void stor_sym_init(struct symbol *sym)
{
struct storage *stor;
struct symbol_private *priv;
priv = calloc(1, sizeof(*priv) + sizeof(*stor));
if (!priv)
die("OOM in stor_sym_init");
stor = (struct storage *) (priv + 1);
priv->addr = stor;
stor->type = STOR_SYM;
stor->sym = sym;
}
static const char *stor_op_name(struct storage *s)
{
static char name[32];
switch (s->type) {
case STOR_PSEUDO:
strcpy(name, pretty_offset((int) pseudo_offset(s)));
break;
case STOR_ARG:
strcpy(name, pretty_offset((int) arg_offset(s)));
break;
case STOR_SYM:
strcpy(name, show_ident(s->sym->ident));
break;
case STOR_REG:
strcpy(name, s->reg->name);
break;
case STOR_VALUE:
sprintf(name, "$%lld", s->value);
break;
case STOR_LABEL:
sprintf(name, "%s.L%d", s->flags & STOR_LABEL_VAL ? "$" : "",
s->label);
break;
case STOR_LABELSYM:
sprintf(name, "%s.LS%p", s->flags & STOR_LABEL_VAL ? "$" : "",
s->labelsym);
break;
}
return name;
}
static struct atom *new_atom(enum atom_type type)
{
struct atom *atom;
atom = calloc(1, sizeof(*atom)); /* TODO: chunked alloc */
if (!atom)
die("nuclear OOM");
atom->type = type;
return atom;
}
static inline void push_cstring(struct function *f, struct string *str,
int label)
{
struct atom *atom;
atom = new_atom(ATOM_CSTR);
atom->string = str;
atom->label = label;
add_ptr_list(&f->str_list, atom); /* note: _not_ atom_list */
}
static inline void push_atom(struct function *f, struct atom *atom)
{
add_ptr_list(&f->atom_list, atom);
}
static void push_text_atom(struct function *f, const char *text)
{
struct atom *atom = new_atom(ATOM_TEXT);
atom->text = strdup(text);
atom->text_len = strlen(text);
push_atom(f, atom);
}
static struct storage *new_storage(enum storage_type type)
{
struct storage *stor;
stor = calloc(1, sizeof(*stor));
if (!stor)
die("OOM in new_storage");
stor->type = type;
return stor;
}
static struct storage *stack_alloc(int n_bytes)
{
struct function *f = current_func;
struct storage *stor;
assert(f != NULL);
stor = new_storage(STOR_PSEUDO);
stor->type = STOR_PSEUDO;
stor->pseudo = f->pseudo_nr;
stor->offset = f->stack_size; /* FIXME: stack req. natural align */
stor->size = n_bytes;
f->stack_size += n_bytes;
f->pseudo_nr++;
add_ptr_list(&f->pseudo_list, stor);
return stor;
}
static struct storage *new_labelsym(struct symbol *sym)
{
struct storage *stor;
stor = new_storage(STOR_LABELSYM);
if (stor) {
stor->flags |= STOR_WANTS_FREE;
stor->labelsym = sym;
}
return stor;
}
static struct storage *new_val(long long value)
{
struct storage *stor;
stor = new_storage(STOR_VALUE);
if (stor) {
stor->flags |= STOR_WANTS_FREE;
stor->value = value;
}
return stor;
}
static int new_label(void)
{
static int label = 0;
return ++label;
}
static void textbuf_push(struct textbuf **buf_p, const char *text)
{
struct textbuf *tmp, *list = *buf_p;
unsigned int text_len = strlen(text);
unsigned int alloc_len = text_len + 1 + sizeof(*list);
tmp = calloc(1, alloc_len);
if (!tmp)
die("OOM on textbuf alloc");
tmp->text = ((void *) tmp) + sizeof(*tmp);
memcpy(tmp->text, text, text_len + 1);
tmp->len = text_len;
/* add to end of list */
if (!list) {
list = tmp;
tmp->prev = tmp;
} else {
tmp->prev = list->prev;
tmp->prev->next = tmp;
list->prev = tmp;
}
tmp->next = list;
*buf_p = list;
}
static void textbuf_emit(struct textbuf **buf_p)
{
struct textbuf *tmp, *list = *buf_p;
while (list) {
tmp = list;
if (tmp->next == tmp)
list = NULL;
else {
tmp->prev->next = tmp->next;
tmp->next->prev = tmp->prev;
list = tmp->next;
}
fputs(tmp->text, stdout);
free(tmp);
}
*buf_p = list;
}
static void insn(const char *insn, struct storage *op1, struct storage *op2,
const char *comment_in)
{
struct function *f = current_func;
struct atom *atom = new_atom(ATOM_INSN);
assert(insn != NULL);
strcpy(atom->insn, insn);
if (comment_in && (*comment_in))
strncpy(atom->comment, comment_in,
sizeof(atom->comment) - 1);
atom->op1 = op1;
atom->op2 = op2;
push_atom(f, atom);
}
static void emit_comment(const char *fmt, ...)
{
struct function *f = current_func;
static char tmpbuf[100] = "\t# ";
va_list args;
int i;
va_start(args, fmt);
i = vsnprintf(tmpbuf+3, sizeof(tmpbuf)-4, fmt, args);
va_end(args);
tmpbuf[i+3] = '\n';
tmpbuf[i+4] = '\0';
push_text_atom(f, tmpbuf);
}
static void emit_label (int label, const char *comment)
{
struct function *f = current_func;
char s[64];
if (!comment)
sprintf(s, ".L%d:\n", label);
else
sprintf(s, ".L%d:\t\t\t\t\t# %s\n", label, comment);
push_text_atom(f, s);
}
static void emit_labelsym (struct symbol *sym, const char *comment)
{
struct function *f = current_func;
char s[64];
if (!comment)
sprintf(s, ".LS%p:\n", sym);
else
sprintf(s, ".LS%p:\t\t\t\t# %s\n", sym, comment);
push_text_atom(f, s);
}
void emit_unit_begin(const char *basename)
{
printf("\t.file\t\"%s\"\n", basename);
}
void emit_unit_end(void)
{
textbuf_emit(&unit_post_text);
printf("\t.ident\t\"sparse silly x86 backend (version %s)\"\n", sparse_version);
}
/* conditionally switch sections */
static void emit_section(const char *s)
{
if (s == current_section)
return;
if (current_section && (!strcmp(s, current_section)))
return;
printf("\t%s\n", s);
current_section = s;
}
static void emit_insn_atom(struct function *f, struct atom *atom)
{
char s[128];
char comment[64];
struct storage *op1 = atom->op1;
struct storage *op2 = atom->op2;
if (atom->comment[0])
sprintf(comment, "\t\t# %s", atom->comment);
else
comment[0] = 0;
if (atom->op2) {
char tmp[16];
strcpy(tmp, stor_op_name(op1));
sprintf(s, "\t%s\t%s, %s%s\n",
atom->insn, tmp, stor_op_name(op2), comment);
} else if (atom->op1)
sprintf(s, "\t%s\t%s%s%s\n",
atom->insn, stor_op_name(op1),
comment[0] ? "\t" : "", comment);
else
sprintf(s, "\t%s\t%s%s\n",
atom->insn,
comment[0] ? "\t\t" : "", comment);
if (write(STDOUT_FILENO, s, strlen(s)) < 0)
die("can't write to stdout");
}
static void emit_atom_list(struct function *f)
{
struct atom *atom;
FOR_EACH_PTR(f->atom_list, atom) {
switch (atom->type) {
case ATOM_TEXT: {
if (write(STDOUT_FILENO, atom->text, atom->text_len) < 0)
die("can't write to stdout");
break;
}
case ATOM_INSN:
emit_insn_atom(f, atom);
break;
case ATOM_CSTR:
assert(0);
break;
}
} END_FOR_EACH_PTR(atom);
}
static void emit_string_list(struct function *f)
{
struct atom *atom;
emit_section(".section\t.rodata");
FOR_EACH_PTR(f->str_list, atom) {
/* FIXME: escape " in string */
printf(".L%d:\n", atom->label);
printf("\t.string\t%s\n", show_string(atom->string));
free(atom);
} END_FOR_EACH_PTR(atom);
}
static void func_cleanup(struct function *f)
{
struct storage *stor;
struct atom *atom;
FOR_EACH_PTR(f->atom_list, atom) {
if ((atom->type == ATOM_TEXT) && (atom->text))
free(atom->text);
if (atom->op1 && (atom->op1->flags & STOR_WANTS_FREE))
free(atom->op1);
if (atom->op2 && (atom->op2->flags & STOR_WANTS_FREE))
free(atom->op2);
free(atom);
} END_FOR_EACH_PTR(atom);
FOR_EACH_PTR(f->pseudo_list, stor) {
free(stor);
} END_FOR_EACH_PTR(stor);
free_ptr_list(&f->pseudo_list);
free(f);
}
/* function prologue */
static void emit_func_pre(struct symbol *sym)
{
struct function *f;
struct symbol *arg;
unsigned int i, argc = 0, alloc_len;
unsigned char *mem;
struct symbol_private *privbase;
struct storage *storage_base;
struct symbol *base_type = sym->ctype.base_type;
FOR_EACH_PTR(base_type->arguments, arg) {
argc++;
} END_FOR_EACH_PTR(arg);
alloc_len =
sizeof(*f) +
(argc * sizeof(struct symbol *)) +
(argc * sizeof(struct symbol_private)) +
(argc * sizeof(struct storage));
mem = calloc(1, alloc_len);
if (!mem)
die("OOM on func info");
f = (struct function *) mem;
mem += sizeof(*f);
f->argv = (struct symbol **) mem;
mem += (argc * sizeof(struct symbol *));
privbase = (struct symbol_private *) mem;
mem += (argc * sizeof(struct symbol_private));
storage_base = (struct storage *) mem;
f->argc = argc;
f->ret_target = new_label();
i = 0;
FOR_EACH_PTR(base_type->arguments, arg) {
f->argv[i] = arg;
arg->aux = &privbase[i];
storage_base[i].type = STOR_ARG;
storage_base[i].idx = i;
privbase[i].addr = &storage_base[i];
i++;
} END_FOR_EACH_PTR(arg);
assert(current_func == NULL);
current_func = f;
}
/* function epilogue */
static void emit_func_post(struct symbol *sym)
{
const char *name = show_ident(sym->ident);
struct function *f = current_func;
int stack_size = f->stack_size;
if (f->str_list)
emit_string_list(f);
/* function prologue */
emit_section(".text");
if ((sym->ctype.modifiers & MOD_STATIC) == 0)
printf(".globl %s\n", name);
printf("\t.type\t%s, @function\n", name);
printf("%s:\n", name);
if (stack_size) {
char pseudo_const[16];
sprintf(pseudo_const, "$%d", stack_size);
printf("\tsubl\t%s, %%esp\n", pseudo_const);
}
/* function epilogue */
/* jump target for 'return' statements */
emit_label(f->ret_target, NULL);
if (stack_size) {
struct storage *val;
val = new_storage(STOR_VALUE);
val->value = (long long) (stack_size);
val->flags = STOR_WANTS_FREE;
insn("addl", val, REG_ESP, NULL);
}
insn("ret", NULL, NULL, NULL);
/* output everything to stdout */
fflush(stdout); /* paranoia; needed? */
emit_atom_list(f);
/* function footer */
name = show_ident(sym->ident);
printf("\t.size\t%s, .-%s\n", name, name);
func_cleanup(f);
current_func = NULL;
}
/* emit object (a.k.a. variable, a.k.a. data) prologue */
static void emit_object_pre(const char *name, unsigned long modifiers,
unsigned long alignment, unsigned int byte_size)
{
if ((modifiers & MOD_STATIC) == 0)
printf(".globl %s\n", name);
emit_section(".data");
if (alignment)
printf("\t.align %lu\n", alignment);
printf("\t.type\t%s, @object\n", name);
printf("\t.size\t%s, %d\n", name, byte_size);
printf("%s:\n", name);
}
/* emit value (only) for an initializer scalar */
static void emit_scalar(struct expression *expr, unsigned int bit_size)
{
const char *type;
long long ll;
assert(expr->type == EXPR_VALUE);
if (expr->value == 0ULL) {
printf("\t.zero\t%d\n", bit_size / 8);
return;
}
ll = (long long) expr->value;
switch (bit_size) {
case 8: type = "byte"; ll = (char) ll; break;
case 16: type = "value"; ll = (short) ll; break;
case 32: type = "long"; ll = (int) ll; break;
case 64: type = "quad"; break;
default: type = NULL; break;
}
assert(type != NULL);
printf("\t.%s\t%lld\n", type, ll);
}
static void emit_global_noinit(const char *name, unsigned long modifiers,
unsigned long alignment, unsigned int byte_size)
{
char s[64];
if (modifiers & MOD_STATIC) {
sprintf(s, "\t.local\t%s\n", name);
textbuf_push(&unit_post_text, s);
}
if (alignment)
sprintf(s, "\t.comm\t%s,%d,%lu\n", name, byte_size, alignment);
else
sprintf(s, "\t.comm\t%s,%d\n", name, byte_size);
textbuf_push(&unit_post_text, s);
}
static int ea_current, ea_last;
static void emit_initializer(struct symbol *sym,
struct expression *expr)
{
int distance = ea_current - ea_last - 1;
if (distance > 0)
printf("\t.zero\t%d\n", (sym->bit_size / 8) * distance);
if (expr->type == EXPR_VALUE) {
struct symbol *base_type = sym->ctype.base_type;
assert(base_type != NULL);
emit_scalar(expr, sym->bit_size / get_expression_value(base_type->array_size));
return;
}
if (expr->type != EXPR_INITIALIZER)
return;
assert(0); /* FIXME */
}
static int sort_array_cmp(const struct expression *a,
const struct expression *b)
{
int a_ofs = 0, b_ofs = 0;
if (a->type == EXPR_POS)
a_ofs = (int) a->init_offset;
if (b->type == EXPR_POS)
b_ofs = (int) b->init_offset;
return a_ofs - b_ofs;
}
/* move to front-end? */
static void sort_array(struct expression *expr)
{
struct expression *entry, **list;
unsigned int elem, sorted, i;
elem = expression_list_size(expr->expr_list);
if (!elem)
return;
list = malloc(sizeof(entry) * elem);
if (!list)
die("OOM in sort_array");
/* this code is no doubt evil and ignores EXPR_INDEX possibly
* to its detriment and other nasty things. improvements
* welcome.
*/
i = 0;
sorted = 0;
FOR_EACH_PTR(expr->expr_list, entry) {
if ((entry->type == EXPR_POS) || (entry->type == EXPR_VALUE)) {
/* add entry to list[], in sorted order */
if (sorted == 0) {
list[0] = entry;
sorted = 1;
} else {
for (i = 0; i < sorted; i++)
if (sort_array_cmp(entry, list[i]) <= 0)
break;
/* If inserting into the middle of list[]
* instead of appending, we memmove.
* This is ugly, but thankfully
* uncommon. Input data with tons of
* entries very rarely have explicit
* offsets. convert to qsort eventually...
*/
if (i != sorted)
memmove(&list[i + 1], &list[i],
(sorted - i) * sizeof(entry));
list[i] = entry;
sorted++;
}
}
} END_FOR_EACH_PTR(entry);
i = 0;
FOR_EACH_PTR(expr->expr_list, entry) {
if ((entry->type == EXPR_POS) || (entry->type == EXPR_VALUE))
*THIS_ADDRESS(entry) = list[i++];
} END_FOR_EACH_PTR(entry);
free(list);
}
static void emit_array(struct symbol *sym)
{
struct symbol *base_type = sym->ctype.base_type;
struct expression *expr = sym->initializer;
struct expression *entry;
assert(base_type != NULL);
stor_sym_init(sym);
ea_last = -1;
emit_object_pre(show_ident(sym->ident), sym->ctype.modifiers,
sym->ctype.alignment,
sym->bit_size / 8);
sort_array(expr);
FOR_EACH_PTR(expr->expr_list, entry) {
if (entry->type == EXPR_VALUE) {
ea_current = 0;
emit_initializer(sym, entry);
ea_last = ea_current;
} else if (entry->type == EXPR_POS) {
ea_current =
entry->init_offset / (base_type->bit_size / 8);
emit_initializer(sym, entry->init_expr);
ea_last = ea_current;
}
} END_FOR_EACH_PTR(entry);
}
void emit_one_symbol(struct symbol *sym)
{
x86_symbol(sym);
}
static void emit_copy(struct storage *dest, struct storage *src,
struct symbol *ctype)
{
struct storage *reg = NULL;
unsigned int bit_size;
/* FIXME: Bitfield copy! */
bit_size = src->size * 8;
if (!bit_size)
bit_size = 32;
if ((src->type == STOR_ARG) && (bit_size < 32))
bit_size = 32;
reg = temp_from_bits(bit_size);
emit_move(src, reg, ctype, "begin copy ..");
bit_size = dest->size * 8;
if (!bit_size)
bit_size = 32;
if ((dest->type == STOR_ARG) && (bit_size < 32))
bit_size = 32;
emit_move(reg, dest, ctype, ".... end copy");
put_reg(reg);
}
static void emit_store(struct expression *dest_expr, struct storage *dest,
struct storage *src, int bits)
{
/* FIXME: Bitfield store! */
printf("\tst.%d\t\tv%d,[v%d]\n", bits, src->pseudo, dest->pseudo);
}
static void emit_scalar_noinit(struct symbol *sym)
{
emit_global_noinit(show_ident(sym->ident),
sym->ctype.modifiers, sym->ctype.alignment,
sym->bit_size / 8);
stor_sym_init(sym);
}
static void emit_array_noinit(struct symbol *sym)
{
emit_global_noinit(show_ident(sym->ident),
sym->ctype.modifiers, sym->ctype.alignment,
get_expression_value(sym->array_size) * (sym->bit_size / 8));
stor_sym_init(sym);
}
static const char *opbits(const char *insn, unsigned int bits)
{
static char opbits_str[32];
char c;
switch (bits) {
case 8: c = 'b'; break;
case 16: c = 'w'; break;
case 32: c = 'l'; break;
case 64: c = 'q'; break;
default: abort(); break;
}
sprintf(opbits_str, "%s%c", insn, c);
return opbits_str;
}
static void emit_move(struct storage *src, struct storage *dest,
struct symbol *ctype, const char *comment)
{
unsigned int bits;
unsigned int is_signed;
unsigned int is_dest = (src->type == STOR_REG);
const char *opname;
if (ctype) {
bits = ctype->bit_size;
is_signed = is_signed_type(ctype);
} else {
bits = 32;
is_signed = 0;
}
/*
* Are we moving from a register to a register?
* Make the new reg to be the "cache".
*/
if ((dest->type == STOR_REG) && (src->type == STOR_REG)) {
struct storage *backing;
reg_reg_move:
if (dest == src)
return;
backing = src->reg->contains;
if (backing) {
/* Is it still valid? */
if (backing->reg != src->reg)
backing = NULL;
else
backing->reg = dest->reg;
}
dest->reg->contains = backing;
insn("mov", src, dest, NULL);
return;
}
/*
* Are we moving to a register from a non-reg?
*
* See if we have the non-reg source already cached
* in a register..
*/
if (dest->type == STOR_REG) {
if (src->reg) {
struct reg_info *info = src->reg;
if (info->contains == src) {
src = reginfo_reg(info);
goto reg_reg_move;
}
}
dest->reg->contains = src;
src->reg = dest->reg;
}
if (src->type == STOR_REG) {
/* We could just mark the register dirty here and do lazy store.. */
src->reg->contains = dest;
dest->reg = src->reg;
}
if ((bits == 8) || (bits == 16)) {
if (is_dest)
opname = "mov";
else
opname = is_signed ? "movsx" : "movzx";
} else
opname = "mov";
insn(opbits(opname, bits), src, dest, comment);
}
static struct storage *emit_compare(struct expression *expr)
{
struct storage *left = x86_expression(expr->left);
struct storage *right = x86_expression(expr->right);
struct storage *reg1, *reg2;
struct storage *new, *val;
const char *opname = NULL;
unsigned int right_bits = expr->right->ctype->bit_size;
switch(expr->op) {
case '<': opname = "setl"; break;
case '>': opname = "setg"; break;
case SPECIAL_LTE:
opname = "setle"; break;
case SPECIAL_GTE:
opname = "setge"; break;
case SPECIAL_EQUAL: opname = "sete"; break;
case SPECIAL_NOTEQUAL: opname = "setne"; break;
case SPECIAL_UNSIGNED_LT:
opname = "setb"; break;
case SPECIAL_UNSIGNED_GT:
opname = "seta"; break;
case SPECIAL_UNSIGNED_LTE:
opname = "setb"; break;
case SPECIAL_UNSIGNED_GTE:
opname = "setae"; break;
default:
assert(0);
break;
}
/* init EDX to 0 */
val = new_storage(STOR_VALUE);
val->flags = STOR_WANTS_FREE;
reg1 = get_reg(®class_32_8);
emit_move(val, reg1, NULL, NULL);
/* move op1 into EAX */
reg2 = get_reg_value(left, get_regclass(expr->left));
/* perform comparison, RHS (op1, right) and LHS (op2, EAX) */
insn(opbits("cmp", right_bits), right, reg2, NULL);
put_reg(reg2);
/* store result of operation, 0 or 1, in DL using SETcc */
insn(opname, byte_reg(reg1), NULL, NULL);
/* finally, store the result (DL) in a new pseudo / stack slot */
new = stack_alloc(4);
emit_move(reg1, new, NULL, "end EXPR_COMPARE");
put_reg(reg1);
return new;
}
static struct storage *emit_value(struct expression *expr)
{
#if 0 /* old and slow way */
struct storage *new = stack_alloc(4);
struct storage *val;
val = new_storage(STOR_VALUE);
val->value = (long long) expr->value;
val->flags = STOR_WANTS_FREE;
insn("movl", val, new, NULL);
return new;
#else
struct storage *val;
val = new_storage(STOR_VALUE);
val->value = (long long) expr->value;
return val; /* FIXME: memory leak */
#endif
}
static struct storage *emit_divide(struct expression *expr, struct storage *left, struct storage *right)
{
struct storage *eax_edx;
struct storage *reg, *new;
struct storage *val = new_storage(STOR_VALUE);
emit_comment("begin DIVIDE");
eax_edx = get_hardreg(hardreg_storage_table + EAX_EDX, 1);
/* init EDX to 0 */
val->flags = STOR_WANTS_FREE;
emit_move(val, REG_EDX, NULL, NULL);
new = stack_alloc(expr->ctype->bit_size / 8);
/* EAX is dividend */
emit_move(left, REG_EAX, NULL, NULL);
reg = get_reg_value(right, ®class_32);
/* perform binop */
insn("div", reg, REG_EAX, NULL);
put_reg(reg);
reg = REG_EAX;
if (expr->op == '%')
reg = REG_EDX;
emit_move(reg, new, NULL, NULL);
put_reg(eax_edx);
emit_comment("end DIVIDE");
return new;
}
static struct storage *emit_binop(struct expression *expr)
{
struct storage *left = x86_expression(expr->left);
struct storage *right = x86_expression(expr->right);
struct storage *new;
struct storage *dest, *src;
const char *opname = NULL;
const char *suffix = NULL;
char opstr[16];
int is_signed;
/* Divides have special register constraints */
if ((expr->op == '/') || (expr->op == '%'))
return emit_divide(expr, left, right);
is_signed = is_signed_type(expr->ctype);
switch (expr->op) {
case '+':
opname = "add";
break;
case '-':
opname = "sub";
break;
case '&':
opname = "and";
break;
case '|':
opname = "or";
break;
case '^':
opname = "xor";
break;
case SPECIAL_LEFTSHIFT:
opname = "shl";
break;
case SPECIAL_RIGHTSHIFT:
if (is_signed)
opname = "sar";
else
opname = "shr";
break;
case '*':
if (is_signed)
opname = "imul";
else
opname = "mul";
break;
case SPECIAL_LOGICAL_AND:
warning(expr->pos, "bogus bitwise and for logical op (should use '2*setne + and' or something)");
opname = "and";
break;
case SPECIAL_LOGICAL_OR:
warning(expr->pos, "bogus bitwise or for logical op (should use 'or + setne' or something)");
opname = "or";
break;
default:
error_die(expr->pos, "unhandled binop '%s'\n", show_special(expr->op));
break;
}
dest = get_reg_value(right, ®class_32);
src = get_reg_value(left, ®class_32);
switch (expr->ctype->bit_size) {
case 8:
suffix = "b";
break;
case 16:
suffix = "w";
break;
case 32:
suffix = "l";
break;
case 64:
suffix = "q"; /* FIXME */
break;
default:
assert(0);
break;
}
snprintf(opstr, sizeof(opstr), "%s%s", opname, suffix);
/* perform binop */
insn(opstr, src, dest, NULL);
put_reg(src);
/* store result in new pseudo / stack slot */
new = stack_alloc(expr->ctype->bit_size / 8);
emit_move(dest, new, NULL, "end EXPR_BINOP");
put_reg(dest);
return new;
}
static int emit_conditional_test(struct storage *val)
{
struct storage *reg;
struct storage *target_val;
int target_false;
/* load result into EAX */
emit_comment("begin if/conditional");
reg = get_reg_value(val, ®class_32);
/* compare result with zero */
insn("test", reg, reg, NULL);
put_reg(reg);
/* create conditional-failed label to jump to */
target_false = new_label();
target_val = new_storage(STOR_LABEL);
target_val->label = target_false;
target_val->flags = STOR_WANTS_FREE;
insn("jz", target_val, NULL, NULL);
return target_false;
}
static int emit_conditional_end(int target_false)
{
struct storage *cond_end_st;
int cond_end;
/* finished generating code for if-true statement.
* add a jump-to-end jump to avoid falling through
* to the if-false statement code.
*/
cond_end = new_label();
cond_end_st = new_storage(STOR_LABEL);
cond_end_st->label = cond_end;
cond_end_st->flags = STOR_WANTS_FREE;
insn("jmp", cond_end_st, NULL, NULL);
/* if we have both if-true and if-false statements,
* the failed-conditional case will fall through to here
*/
emit_label(target_false, NULL);
return cond_end;
}
static void emit_if_conditional(struct statement *stmt)
{
struct storage *val;
int cond_end;
/* emit test portion of conditional */
val = x86_expression(stmt->if_conditional);
cond_end = emit_conditional_test(val);
/* emit if-true statement */
x86_statement(stmt->if_true);
/* emit if-false statement, if present */
if (stmt->if_false) {
cond_end = emit_conditional_end(cond_end);
x86_statement(stmt->if_false);
}
/* end of conditional; jump target for if-true branch */
emit_label(cond_end, "end if");
}
static struct storage *emit_inc_dec(struct expression *expr, int postop)
{
struct storage *addr = x86_address_gen(expr->unop);
struct storage *retval;
char opname[16];
strcpy(opname, opbits(expr->op == SPECIAL_INCREMENT ? "inc" : "dec",
expr->ctype->bit_size));
if (postop) {
struct storage *new = stack_alloc(4);
emit_copy(new, addr, expr->unop->ctype);
retval = new;
} else
retval = addr;
insn(opname, addr, NULL, NULL);
return retval;
}
static struct storage *emit_postop(struct expression *expr)
{
return emit_inc_dec(expr, 1);
}
static struct storage *emit_return_stmt(struct statement *stmt)
{
struct function *f = current_func;
struct expression *expr = stmt->ret_value;
struct storage *val = NULL, *jmplbl;
if (expr && expr->ctype) {
val = x86_expression(expr);
assert(val != NULL);
emit_move(val, REG_EAX, expr->ctype, "return");
}
jmplbl = new_storage(STOR_LABEL);
jmplbl->flags |= STOR_WANTS_FREE;
jmplbl->label = f->ret_target;
insn("jmp", jmplbl, NULL, NULL);
return val;
}
static struct storage *emit_conditional_expr(struct expression *expr)
{
struct storage *cond, *stot = NULL, *stof = NULL;
struct storage *new = stack_alloc(expr->ctype->bit_size / 8);
int target_false, cond_end;
/* evaluate conditional */
cond = x86_expression(expr->conditional);
target_false = emit_conditional_test(cond);
/* handle if-true part of the expression */
stot = x86_expression(expr->cond_true);
emit_copy(new, stot, expr->ctype);
cond_end = emit_conditional_end(target_false);
/* handle if-false part of the expression */
stof = x86_expression(expr->cond_false);
emit_copy(new, stof, expr->ctype);
/* end of conditional; jump target for if-true branch */
emit_label(cond_end, "end conditional");
return new;
}
static struct storage *emit_select_expr(struct expression *expr)
{
struct storage *cond = x86_expression(expr->conditional);
struct storage *stot = x86_expression(expr->cond_true);
struct storage *stof = x86_expression(expr->cond_false);
struct storage *reg_cond, *reg_true, *reg_false;
struct storage *new = stack_alloc(4);
emit_comment("begin SELECT");
reg_cond = get_reg_value(cond, get_regclass(expr->conditional));
reg_true = get_reg_value(stot, get_regclass(expr));
reg_false = get_reg_value(stof, get_regclass(expr));
/*
* Do the actual select: check the conditional for zero,
* move false over true if zero
*/
insn("test", reg_cond, reg_cond, NULL);
insn("cmovz", reg_false, reg_true, NULL);
/* Store it back */
emit_move(reg_true, new, expr->ctype, NULL);
put_reg(reg_cond);
put_reg(reg_true);
put_reg(reg_false);
emit_comment("end SELECT");
return new;
}
static struct storage *emit_symbol_expr_init(struct symbol *sym)
{
struct expression *expr = sym->initializer;
struct symbol_private *priv = sym->aux;
if (priv == NULL) {
priv = calloc(1, sizeof(*priv));
sym->aux = priv;
if (expr == NULL) {
struct storage *new = stack_alloc(4);
fprintf(stderr, "FIXME! no value for symbol %s. creating pseudo %d (stack offset %d)\n",
show_ident(sym->ident),
new->pseudo, new->pseudo * 4);
priv->addr = new;
} else {
priv->addr = x86_expression(expr);
}
}
return priv->addr;
}
static struct storage *emit_string_expr(struct expression *expr)
{
struct function *f = current_func;
int label = new_label();
struct storage *new;
push_cstring(f, expr->string, label);
new = new_storage(STOR_LABEL);
new->label = label;
new->flags = STOR_LABEL_VAL | STOR_WANTS_FREE;
return new;
}
static struct storage *emit_cast_expr(struct expression *expr)
{
struct symbol *old_type, *new_type;
struct storage *op = x86_expression(expr->cast_expression);
int oldbits, newbits;
struct storage *new;
old_type = expr->cast_expression->ctype;
new_type = expr->cast_type;
oldbits = old_type->bit_size;
newbits = new_type->bit_size;
if (oldbits >= newbits)
return op;
emit_move(op, REG_EAX, old_type, "begin cast ..");
new = stack_alloc(newbits / 8);
emit_move(REG_EAX, new, new_type, ".... end cast");
return new;
}
static struct storage *emit_regular_preop(struct expression *expr)
{
struct storage *target = x86_expression(expr->unop);
struct storage *val, *new = stack_alloc(4);
const char *opname = NULL;
switch (expr->op) {
case '!':
val = new_storage(STOR_VALUE);
val->flags = STOR_WANTS_FREE;
emit_move(val, REG_EDX, NULL, NULL);
emit_move(target, REG_EAX, expr->unop->ctype, NULL);
insn("test", REG_EAX, REG_EAX, NULL);
insn("setz", REG_DL, NULL, NULL);
emit_move(REG_EDX, new, expr->unop->ctype, NULL);
break;
case '~':
opname = "not";
case '-':
if (!opname)
opname = "neg";
emit_move(target, REG_EAX, expr->unop->ctype, NULL);
insn(opname, REG_EAX, NULL, NULL);
emit_move(REG_EAX, new, expr->unop->ctype, NULL);
break;
default:
assert(0);
break;
}
return new;
}
static void emit_case_statement(struct statement *stmt)
{
emit_labelsym(stmt->case_label, NULL);
x86_statement(stmt->case_statement);
}
static void emit_switch_statement(struct statement *stmt)
{
struct storage *val = x86_expression(stmt->switch_expression);
struct symbol *sym, *default_sym = NULL;
struct storage *labelsym, *label;
int switch_end = 0;
emit_move(val, REG_EAX, stmt->switch_expression->ctype, "begin case");
/*
* This is where a _real_ back-end would go through the
* cases to decide whether to use a lookup table or a
* series of comparisons etc
*/
FOR_EACH_PTR(stmt->switch_case->symbol_list, sym) {
struct statement *case_stmt = sym->stmt;
struct expression *expr = case_stmt->case_expression;
struct expression *to = case_stmt->case_to;
/* default: */
if (!expr)
default_sym = sym;
/* case NNN: */
else {
struct storage *case_val = new_val(expr->value);
assert (expr->type == EXPR_VALUE);
insn("cmpl", case_val, REG_EAX, NULL);
if (!to) {
labelsym = new_labelsym(sym);
insn("je", labelsym, NULL, NULL);
} else {
int next_test;
label = new_storage(STOR_LABEL);
label->flags |= STOR_WANTS_FREE;
label->label = next_test = new_label();
/* FIXME: signed/unsigned */
insn("jl", label, NULL, NULL);
case_val = new_val(to->value);
insn("cmpl", case_val, REG_EAX, NULL);
/* TODO: implement and use refcounting... */
label = new_storage(STOR_LABEL);
label->flags |= STOR_WANTS_FREE;
label->label = next_test;
/* FIXME: signed/unsigned */
insn("jg", label, NULL, NULL);
labelsym = new_labelsym(sym);
insn("jmp", labelsym, NULL, NULL);
emit_label(next_test, NULL);
}
}
} END_FOR_EACH_PTR(sym);
if (default_sym) {
labelsym = new_labelsym(default_sym);
insn("jmp", labelsym, NULL, "default");
} else {
label = new_storage(STOR_LABEL);
label->flags |= STOR_WANTS_FREE;
label->label = switch_end = new_label();
insn("jmp", label, NULL, "goto end of switch");
}
x86_statement(stmt->switch_statement);
if (stmt->switch_break->used)
emit_labelsym(stmt->switch_break, NULL);
if (switch_end)
emit_label(switch_end, NULL);
}
static void x86_struct_member(struct symbol *sym)
{
printf("\t%s:%d:%ld at offset %ld.%d", show_ident(sym->ident), sym->bit_size, sym->ctype.alignment, sym->offset, sym->bit_offset);
printf("\n");
}
static void x86_symbol(struct symbol *sym)
{
struct symbol *type;
if (!sym)
return;
type = sym->ctype.base_type;
if (!type)
return;
/*
* Show actual implementation information
*/
switch (type->type) {
case SYM_ARRAY:
if (sym->initializer)
emit_array(sym);
else
emit_array_noinit(sym);
break;
case SYM_BASETYPE:
if (sym->initializer) {
emit_object_pre(show_ident(sym->ident),
sym->ctype.modifiers,
sym->ctype.alignment,
sym->bit_size / 8);
emit_scalar(sym->initializer, sym->bit_size);
stor_sym_init(sym);
} else
emit_scalar_noinit(sym);
break;
case SYM_STRUCT:
case SYM_UNION: {
struct symbol *member;
printf(" {\n");
FOR_EACH_PTR(type->symbol_list, member) {
x86_struct_member(member);
} END_FOR_EACH_PTR(member);
printf("}\n");
break;
}
case SYM_FN: {
struct statement *stmt = type->stmt;
if (stmt) {
emit_func_pre(sym);
x86_statement(stmt);
emit_func_post(sym);
}
break;
}
default:
break;
}
if (sym->initializer && (type->type != SYM_BASETYPE) &&
(type->type != SYM_ARRAY)) {
printf(" = \n");
x86_expression(sym->initializer);
}
}
static void x86_symbol_init(struct symbol *sym);
static void x86_symbol_decl(struct symbol_list *syms)
{
struct symbol *sym;
FOR_EACH_PTR(syms, sym) {
x86_symbol_init(sym);
} END_FOR_EACH_PTR(sym);
}
static void loopstk_push(int cont_lbl, int loop_bottom_lbl)
{
struct function *f = current_func;
struct loop_stack *ls;
ls = malloc(sizeof(*ls));
ls->continue_lbl = cont_lbl;
ls->loop_bottom_lbl = loop_bottom_lbl;
ls->next = f->loop_stack;
f->loop_stack = ls;
}
static void loopstk_pop(void)
{
struct function *f = current_func;
struct loop_stack *ls;
assert(f->loop_stack != NULL);
ls = f->loop_stack;
f->loop_stack = f->loop_stack->next;
free(ls);
}
static int loopstk_break(void)
{
return current_func->loop_stack->loop_bottom_lbl;
}
static int loopstk_continue(void)
{
return current_func->loop_stack->continue_lbl;
}
static void emit_loop(struct statement *stmt)
{
struct statement *pre_statement = stmt->iterator_pre_statement;
struct expression *pre_condition = stmt->iterator_pre_condition;
struct statement *statement = stmt->iterator_statement;
struct statement *post_statement = stmt->iterator_post_statement;
struct expression *post_condition = stmt->iterator_post_condition;
int loop_top = 0, loop_bottom, loop_continue;
int have_bottom = 0;
struct storage *val;
loop_bottom = new_label();
loop_continue = new_label();
loopstk_push(loop_continue, loop_bottom);
x86_symbol_decl(stmt->iterator_syms);
x86_statement(pre_statement);
if (!post_condition || post_condition->type != EXPR_VALUE || post_condition->value) {
loop_top = new_label();
emit_label(loop_top, "loop top");
}
if (pre_condition) {
if (pre_condition->type == EXPR_VALUE) {
if (!pre_condition->value) {
struct storage *lbv;
lbv = new_storage(STOR_LABEL);
lbv->label = loop_bottom;
lbv->flags = STOR_WANTS_FREE;
insn("jmp", lbv, NULL, "go to loop bottom");
have_bottom = 1;
}
} else {
struct storage *lbv = new_storage(STOR_LABEL);
lbv->label = loop_bottom;
lbv->flags = STOR_WANTS_FREE;
have_bottom = 1;
val = x86_expression(pre_condition);
emit_move(val, REG_EAX, NULL, "loop pre condition");
insn("test", REG_EAX, REG_EAX, NULL);
insn("jz", lbv, NULL, NULL);
}
}
x86_statement(statement);
if (stmt->iterator_continue->used)
emit_label(loop_continue, "'continue' iterator");
x86_statement(post_statement);
if (!post_condition) {
struct storage *lbv = new_storage(STOR_LABEL);
lbv->label = loop_top;
lbv->flags = STOR_WANTS_FREE;
insn("jmp", lbv, NULL, "go to loop top");
} else if (post_condition->type == EXPR_VALUE) {
if (post_condition->value) {
struct storage *lbv = new_storage(STOR_LABEL);
lbv->label = loop_top;
lbv->flags = STOR_WANTS_FREE;
insn("jmp", lbv, NULL, "go to loop top");
}
} else {
struct storage *lbv = new_storage(STOR_LABEL);
lbv->label = loop_top;
lbv->flags = STOR_WANTS_FREE;
val = x86_expression(post_condition);
emit_move(val, REG_EAX, NULL, "loop post condition");
insn("test", REG_EAX, REG_EAX, NULL);
insn("jnz", lbv, NULL, NULL);
}
if (have_bottom || stmt->iterator_break->used)
emit_label(loop_bottom, "loop bottom");
loopstk_pop();
}
/*
* Print out a statement
*/
static struct storage *x86_statement(struct statement *stmt)
{
if (!stmt)
return NULL;
switch (stmt->type) {
default:
return NULL;
case STMT_RETURN:
return emit_return_stmt(stmt);
case STMT_DECLARATION:
x86_symbol_decl(stmt->declaration);
break;
case STMT_COMPOUND: {
struct statement *s;
struct storage *last = NULL;
FOR_EACH_PTR(stmt->stmts, s) {
last = x86_statement(s);
} END_FOR_EACH_PTR(s);
return last;
}
case STMT_EXPRESSION:
return x86_expression(stmt->expression);
case STMT_IF:
emit_if_conditional(stmt);
return NULL;
case STMT_CASE:
emit_case_statement(stmt);
break;
case STMT_SWITCH:
emit_switch_statement(stmt);
break;
case STMT_ITERATOR:
emit_loop(stmt);
break;
case STMT_NONE:
break;
case STMT_LABEL:
printf(".L%p:\n", stmt->label_identifier);
x86_statement(stmt->label_statement);
break;
case STMT_GOTO:
if (stmt->goto_expression) {
struct storage *val = x86_expression(stmt->goto_expression);
printf("\tgoto *v%d\n", val->pseudo);
} else if (!strcmp("break", show_ident(stmt->goto_label->ident))) {
struct storage *lbv = new_storage(STOR_LABEL);
lbv->label = loopstk_break();
lbv->flags = STOR_WANTS_FREE;
insn("jmp", lbv, NULL, "'break'; go to loop bottom");
} else if (!strcmp("continue", show_ident(stmt->goto_label->ident))) {
struct storage *lbv = new_storage(STOR_LABEL);
lbv->label = loopstk_continue();
lbv->flags = STOR_WANTS_FREE;
insn("jmp", lbv, NULL, "'continue'; go to loop top");
} else {
struct storage *labelsym = new_labelsym(stmt->goto_label);
insn("jmp", labelsym, NULL, NULL);
}
break;
case STMT_ASM:
printf("\tasm( .... )\n");
break;
}
return NULL;
}
static struct storage *x86_call_expression(struct expression *expr)
{
struct function *f = current_func;
struct symbol *direct;
struct expression *arg, *fn;
struct storage *retval, *fncall;
int framesize;
char s[64];
if (!expr->ctype) {
warning(expr->pos, "\tcall with no type!");
return NULL;
}
framesize = 0;
FOR_EACH_PTR_REVERSE(expr->args, arg) {
struct storage *new = x86_expression(arg);
int size = arg->ctype->bit_size;
/*
* FIXME: i386 SysV ABI dictates that values
* smaller than 32 bits should be placed onto
* the stack as 32-bit objects. We should not
* blindly do a 32-bit push on objects smaller
* than 32 bits.
*/
if (size < 32)
size = 32;
insn("pushl", new, NULL,
!framesize ? "begin function call" : NULL);
framesize += bits_to_bytes(size);
} END_FOR_EACH_PTR_REVERSE(arg);
fn = expr->fn;
/* Remove dereference, if any */
direct = NULL;
if (fn->type == EXPR_PREOP) {
if (fn->unop->type == EXPR_SYMBOL) {
struct symbol *sym = fn->unop->symbol;
if (sym->ctype.base_type->type == SYM_FN)
direct = sym;
}
}
if (direct) {
struct storage *direct_stor = new_storage(STOR_SYM);
direct_stor->flags |= STOR_WANTS_FREE;
direct_stor->sym = direct;
insn("call", direct_stor, NULL, NULL);
} else {
fncall = x86_expression(fn);
emit_move(fncall, REG_EAX, fn->ctype, NULL);
strcpy(s, "\tcall\t*%eax\n");
push_text_atom(f, s);
}
/* FIXME: pay attention to BITS_IN_POINTER */
if (framesize) {
struct storage *val = new_storage(STOR_VALUE);
val->value = (long long) framesize;
val->flags = STOR_WANTS_FREE;
insn("addl", val, REG_ESP, NULL);
}
retval = stack_alloc(4);
emit_move(REG_EAX, retval, NULL, "end function call");
return retval;
}
static struct storage *x86_address_gen(struct expression *expr)
{
struct function *f = current_func;
struct storage *addr;
struct storage *new;
char s[32];
addr = x86_expression(expr->unop);
if (expr->unop->type == EXPR_SYMBOL)
return addr;
emit_move(addr, REG_EAX, NULL, "begin deref ..");
/* FIXME: operand size */
strcpy(s, "\tmovl\t(%eax), %ecx\n");
push_text_atom(f, s);
new = stack_alloc(4);
emit_move(REG_ECX, new, NULL, ".... end deref");
return new;
}
static struct storage *x86_assignment(struct expression *expr)
{
struct expression *target = expr->left;
struct storage *val, *addr;
if (!expr->ctype)
return NULL;
val = x86_expression(expr->right);
addr = x86_address_gen(target);
switch (val->type) {
/* copy, where both operands are memory */
case STOR_PSEUDO:
case STOR_ARG:
emit_copy(addr, val, expr->ctype);
break;
/* copy, one or zero operands are memory */
case STOR_REG:
case STOR_SYM:
case STOR_VALUE:
case STOR_LABEL:
emit_move(val, addr, expr->left->ctype, NULL);
break;
case STOR_LABELSYM:
assert(0);
break;
}
return val;
}
static int x86_initialization(struct symbol *sym, struct expression *expr)
{
struct storage *val, *addr;
int bits;
if (!expr->ctype)
return 0;
bits = expr->ctype->bit_size;
val = x86_expression(expr);
addr = x86_symbol_expr(sym);
// FIXME! The "target" expression is for bitfield store information.
// Leave it NULL, which works fine.
emit_store(NULL, addr, val, bits);
return 0;
}
static struct storage *x86_access(struct expression *expr)
{
return x86_address_gen(expr);
}
static struct storage *x86_preop(struct expression *expr)
{
/*
* '*' is an lvalue access, and is fundamentally different
* from an arithmetic operation. Maybe it should have an
* expression type of its own..
*/
if (expr->op == '*')
return x86_access(expr);
if (expr->op == SPECIAL_INCREMENT || expr->op == SPECIAL_DECREMENT)
return emit_inc_dec(expr, 0);
return emit_regular_preop(expr);
}
static struct storage *x86_symbol_expr(struct symbol *sym)
{
struct storage *new = stack_alloc(4);
if (sym->ctype.modifiers & (MOD_TOPLEVEL | MOD_EXTERN | MOD_STATIC)) {
printf("\tmovi.%d\t\tv%d,$%s\n", bits_in_pointer, new->pseudo, show_ident(sym->ident));
return new;
}
if (sym->ctype.modifiers & MOD_ADDRESSABLE) {
printf("\taddi.%d\t\tv%d,vFP,$%lld\n", bits_in_pointer, new->pseudo, 0LL);
return new;
}
printf("\taddi.%d\t\tv%d,vFP,$offsetof(%s:%p)\n", bits_in_pointer, new->pseudo, show_ident(sym->ident), sym);
return new;
}
static void x86_symbol_init(struct symbol *sym)
{
struct symbol_private *priv = sym->aux;
struct expression *expr = sym->initializer;
struct storage *new;
if (expr)
new = x86_expression(expr);
else
new = stack_alloc(sym->bit_size / 8);
if (!priv) {
priv = calloc(1, sizeof(*priv));
sym->aux = priv;
/* FIXME: leak! we don't free... */
/* (well, we don't free symbols either) */
}
priv->addr = new;
}
static struct storage *x86_label_expr(struct expression *expr)
{
struct storage *new = stack_alloc(4);
printf("\tmovi.%d\t\tv%d,.L%p\n", bits_in_pointer, new->pseudo, expr->label_symbol);
return new;
}
static struct storage *x86_statement_expr(struct expression *expr)
{
return x86_statement(expr->statement);
}
static int x86_position_expr(struct expression *expr, struct symbol *base)
{
struct storage *new = x86_expression(expr->init_expr);
struct symbol *ctype = expr->init_expr->ctype;
printf("\tinsert v%d at [%d:%d] of %s\n", new->pseudo,
expr->init_offset, ctype->bit_offset,
show_ident(base->ident));
return 0;
}
static void x86_initializer_expr(struct expression *expr, struct symbol *ctype)
{
struct expression *entry;
FOR_EACH_PTR(expr->expr_list, entry) {
// Nested initializers have their positions already
// recursively calculated - just output them too
if (entry->type == EXPR_INITIALIZER) {
x86_initializer_expr(entry, ctype);
continue;
}
// Ignore initializer indexes and identifiers - the
// evaluator has taken them into account
if (entry->type == EXPR_IDENTIFIER || entry->type == EXPR_INDEX)
continue;
if (entry->type == EXPR_POS) {
x86_position_expr(entry, ctype);
continue;
}
x86_initialization(ctype, entry);
} END_FOR_EACH_PTR(entry);
}
/*
* Print out an expression. Return the pseudo that contains the
* variable.
*/
static struct storage *x86_expression(struct expression *expr)
{
if (!expr)
return NULL;
if (!expr->ctype) {
struct position *pos = &expr->pos;
printf("\tno type at %s:%d:%d\n",
stream_name(pos->stream),
pos->line, pos->pos);
return NULL;
}
switch (expr->type) {
default:
return NULL;
case EXPR_CALL:
return x86_call_expression(expr);
case EXPR_ASSIGNMENT:
return x86_assignment(expr);
case EXPR_COMPARE:
return emit_compare(expr);
case EXPR_BINOP:
case EXPR_COMMA:
case EXPR_LOGICAL:
return emit_binop(expr);
case EXPR_PREOP:
return x86_preop(expr);
case EXPR_POSTOP:
return emit_postop(expr);
case EXPR_SYMBOL:
return emit_symbol_expr_init(expr->symbol);
case EXPR_DEREF:
case EXPR_SIZEOF:
case EXPR_ALIGNOF:
warning(expr->pos, "invalid expression after evaluation");
return NULL;
case EXPR_CAST:
case EXPR_FORCE_CAST:
case EXPR_IMPLIED_CAST:
return emit_cast_expr(expr);
case EXPR_VALUE:
return emit_value(expr);
case EXPR_STRING:
return emit_string_expr(expr);
case EXPR_INITIALIZER:
x86_initializer_expr(expr, expr->ctype);
return NULL;
case EXPR_SELECT:
return emit_select_expr(expr);
case EXPR_CONDITIONAL:
return emit_conditional_expr(expr);
case EXPR_STATEMENT:
return x86_statement_expr(expr);
case EXPR_LABEL:
return x86_label_expr(expr);
// None of these should exist as direct expressions: they are only
// valid as sub-expressions of initializers.
case EXPR_POS:
warning(expr->pos, "unable to show plain initializer position expression");
return NULL;
case EXPR_IDENTIFIER:
warning(expr->pos, "unable to show identifier expression");
return NULL;
case EXPR_INDEX:
warning(expr->pos, "unable to show index expression");
return NULL;
case EXPR_TYPE:
warning(expr->pos, "unable to show type expression");
return NULL;
case EXPR_FVALUE:
warning(expr->pos, "floating point support is not implemented");
return NULL;
}
return NULL;
}
sparse-0.6.4/compile.c 0000664 0000000 0000000 00000004617 14115310122 0014610 0 ustar 00root root 0000000 0000000 /*
* Example trivial client program that uses the sparse library
* and x86 backend.
*
* Copyright (C) 2003 Transmeta Corp.
* 2003 Linus Torvalds
* Copyright 2003 Jeff Garzik
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
#include
#include
#include
#include
#include
#include
#include
#include "lib.h"
#include "allocate.h"
#include "token.h"
#include "parse.h"
#include "symbol.h"
#include "expression.h"
#include "compile.h"
static void clean_up_symbols(struct symbol_list *list)
{
struct symbol *sym;
FOR_EACH_PTR(list, sym) {
expand_symbol(sym);
emit_one_symbol(sym);
} END_FOR_EACH_PTR(sym);
}
int main(int argc, char **argv)
{
char *file;
struct string_list *filelist = NULL;
bits_in_bool = 8;
clean_up_symbols(sparse_initialize(argc, argv, &filelist));
FOR_EACH_PTR(filelist, file) {
struct symbol_list *list;
const char *basename = strrchr(file, '/');
basename = basename ? basename+1 : file;
list = sparse(file);
// Do type evaluation and simplification
emit_unit_begin(basename);
clean_up_symbols(list);
emit_unit_end();
} END_FOR_EACH_PTR(file);
#if 0
// And show the allocation statistics
show_ident_alloc();
show_token_alloc();
show_symbol_alloc();
show_expression_alloc();
show_statement_alloc();
show_string_alloc();
show_bytes_alloc();
#endif
return 0;
}
sparse-0.6.4/compile.h 0000664 0000000 0000000 00000000307 14115310122 0014605 0 ustar 00root root 0000000 0000000 #ifndef COMPILE_H
#define COMPILE_H
struct symbol;
extern void emit_one_symbol(struct symbol *);
extern void emit_unit_begin(const char *);
extern void emit_unit_end(void);
#endif /* COMPILE_H */
sparse-0.6.4/cse.c 0000664 0000000 0000000 00000020174 14115310122 0013726 0 ustar 00root root 0000000 0000000 /*
* CSE - walk the linearized instruction flow, and
* see if we can simplify it and apply CSE on it.
*
* Copyright (C) 2004 Linus Torvalds
*/
#include
#include
#include
#include
#include
#include
#include "parse.h"
#include "expression.h"
#include "flowgraph.h"
#include "linearize.h"
#include "flow.h"
#include "cse.h"
#define INSN_HASH_SIZE 256
static struct instruction_list *insn_hash_table[INSN_HASH_SIZE];
static int phi_compare(pseudo_t phi1, pseudo_t phi2)
{
const struct instruction *def1 = phi1->def;
const struct instruction *def2 = phi2->def;
if (def1->src1 != def2->src1)
return def1->src1 < def2->src1 ? -1 : 1;
if (def1->bb != def2->bb)
return def1->bb < def2->bb ? -1 : 1;
return 0;
}
void cse_collect(struct instruction *insn)
{
unsigned long hash;
hash = (insn->opcode << 3) + (insn->size >> 3);
switch (insn->opcode) {
case OP_SEL:
hash += hashval(insn->src3);
/* Fall through */
/* Binary arithmetic */
case OP_ADD: case OP_SUB:
case OP_MUL:
case OP_DIVU: case OP_DIVS:
case OP_MODU: case OP_MODS:
case OP_SHL:
case OP_LSR: case OP_ASR:
case OP_AND: case OP_OR:
/* Binary logical */
case OP_XOR:
/* Binary comparison */
case OP_SET_EQ: case OP_SET_NE:
case OP_SET_LE: case OP_SET_GE:
case OP_SET_LT: case OP_SET_GT:
case OP_SET_B: case OP_SET_A:
case OP_SET_BE: case OP_SET_AE:
/* floating-point arithmetic & comparison */
case OP_FPCMP ... OP_FPCMP_END:
case OP_FADD:
case OP_FSUB:
case OP_FMUL:
case OP_FDIV:
hash += hashval(insn->src2);
/* Fall through */
/* Unary */
case OP_NOT: case OP_NEG:
case OP_FNEG:
case OP_SYMADDR:
hash += hashval(insn->src1);
break;
case OP_LABEL:
hash += hashval(insn->bb_true);
break;
case OP_SETVAL:
hash += hashval(insn->val);
break;
case OP_SETFVAL:
hash += hashval(insn->fvalue);
break;
case OP_SEXT: case OP_ZEXT:
case OP_TRUNC:
case OP_PTRCAST:
case OP_UTPTR: case OP_PTRTU:
if (!insn->orig_type || insn->orig_type->bit_size < 0)
return;
hash += hashval(insn->src);
// Note: see corresponding line in insn_compare()
hash += hashval(insn->orig_type->bit_size);
break;
/* Other */
case OP_PHI: {
pseudo_t phi;
FOR_EACH_PTR(insn->phi_list, phi) {
struct instruction *def;
if (phi == VOID || !phi->def)
continue;
def = phi->def;
hash += hashval(def->src1);
hash += hashval(def->bb);
} END_FOR_EACH_PTR(phi);
break;
}
default:
/*
* Nothing to do, don't even bother hashing them,
* we're not going to try to CSE them
*/
return;
}
hash += hash >> 16;
hash &= INSN_HASH_SIZE-1;
add_instruction(insn_hash_table + hash, insn);
}
/* Compare two (sorted) phi-lists */
static int phi_list_compare(struct pseudo_list *l1, struct pseudo_list *l2)
{
pseudo_t phi1, phi2;
PREPARE_PTR_LIST(l1, phi1);
PREPARE_PTR_LIST(l2, phi2);
for (;;) {
int cmp;
while (phi1 && (phi1 == VOID || !phi1->def))
NEXT_PTR_LIST(phi1);
while (phi2 && (phi2 == VOID || !phi2->def))
NEXT_PTR_LIST(phi2);
if (!phi1)
return phi2 ? -1 : 0;
if (!phi2)
return phi1 ? 1 : 0;
cmp = phi_compare(phi1, phi2);
if (cmp)
return cmp;
NEXT_PTR_LIST(phi1);
NEXT_PTR_LIST(phi2);
}
/* Not reached, but we need to make the nesting come out right */
FINISH_PTR_LIST(phi2);
FINISH_PTR_LIST(phi1);
}
static int insn_compare(const void *_i1, const void *_i2)
{
const struct instruction *i1 = _i1;
const struct instruction *i2 = _i2;
int size1, size2;
int diff;
if (i1->opcode != i2->opcode)
return i1->opcode < i2->opcode ? -1 : 1;
switch (i1->opcode) {
/* commutative binop */
case OP_ADD:
case OP_MUL:
case OP_AND: case OP_OR:
case OP_XOR:
case OP_SET_EQ: case OP_SET_NE:
if (i1->src1 == i2->src2 && i1->src2 == i2->src1)
return 0;
goto case_binops;
case OP_SEL:
if (i1->src3 != i2->src3)
return i1->src3 < i2->src3 ? -1 : 1;
/* Fall-through to binops */
/* Binary arithmetic */
case OP_SUB:
case OP_DIVU: case OP_DIVS:
case OP_MODU: case OP_MODS:
case OP_SHL:
case OP_LSR: case OP_ASR:
/* Binary comparison */
case OP_SET_LE: case OP_SET_GE:
case OP_SET_LT: case OP_SET_GT:
case OP_SET_B: case OP_SET_A:
case OP_SET_BE: case OP_SET_AE:
/* floating-point arithmetic */
case OP_FPCMP ... OP_FPCMP_END:
case OP_FADD:
case OP_FSUB:
case OP_FMUL:
case OP_FDIV:
case_binops:
if (i1->src2 != i2->src2)
return i1->src2 < i2->src2 ? -1 : 1;
/* Fall through to unops */
/* Unary */
case OP_NOT: case OP_NEG:
case OP_FNEG:
case OP_SYMADDR:
if (i1->src1 != i2->src1)
return i1->src1 < i2->src1 ? -1 : 1;
break;
case OP_LABEL:
if (i1->bb_true != i2->bb_true)
return i1->bb_true < i2->bb_true ? -1 : 1;
break;
case OP_SETVAL:
if (i1->val != i2->val)
return i1->val < i2->val ? -1 : 1;
break;
case OP_SETFVAL:
diff = memcmp(&i1->fvalue, &i2->fvalue, sizeof(i1->fvalue));
if (diff)
return diff;
break;
/* Other */
case OP_PHI:
return phi_list_compare(i1->phi_list, i2->phi_list);
case OP_SEXT: case OP_ZEXT:
case OP_TRUNC:
case OP_PTRCAST:
case OP_UTPTR: case OP_PTRTU:
if (i1->src != i2->src)
return i1->src < i2->src ? -1 : 1;
// Note: if it can be guaranted that identical ->src
// implies identical orig_type->bit_size, then this
// test and the hashing of the original size in
// cse_collect() are not needed.
// It must be generaly true but it isn't guaranted (yet).
size1 = i1->orig_type->bit_size;
size2 = i2->orig_type->bit_size;
if (size1 != size2)
return size1 < size2 ? -1 : 1;
break;
default:
warning(i1->pos, "bad instruction on hash chain");
}
if (i1->size != i2->size)
return i1->size < i2->size ? -1 : 1;
return 0;
}
static void sort_instruction_list(struct instruction_list **list)
{
sort_list((struct ptr_list **)list , insn_compare);
}
static struct instruction * cse_one_instruction(struct instruction *insn, struct instruction *def)
{
convert_instruction_target(insn, def->target);
kill_instruction(insn);
repeat_phase |= REPEAT_CSE;
return def;
}
static struct basic_block *trivial_common_parent(struct basic_block *bb1, struct basic_block *bb2)
{
struct basic_block *parent;
if (bb_list_size(bb1->parents) != 1)
return NULL;
parent = first_basic_block(bb1->parents);
if (bb_list_size(bb2->parents) != 1)
return NULL;
if (first_basic_block(bb2->parents) != parent)
return NULL;
return parent;
}
static inline void remove_instruction(struct instruction_list **list, struct instruction *insn, int count)
{
delete_ptr_list_entry((struct ptr_list **)list, insn, count);
}
static struct instruction * try_to_cse(struct entrypoint *ep, struct instruction *i1, struct instruction *i2)
{
struct basic_block *b1, *b2, *common;
/*
* OK, i1 and i2 are the same instruction, modulo "target".
* We should now see if we can combine them.
*/
b1 = i1->bb;
b2 = i2->bb;
/*
* Currently we only handle the uninteresting degenerate case where
* the CSE is inside one basic-block.
*/
if (b1 == b2) {
struct instruction *insn;
FOR_EACH_PTR(b1->insns, insn) {
if (insn == i1)
return cse_one_instruction(i2, i1);
if (insn == i2)
return cse_one_instruction(i1, i2);
} END_FOR_EACH_PTR(insn);
warning(b1->pos, "Whaa? unable to find CSE instructions");
return i1;
}
if (domtree_dominates(b1, b2))
return cse_one_instruction(i2, i1);
if (domtree_dominates(b2, b1))
return cse_one_instruction(i1, i2);
/* No direct dominance - but we could try to find a common ancestor.. */
common = trivial_common_parent(b1, b2);
if (common) {
i1 = cse_one_instruction(i2, i1);
remove_instruction(&b1->insns, i1, 1);
insert_last_instruction(common, i1);
} else {
i1 = i2;
}
return i1;
}
void cse_eliminate(struct entrypoint *ep)
{
int i;
for (i = 0; i < INSN_HASH_SIZE; i++) {
struct instruction_list **list = insn_hash_table + i;
if (*list) {
if (instruction_list_size(*list) > 1) {
struct instruction *insn, *last;
sort_instruction_list(list);
last = NULL;
FOR_EACH_PTR(*list, insn) {
if (!insn->bb)
continue;
if (last) {
if (!insn_compare(last, insn))
insn = try_to_cse(ep, last, insn);
}
last = insn;
} END_FOR_EACH_PTR(insn);
}
free_ptr_list(list);
}
}
}
sparse-0.6.4/cse.h 0000664 0000000 0000000 00000000260 14115310122 0013725 0 ustar 00root root 0000000 0000000 #ifndef CSE_H
#define CSE_H
struct instruction;
struct entrypoint;
/* cse.c */
void cse_collect(struct instruction *insn);
void cse_eliminate(struct entrypoint *ep);
#endif
sparse-0.6.4/ctags.c 0000664 0000000 0000000 00000013141 14115310122 0014251 0 ustar 00root root 0000000 0000000 /*
* Sparse Ctags
*
* Ctags generates tags from preprocessing results.
*
* Copyright (C) 2006 Christopher Li
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include
#include
#include
#include
#include
#include "parse.h"
#include "scope.h"
static struct symbol_list *taglist = NULL;
static void examine_symbol(struct symbol *sym);
#define MAX(_x,_y) ((_x) > (_y) ? (_x) : (_y))
static int cmp_sym(const void *m, const void *n)
{
const struct ident *a = ((const struct symbol *)m)->ident;
const struct ident *b = ((const struct symbol *)n)->ident;
int ret = strncmp(a->name, b->name, MAX(a->len, b->len));
if (!ret) {
const struct position a_pos = ((const struct symbol *)m)->pos;
const struct position b_pos = ((const struct symbol *)n)->pos;
ret = strcmp(stream_name(a_pos.stream),
stream_name(b_pos.stream));
if (!ret)
return a_pos.line < b_pos.line;
}
return ret;
}
static void show_tag_header(FILE *fp)
{
fprintf(fp, "!_TAG_FILE_FORMAT\t2\t/extended format; --format=1 will not append ;\" to lines/\n");
fprintf(fp, "!_TAG_FILE_SORTED\t0\t/0=unsorted, 1=sorted, 2=foldcase/\n");
fprintf(fp, "!_TAG_PROGRAM_AUTHOR\tChristopher Li\t/sparse@chrisli.org/\n");
fprintf(fp, "!_TAG_PROGRAM_NAME\tSparse Ctags\t//\n");
fprintf(fp, "!_TAG_PROGRAM_URL\thttp://www.kernel.org/pub/software/devel/sparse/\t/official site/\n");
fprintf(fp, "!_TAG_PROGRAM_VERSION\t0.01\t//\n");
}
static inline void show_symbol_tag(FILE *fp, struct symbol *sym)
{
fprintf(fp, "%s\t%s\t%d;\"\t%c\tfile:\n", show_ident(sym->ident),
stream_name(sym->pos.stream), sym->pos.line, (int)sym->kind);
}
static void show_tags(struct symbol_list *list)
{
struct symbol *sym;
struct ident *ident = NULL;
struct position pos = {};
static const char *filename;
FILE *fp;
if (!list)
return;
fp = fopen("tags", "w");
if (!fp) {
perror("open tags file");
return;
}
show_tag_header(fp);
FOR_EACH_PTR(list, sym) {
if (ident == sym->ident && pos.line == sym->pos.line &&
!strcmp(filename, stream_name(sym->pos.stream)))
continue;
show_symbol_tag(fp, sym);
ident = sym->ident;
pos = sym->pos;
filename = stream_name(sym->pos.stream);
} END_FOR_EACH_PTR(sym);
fclose(fp);
}
static inline void add_tag(struct symbol *sym)
{
if (sym->ident && !sym->visited) {
sym->visited = 1;
add_symbol(&taglist, sym);
}
}
static inline void examine_members(struct symbol_list *list)
{
struct symbol *sym;
FOR_EACH_PTR(list, sym) {
sym->kind = 'm';
examine_symbol(sym);
} END_FOR_EACH_PTR(sym);
}
static void examine_symbol(struct symbol *sym)
{
struct symbol *base = sym;
if (!sym || sym->visited)
return;
if (sym->ident && sym->ident->reserved)
return;
if (sym->type == SYM_KEYWORD || sym->type == SYM_PREPROCESSOR)
return;
add_tag(sym);
base = sym->ctype.base_type;
switch (sym->type) {
case SYM_NODE:
if (base && base->type == SYM_FN)
sym->kind = 'f';
examine_symbol(base);
break;
case SYM_STRUCT:
sym->kind = 's';
examine_members(sym->symbol_list);
break;
case SYM_UNION:
sym->kind = 'u';
examine_members(sym->symbol_list);
break;
case SYM_ENUM:
sym->kind = 'e';
case SYM_PTR:
case SYM_TYPEOF:
case SYM_BITFIELD:
case SYM_FN:
case SYM_ARRAY:
examine_symbol(sym->ctype.base_type);
break;
case SYM_BASETYPE:
break;
default:
die("unknown symbol %s namespace:%d type:%d\n", show_ident(sym->ident),
sym->namespace, sym->type);
}
if (!sym->kind)
sym->kind = 'v';
return;
}
static void examine_namespace(struct symbol *sym)
{
if (sym->visited)
return;
if (sym->ident && sym->ident->reserved)
return;
switch(sym->namespace) {
case NS_KEYWORD:
case NS_PREPROCESSOR:
return;
case NS_LABEL:
sym->kind = 'l';
break;
case NS_MACRO:
case NS_UNDEF:
sym->kind = 'd';
break;
case NS_TYPEDEF:
sym->kind = 't';
case NS_SYMBOL:
case NS_STRUCT:
examine_symbol(sym);
break;
default:
die("unknown namespace %d symbol:%s type:%d\n", sym->namespace,
show_ident(sym->ident), sym->type);
}
add_tag(sym);
}
static inline void examine_symbol_list(struct symbol_list *list)
{
struct symbol *sym;
if (!list)
return;
FOR_EACH_PTR(list, sym) {
examine_namespace(sym);
} END_FOR_EACH_PTR(sym);
}
int main(int argc, char **argv)
{
struct string_list *filelist = NULL;
char *file;
examine_symbol_list(sparse_initialize(argc, argv, &filelist));
FOR_EACH_PTR(filelist, file) {
sparse(file);
examine_symbol_list(file_scope->symbols);
} END_FOR_EACH_PTR(file);
examine_symbol_list(global_scope->symbols);
sort_list((struct ptr_list **)&taglist, cmp_sym);
show_tags(taglist);
return 0;
}
sparse-0.6.4/dissect.c 0000664 0000000 0000000 00000036370 14115310122 0014617 0 ustar 00root root 0000000 0000000 /*
* sparse/dissect.c
*
* Started by Oleg Nesterov
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "dissect.h"
#define U_VOID 0x00
#define U_SELF ((1 << U_SHIFT) - 1)
#define U_MASK (U_R_VAL | U_W_VAL | U_R_AOF)
#define DO_LIST(l__, p__, expr__) \
do { \
typeof(l__->list[0]) p__; \
FOR_EACH_PTR(l__, p__) \
expr__; \
END_FOR_EACH_PTR(p__); \
} while (0)
#define DO_2_LIST(l1__,l2__, p1__,p2__, expr__) \
do { \
typeof(l1__->list[0]) p1__; \
typeof(l2__->list[0]) p2__; \
PREPARE_PTR_LIST(l1__, p1__); \
FOR_EACH_PTR(l2__, p2__) \
expr__; \
NEXT_PTR_LIST(p1__); \
END_FOR_EACH_PTR(p2__); \
FINISH_PTR_LIST(p1__); \
} while (0)
typedef unsigned usage_t;
struct symbol *dissect_ctx;
static struct reporter *reporter;
static void do_sym_list(struct symbol_list *list);
static struct symbol
*base_type(struct symbol *sym),
*do_initializer(struct symbol *type, struct expression *expr),
*do_expression(usage_t mode, struct expression *expr),
*do_statement(usage_t mode, struct statement *stmt);
static inline int is_ptr(struct symbol *type)
{
return type->type == SYM_PTR || type->type == SYM_ARRAY;
}
static inline usage_t u_rval(usage_t mode)
{
return mode & (U_R_VAL | (U_MASK << U_SHIFT))
? U_R_VAL : 0;
}
static inline usage_t u_addr(usage_t mode)
{
return mode = mode & U_MASK
? U_R_AOF | (mode & U_W_AOF) : 0;
}
static usage_t u_lval(struct symbol *type)
{
int wptr = is_ptr(type) && !(type->ctype.modifiers & MOD_CONST);
return wptr || type == &bad_ctype
? U_W_AOF | U_R_VAL : U_R_VAL;
}
static usage_t fix_mode(struct symbol *type, usage_t mode)
{
mode &= (U_SELF | (U_SELF << U_SHIFT));
switch (type->type) {
case SYM_BASETYPE:
if (!type->ctype.base_type)
break;
case SYM_ENUM:
case SYM_BITFIELD:
if (mode & U_MASK)
mode &= U_SELF;
default:
break; case SYM_FN:
if (mode & U_R_VAL)
mode |= U_R_AOF;
mode &= ~(U_R_VAL | U_W_AOF);
break; case SYM_ARRAY:
if (mode & (U_MASK << U_SHIFT))
mode >>= U_SHIFT;
else if (mode != U_W_VAL)
mode = u_addr(mode);
}
if (!(mode & U_R_AOF))
mode &= ~U_W_AOF;
return mode;
}
static struct symbol *report_member(usage_t mode, struct position *pos,
struct symbol *type, struct symbol *mem)
{
struct symbol *ret = mem->ctype.base_type;
if (mem->ident || mem->type == SYM_BAD)
reporter->r_member(fix_mode(ret, mode), pos, type, mem);
return ret;
}
static void report_implicit(usage_t mode, struct position *pos, struct symbol *type)
{
if (type->type != SYM_STRUCT && type->type != SYM_UNION)
return;
if (type->ident != NULL)
reporter->r_member(mode, pos, type, NULL);
DO_LIST(type->symbol_list, mem,
report_implicit(mode, pos, base_type(mem)));
}
static inline struct symbol *expr_symbol(struct expression *expr)
{
struct symbol *sym = expr->symbol;
if (!sym) {
sym = lookup_symbol(expr->symbol_name, NS_SYMBOL);
if (!sym) {
sym = alloc_symbol(expr->pos, SYM_BAD);
bind_symbol(sym, expr->symbol_name, NS_SYMBOL);
sym->kind = expr->op ?: 'v'; /* see EXPR_CALL */
}
}
if (!sym->ctype.base_type)
sym->ctype.base_type = &bad_ctype;
return sym;
}
static struct symbol *report_symbol(usage_t mode, struct expression *expr)
{
struct symbol *sym = expr_symbol(expr);
struct symbol *ret = base_type(sym);
if (0 && ret->type == SYM_ENUM)
return report_member(mode, &expr->pos, ret, expr->symbol);
reporter->r_symbol(fix_mode(ret, mode), &expr->pos, sym);
return ret;
}
static bool deanon(struct symbol *base, struct ident *node, struct symbol *parent)
{
struct ident *pi = parent ? parent->ident : NULL;
char name[256];
if (!node) {
base->ident = pi;
return false;
}
snprintf(name, sizeof(name), "%.*s:%.*s",
pi ? pi->len : 0, pi ? pi->name : NULL, node->len, node->name);
base->ident = built_in_ident(name);
return true;
}
static void report_memdef(struct symbol *sym, struct symbol *mem)
{
mem->kind = 'm';
if (sym && mem->ident)
reporter->r_memdef(sym, mem);
}
static void examine_sym_node(struct symbol *node, struct symbol *parent)
{
struct ident *name = node->ident;
struct symbol *base, *dctx;
if (node->visited)
return;
node->visited = 1;
node->kind = 'v';
while ((base = node->ctype.base_type) != NULL)
switch (base->type) {
case SYM_TYPEOF:
node->ctype.base_type =
do_expression(U_VOID, base->initializer);
break;
case SYM_ARRAY:
do_expression(U_R_VAL, base->array_size);
case SYM_PTR:
node = base;
break;
case SYM_FN:
node->kind = 'f';
node = base;
break;
case SYM_STRUCT: case SYM_UNION: //case SYM_ENUM:
if (base->inspected)
return;
base->inspected = 1;
base->kind = 's';
if (!base->symbol_list)
return;
dctx = dissect_ctx;
if (toplevel(base->scope))
dissect_ctx = NULL;
if (base->ident || deanon(base, name, parent))
reporter->r_symdef(base);
if (base->ident)
parent = base;
DO_LIST(base->symbol_list, mem,
examine_sym_node(mem, parent);
report_memdef(parent, mem));
dissect_ctx = dctx;
default:
return;
}
}
static struct symbol *base_type(struct symbol *sym)
{
if (!sym)
return &bad_ctype;
if (sym->type == SYM_NODE)
examine_sym_node(sym, NULL);
return sym->ctype.base_type // builtin_fn_type
?: &bad_ctype;
}
static struct symbol *__lookup_member(struct symbol *type, struct ident *name, int *p_addr)
{
struct symbol *node;
int addr = 0;
FOR_EACH_PTR(type->symbol_list, node)
if (!name) {
if (addr == *p_addr)
return node;
}
else if (node->ident == NULL) {
node = __lookup_member(node->ctype.base_type, name, NULL);
if (node)
goto found;
}
else if (node->ident == name) {
found:
if (p_addr)
*p_addr = addr;
return node;
}
addr++;
END_FOR_EACH_PTR(node);
return NULL;
}
static struct symbol *lookup_member(struct symbol *type, struct ident *name, int *addr)
{
struct symbol *mem = __lookup_member(type, name, addr);
if (!mem) {
static struct symbol bad_member = {
.type = SYM_BAD,
.ctype.base_type = &bad_ctype,
.kind = 'm',
};
if (!type->symbol_list)
type->scope = file_scope;
mem = &bad_member;
mem->ident = name;
}
return mem;
}
static struct expression *peek_preop(struct expression *expr, int op)
{
do {
if (expr->type != EXPR_PREOP)
break;
if (expr->op == op)
return expr->unop;
if (expr->op == '(')
expr = expr->unop;
else
break;
} while (expr);
return NULL;
}
static struct symbol *do_expression(usage_t mode, struct expression *expr)
{
struct symbol *ret = &int_ctype;
again:
if (expr) switch (expr->type) {
default:
warning(expr->pos, "bad expr->type: %d", expr->type);
case EXPR_TYPE: // [struct T]; Why ???
case EXPR_VALUE:
case EXPR_FVALUE:
break; case EXPR_LABEL:
ret = &label_ctype;
break; case EXPR_STRING:
ret = &string_ctype;
break; case EXPR_STATEMENT:
ret = do_statement(mode, expr->statement);
break; case EXPR_SIZEOF: case EXPR_ALIGNOF: case EXPR_PTRSIZEOF:
do_expression(U_VOID, expr->cast_expression);
break; case EXPR_COMMA:
do_expression(U_VOID, expr->left);
ret = do_expression(mode, expr->right);
break; case EXPR_CAST: case EXPR_FORCE_CAST: //case EXPR_IMPLIED_CAST:
ret = base_type(expr->cast_type);
do_initializer(ret, expr->cast_expression);
break; case EXPR_COMPARE: case EXPR_LOGICAL:
mode = u_rval(mode);
do_expression(mode, expr->left);
do_expression(mode, expr->right);
break; case EXPR_CONDITIONAL: //case EXPR_SELECT:
do_expression(expr->cond_true
? U_R_VAL : U_R_VAL | mode,
expr->conditional);
ret = do_expression(mode, expr->cond_true);
ret = do_expression(mode, expr->cond_false);
break; case EXPR_CALL:
if (expr->fn->type == EXPR_SYMBOL)
expr->fn->op = 'f'; /* for expr_symbol() */
ret = do_expression(U_R_PTR, expr->fn);
if (is_ptr(ret))
ret = ret->ctype.base_type;
DO_2_LIST(ret->arguments, expr->args, arg, val,
do_expression(u_lval(base_type(arg)), val));
ret = ret->type == SYM_FN ? base_type(ret)
: &bad_ctype;
break; case EXPR_ASSIGNMENT:
mode |= U_W_VAL | U_R_VAL;
if (expr->op == '=')
mode &= ~U_R_VAL;
ret = do_expression(mode, expr->left);
report_implicit(mode, &expr->pos, ret);
mode = expr->op == '='
? u_lval(ret) : U_R_VAL;
do_expression(mode, expr->right);
break; case EXPR_BINOP: {
struct symbol *l, *r;
mode |= u_rval(mode);
l = do_expression(mode, expr->left);
r = do_expression(mode, expr->right);
if (expr->op != '+' && expr->op != '-')
;
else if (!is_ptr_type(r))
ret = l;
else if (!is_ptr_type(l))
ret = r;
}
break; case EXPR_PREOP: case EXPR_POSTOP: {
struct expression *unop = expr->unop;
switch (expr->op) {
case SPECIAL_INCREMENT:
case SPECIAL_DECREMENT:
mode |= U_W_VAL | U_R_VAL;
default:
mode |= u_rval(mode);
case '(':
ret = do_expression(mode, unop);
break; case '&':
if ((expr = peek_preop(unop, '*')))
goto again;
ret = alloc_symbol(unop->pos, SYM_PTR);
ret->ctype.base_type =
do_expression(u_addr(mode), unop);
break; case '*':
if ((expr = peek_preop(unop, '&')))
goto again;
if (mode & (U_MASK << U_SHIFT))
mode |= U_R_VAL;
mode <<= U_SHIFT;
if (mode & (U_R_AOF << U_SHIFT))
mode |= U_R_VAL;
if (mode & (U_W_VAL << U_SHIFT))
mode |= U_W_AOF;
ret = do_expression(mode, unop);
ret = is_ptr(ret) ? base_type(ret)
: &bad_ctype;
}
}
break; case EXPR_DEREF: {
struct symbol *p_type;
usage_t p_mode;
p_mode = mode & U_SELF;
if (!(mode & U_MASK) && (mode & (U_MASK << U_SHIFT)))
p_mode = U_R_VAL;
p_type = do_expression(p_mode, expr->deref);
ret = report_member(mode, &expr->pos, p_type,
lookup_member(p_type, expr->member, NULL));
}
break; case EXPR_OFFSETOF: {
struct symbol *in = base_type(expr->in);
do {
if (expr->op == '.') {
in = report_member(U_VOID, &expr->pos, in,
lookup_member(in, expr->ident, NULL));
} else {
do_expression(U_R_VAL, expr->index);
in = in->ctype.base_type;
}
} while ((expr = expr->down));
}
break; case EXPR_GENERIC: {
struct type_expression *map;
do_expression(U_VOID, expr->control);
for (map = expr->map; map; map = map->next)
ret = do_expression(mode, map->expr);
if (expr->def)
ret = do_expression(mode, expr->def);
}
break; case EXPR_SYMBOL:
ret = report_symbol(mode, expr);
}
return ret;
}
static void do_asm_xputs(usage_t mode, struct asm_operand_list *xputs)
{
DO_LIST(xputs, op, do_expression(U_W_AOF | mode, op->expr));
}
static struct symbol *do_statement(usage_t mode, struct statement *stmt)
{
struct symbol *ret = &void_ctype;
if (stmt) switch (stmt->type) {
default:
warning(stmt->pos, "bad stmt->type: %d", stmt->type);
case STMT_NONE:
case STMT_RANGE:
case STMT_CONTEXT:
break; case STMT_DECLARATION:
do_sym_list(stmt->declaration);
break; case STMT_EXPRESSION:
ret = do_expression(mode, stmt->expression);
break; case STMT_RETURN: {
struct symbol *type = dissect_ctx->ctype.base_type;
do_expression(u_lval(base_type(type)), stmt->expression);
}
break; case STMT_ASM:
do_expression(U_R_VAL, stmt->asm_string);
do_asm_xputs(U_W_VAL, stmt->asm_outputs);
do_asm_xputs(U_R_VAL, stmt->asm_inputs);
break; case STMT_COMPOUND: {
int count;
count = statement_list_size(stmt->stmts);
DO_LIST(stmt->stmts, st,
ret = do_statement(--count ? U_VOID : mode, st));
}
break; case STMT_ITERATOR:
do_sym_list(stmt->iterator_syms);
do_statement(U_VOID, stmt->iterator_pre_statement);
do_expression(U_R_VAL, stmt->iterator_pre_condition);
do_statement(U_VOID, stmt->iterator_post_statement);
do_statement(U_VOID, stmt->iterator_statement);
do_expression(U_R_VAL, stmt->iterator_post_condition);
break; case STMT_IF:
do_expression(U_R_VAL, stmt->if_conditional);
do_statement(U_VOID, stmt->if_true);
do_statement(U_VOID, stmt->if_false);
break; case STMT_SWITCH:
do_expression(U_R_VAL, stmt->switch_expression);
do_statement(U_VOID, stmt->switch_statement);
break; case STMT_CASE:
do_expression(U_R_VAL, stmt->case_expression);
do_expression(U_R_VAL, stmt->case_to);
do_statement(U_VOID, stmt->case_statement);
break; case STMT_GOTO:
do_expression(U_R_PTR, stmt->goto_expression);
break; case STMT_LABEL:
do_statement(mode, stmt->label_statement);
}
return ret;
}
static struct symbol *do_initializer(struct symbol *type, struct expression *expr)
{
struct symbol *m_type;
struct expression *m_expr;
int m_addr;
if (expr) switch (expr->type) {
default:
do_expression(u_lval(type), expr);
break; case EXPR_INDEX:
do_initializer(base_type(type), expr->idx_expression);
break; case EXPR_INITIALIZER:
m_addr = 0;
FOR_EACH_PTR(expr->expr_list, m_expr) {
if (type->type == SYM_ARRAY) {
m_type = base_type(type);
if (m_expr->type == EXPR_INDEX)
m_expr = m_expr->idx_expression;
} else {
int *m_atop = &m_addr;
m_type = type;
while (m_expr->type == EXPR_IDENTIFIER) {
m_type = report_member(U_W_VAL, &m_expr->pos, m_type,
lookup_member(m_type, m_expr->expr_ident, m_atop));
m_expr = m_expr->ident_expression;
m_atop = NULL;
}
if (m_atop) {
m_type = report_member(U_W_VAL, &m_expr->pos, m_type,
lookup_member(m_type, NULL, m_atop));
}
if (m_expr->type != EXPR_INITIALIZER)
report_implicit(U_W_VAL, &m_expr->pos, m_type);
}
do_initializer(m_type, m_expr);
m_addr++;
} END_FOR_EACH_PTR(m_expr);
}
return type;
}
static inline struct symbol *do_symbol(struct symbol *sym)
{
struct symbol *type = base_type(sym);
struct symbol *dctx = dissect_ctx;
struct statement *stmt;
reporter->r_symdef(sym);
switch (type->type) {
default:
if (!sym->initializer)
break;
reporter->r_symbol(U_W_VAL, &sym->pos, sym);
if (!dctx)
dissect_ctx = sym;
do_initializer(type, sym->initializer);
dissect_ctx = dctx;
break; case SYM_FN:
stmt = sym->ctype.modifiers & MOD_INLINE
? type->inline_stmt : type->stmt;
if (!stmt)
break;
if (dctx)
sparse_error(dctx->pos, "dissect_ctx change %s -> %s",
show_ident(dctx->ident), show_ident(sym->ident));
dissect_ctx = sym;
do_sym_list(type->arguments);
do_statement(U_VOID, stmt);
dissect_ctx = dctx;
}
return type;
}
static void do_sym_list(struct symbol_list *list)
{
DO_LIST(list, sym, do_symbol(sym));
}
void dissect(struct reporter *rep, struct string_list *filelist)
{
reporter = rep;
DO_LIST(filelist, file, do_sym_list(__sparse(file)));
}
sparse-0.6.4/dissect.h 0000664 0000000 0000000 00000001367 14115310122 0014622 0 ustar 00root root 0000000 0000000 #ifndef DISSECT_H
#define DISSECT_H
#include
#include "parse.h"
#include "expression.h"
#include "scope.h"
#define U_SHIFT 8
#define U_R_AOF 0x01
#define U_W_AOF 0x02
#define U_R_VAL 0x04
#define U_W_VAL 0x08
#define U_R_PTR (U_R_VAL << U_SHIFT)
#define U_W_PTR (U_W_VAL << U_SHIFT)
struct reporter
{
void (*r_symdef)(struct symbol *);
void (*r_memdef)(struct symbol *, struct symbol *);
void (*r_symbol)(unsigned, struct position *, struct symbol *);
void (*r_member)(unsigned, struct position *, struct symbol *, struct symbol *);
};
extern struct symbol *dissect_ctx;
static inline bool sym_is_local(struct symbol *sym)
{
return !toplevel(sym->scope);
}
extern void dissect(struct reporter *, struct string_list *);
#endif
sparse-0.6.4/dominate.c 0000664 0000000 0000000 00000006556 14115310122 0014764 0 ustar 00root root 0000000 0000000 // SPDX-License-Identifier: MIT
//
// dominate.c - compute the (iterated) dominance frontier of (a set of) nodes.
//
// Copyright (C) 2017 - Luc Van Oostenryck
//
// The algorithm used is the one described in:
// "A Linear Time Algorithm for Placing phi-nodes"
// by Vugranam C. Sreedhar and Guang R. Gao
//
#include "dominate.h"
#include "flowgraph.h"
#include "linearize.h"
#include "flow.h"
#include
#include
#include
struct piggy {
unsigned int max;
struct basic_block_list *lists[0];
};
static struct piggy *bank_init(unsigned levels)
{
struct piggy *bank;
bank = calloc(1, sizeof(*bank) + levels * sizeof(bank->lists[0]));
bank->max = levels - 1;
return bank;
}
static void bank_free(struct piggy *bank, unsigned int levels)
{
for (; levels-- ;)
free_ptr_list(&bank->lists[levels]);
free(bank);
}
static void bank_put(struct piggy *bank, struct basic_block *bb)
{
unsigned int level = bb->dom_level;
assert(level <= bank->max);
add_bb(&bank->lists[level], bb);
}
static inline struct basic_block *pop_bb(struct basic_block_list **list)
{
return delete_ptr_list_last((struct ptr_list **)list);
}
static struct basic_block *bank_get(struct piggy *bank)
{
int level = bank->max;
do {
struct basic_block *bb = pop_bb(&bank->lists[level]);
if (bb)
return bb;
if (!level)
return NULL;
bank->max = --level;
} while (1);
}
#define VISITED 0x1
#define INPHI 0x2
#define ALPHA 0x4
#define FLAGS 0x7
static void visit(struct piggy *bank, struct basic_block_list **idf, struct basic_block *x, int curr_level)
{
struct basic_block *y;
x->generation |= 1;
FOR_EACH_PTR(x->children, y) {
unsigned flags = y->generation & FLAGS;
if (y->idom == x) // J-edges will be processed later
continue;
if (y->dom_level > curr_level)
continue;
if (flags & INPHI)
continue;
y->generation |= INPHI;
add_bb(idf, y);
if (flags & ALPHA)
continue;
bank_put(bank, y);
} END_FOR_EACH_PTR(y);
FOR_EACH_PTR(x->doms, y) {
if (y->generation & VISITED)
continue;
visit(bank, idf, y, curr_level);
} END_FOR_EACH_PTR(y);
}
void idf_compute(struct entrypoint *ep, struct basic_block_list **idf, struct basic_block_list *alpha)
{
int levels = ep->dom_levels;
struct piggy *bank = bank_init(levels);
struct basic_block *bb;
unsigned long generation = bb_generation;
generation = bb_generation;
generation += -generation & FLAGS;
bb_generation = generation + (FLAGS + 1);
// init all the nodes
FOR_EACH_PTR(ep->bbs, bb) {
// FIXME: this should be removed and the tests for
// visited/in_phi/alpha should use a sparse set
bb->generation = generation;
} END_FOR_EACH_PTR(bb);
FOR_EACH_PTR(alpha, bb) {
bb->generation = generation | ALPHA;
bank_put(bank, bb);
} END_FOR_EACH_PTR(bb);
while ((bb = bank_get(bank))) {
visit(bank, idf, bb, bb->dom_level);
}
bank_free(bank, levels);
}
void idf_dump(struct entrypoint *ep)
{
struct basic_block *bb;
domtree_build(ep);
printf("%s's IDF:\n", show_ident(ep->name->ident));
FOR_EACH_PTR(ep->bbs, bb) {
struct basic_block_list *alpha = NULL;
struct basic_block_list *idf = NULL;
struct basic_block *df;
add_bb(&alpha, bb);
idf_compute(ep, &idf, alpha);
printf("\t%s\t<-", show_label(bb));
FOR_EACH_PTR(idf, df) {
printf(" %s", show_label(df));
} END_FOR_EACH_PTR(df);
printf("\n");
free_ptr_list(&idf);
free_ptr_list(&alpha);
} END_FOR_EACH_PTR(bb);
}
sparse-0.6.4/dominate.h 0000664 0000000 0000000 00000000402 14115310122 0014751 0 ustar 00root root 0000000 0000000 #ifndef DOMINATE_H
#define DOMINATE_H
struct entrypoint;
struct basic_block_list;
void idf_compute(struct entrypoint *ep, struct basic_block_list **idf, struct basic_block_list *alpha);
// For debugging only
void idf_dump(struct entrypoint *ep);
#endif
sparse-0.6.4/evaluate.c 0000664 0000000 0000000 00000304060 14115310122 0014761 0 ustar 00root root 0000000 0000000 /*
* sparse/evaluate.c
*
* Copyright (C) 2003 Transmeta Corp.
* 2003-2004 Linus Torvalds
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* Evaluate constant expressions.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "evaluate.h"
#include "lib.h"
#include "allocate.h"
#include "parse.h"
#include "token.h"
#include "symbol.h"
#include "target.h"
#include "expression.h"
struct symbol *current_fn;
struct ident bad_address_space = { .len = 6, .name = "bad AS", };
static struct symbol *degenerate(struct expression *expr);
static struct symbol *evaluate_symbol(struct symbol *sym);
static inline int valid_expr_type(struct expression *expr)
{
return expr && valid_type(expr->ctype);
}
static inline int valid_subexpr_type(struct expression *expr)
{
return valid_expr_type(expr->left)
&& valid_expr_type(expr->right);
}
static struct symbol *unqualify_type(struct symbol *ctype)
{
if (!ctype)
return ctype;
if (ctype->type == SYM_NODE && (ctype->ctype.modifiers & MOD_QUALIFIER)) {
struct symbol *unqual = alloc_symbol(ctype->pos, 0);
*unqual = *ctype;
unqual->ctype.modifiers &= ~MOD_QUALIFIER;
return unqual;
}
return ctype;
}
static struct symbol *evaluate_symbol_expression(struct expression *expr)
{
struct expression *addr;
struct symbol *sym = expr->symbol;
struct symbol *base_type;
if (!sym) {
expression_error(expr, "undefined identifier '%s'", show_ident(expr->symbol_name));
return NULL;
}
examine_symbol_type(sym);
base_type = get_base_type(sym);
if (!base_type) {
expression_error(expr, "identifier '%s' has no type", show_ident(expr->symbol_name));
return NULL;
}
addr = alloc_expression(expr->pos, EXPR_SYMBOL);
addr->symbol = sym;
addr->symbol_name = expr->symbol_name;
addr->ctype = &lazy_ptr_ctype; /* Lazy evaluation: we need to do a proper job if somebody does &sym */
addr->flags = expr->flags;
expr->type = EXPR_PREOP;
expr->op = '*';
expr->unop = addr;
expr->flags = CEF_NONE;
/* The type of a symbol is the symbol itself! */
expr->ctype = sym;
return sym;
}
static struct symbol *evaluate_string(struct expression *expr)
{
struct symbol *sym = alloc_symbol(expr->pos, SYM_NODE);
struct symbol *array = alloc_symbol(expr->pos, SYM_ARRAY);
struct expression *addr = alloc_expression(expr->pos, EXPR_SYMBOL);
struct expression *initstr = alloc_expression(expr->pos, EXPR_STRING);
unsigned int length = expr->string->length;
struct symbol *char_type = expr->wide ? wchar_ctype : &char_ctype;
sym->array_size = alloc_const_expression(expr->pos, length);
sym->bit_size = length * char_type->bit_size;
sym->ctype.alignment = 1;
sym->string = 1;
sym->ctype.modifiers = MOD_STATIC;
sym->ctype.base_type = array;
sym->initializer = initstr;
sym->examined = 1;
sym->evaluated = 1;
initstr->ctype = sym;
initstr->string = expr->string;
array->array_size = sym->array_size;
array->bit_size = sym->bit_size;
array->ctype.alignment = char_type->ctype.alignment;
array->ctype.modifiers = MOD_STATIC;
array->ctype.base_type = char_type;
array->examined = 1;
array->evaluated = 1;
addr->symbol = sym;
addr->ctype = &lazy_ptr_ctype;
addr->flags = CEF_ADDR;
expr->type = EXPR_PREOP;
expr->op = '*';
expr->unop = addr;
expr->ctype = sym;
return sym;
}
/* type has come from classify_type and is an integer type */
static inline struct symbol *integer_promotion(struct symbol *type)
{
unsigned long mod = type->ctype.modifiers;
int width = type->bit_size;
/*
* Bitfields always promote to the base type,
* even if the bitfield might be bigger than
* an "int".
*/
if (type->type == SYM_BITFIELD) {
type = type->ctype.base_type;
}
mod = type->ctype.modifiers;
if (width < bits_in_int)
return &int_ctype;
/* If char/short has as many bits as int, it still gets "promoted" */
if (type->rank < 0) {
if (mod & MOD_UNSIGNED)
return &uint_ctype;
return &int_ctype;
}
return type;
}
/*
* After integer promotons:
* If both types are the same
* -> no conversion needed
* If the types have the same signedness (their rank must be different)
* -> convert to the type of the highest rank
* If rank(unsigned type) >= rank(signed type)
* -> convert to the unsigned type
* If size(signed type) > size(unsigned type)
* -> convert to the signed type
* Otherwise
* -> convert to the unsigned type corresponding to the signed type.
*/
static struct symbol *bigger_int_type(struct symbol *left, struct symbol *right)
{
static struct symbol *unsigned_types[] = {
[0] = &uint_ctype,
[1] = &ulong_ctype,
[2] = &ullong_ctype,
[3] = &uint128_ctype,
};
unsigned long lmod, rmod;
struct symbol *stype, *utype;
left = integer_promotion(left);
right = integer_promotion(right);
if (left == right)
return left;
lmod = left->ctype.modifiers;
rmod = right->ctype.modifiers;
if (((lmod ^ rmod) & MOD_UNSIGNED) == 0)
return (left->rank > right->rank) ? left : right;
if (lmod & MOD_UNSIGNED) {
utype = left;
stype = right;
} else {
stype = left;
utype = right;
}
if (utype->rank >= stype->rank)
return utype;
if (stype->bit_size > utype->bit_size)
return stype;
utype = unsigned_types[stype->rank];
return utype;
}
static struct symbol *base_type(struct symbol *node, unsigned long *modp, struct ident **asp)
{
unsigned long mod = 0;
struct ident *as = NULL;
while (node) {
mod |= node->ctype.modifiers;
combine_address_space(node->pos, &as, node->ctype.as);
if (node->type == SYM_NODE) {
node = node->ctype.base_type;
continue;
}
break;
}
*modp = mod & ~MOD_IGNORE;
*asp = as;
return node;
}
static int is_same_type(struct expression *expr, struct symbol *new)
{
struct symbol *old = expr->ctype;
unsigned long oldmod, newmod;
struct ident *oldas, *newas;
old = base_type(old, &oldmod, &oldas);
new = base_type(new, &newmod, &newas);
/* Same base type, same address space? */
if (old == new && oldas == newas) {
unsigned long difmod;
/* Check the modifier bits. */
difmod = (oldmod ^ newmod) & ~MOD_NOCAST;
/* Exact same type? */
if (!difmod)
return 1;
/*
* Not the same type, but differs only in "const".
* Don't warn about MOD_NOCAST.
*/
if (difmod == MOD_CONST)
return 0;
}
if ((oldmod | newmod) & MOD_NOCAST) {
const char *tofrom = "to/from";
if (!(newmod & MOD_NOCAST))
tofrom = "from";
if (!(oldmod & MOD_NOCAST))
tofrom = "to";
warning(expr->pos, "implicit cast %s nocast type", tofrom);
}
return 0;
}
static void
warn_for_different_enum_types (struct position pos,
struct symbol *typea,
struct symbol *typeb)
{
if (!Wenum_mismatch)
return;
if (typea->type == SYM_NODE)
typea = typea->ctype.base_type;
if (typeb->type == SYM_NODE)
typeb = typeb->ctype.base_type;
if (typea == typeb)
return;
if (typea->type == SYM_ENUM && typeb->type == SYM_ENUM) {
warning(pos, "mixing different enum types:");
info(pos, " %s", show_typename(typea));
info(pos, " %s", show_typename(typeb));
}
}
static int cast_flags(struct expression *expr, struct expression *target);
static struct symbol *cast_to_bool(struct expression *expr);
/*
* This gets called for implicit casts in assignments and
* integer promotion.
*/
static struct expression * cast_to(struct expression *old, struct symbol *type)
{
struct expression *expr;
warn_for_different_enum_types (old->pos, old->ctype, type);
if (old->ctype != &null_ctype && is_same_type(old, type))
return old;
expr = alloc_expression(old->pos, EXPR_IMPLIED_CAST);
expr->ctype = type;
expr->cast_type = type;
expr->cast_expression = old;
expr->flags = cast_flags(expr, old);
if (is_bool_type(type))
cast_to_bool(expr);
return expr;
}
enum {
TYPE_NUM = 1,
TYPE_BITFIELD = 2,
TYPE_RESTRICT = 4,
TYPE_FLOAT = 8,
TYPE_PTR = 16,
TYPE_COMPOUND = 32,
TYPE_FOULED = 64,
TYPE_FN = 128,
};
static inline int classify_type(struct symbol *type, struct symbol **base)
{
static int type_class[SYM_BAD + 1] = {
[SYM_PTR] = TYPE_PTR,
[SYM_FN] = TYPE_PTR | TYPE_FN,
[SYM_ARRAY] = TYPE_PTR | TYPE_COMPOUND,
[SYM_STRUCT] = TYPE_COMPOUND,
[SYM_UNION] = TYPE_COMPOUND,
[SYM_BITFIELD] = TYPE_NUM | TYPE_BITFIELD,
[SYM_RESTRICT] = TYPE_NUM | TYPE_RESTRICT,
[SYM_FOULED] = TYPE_NUM | TYPE_RESTRICT | TYPE_FOULED,
};
if (type->type == SYM_NODE)
type = type->ctype.base_type;
if (type->type == SYM_TYPEOF) {
type = examine_symbol_type(type);
if (type->type == SYM_NODE)
type = type->ctype.base_type;
}
if (type->type == SYM_ENUM)
type = type->ctype.base_type;
*base = type;
if (type->type == SYM_BASETYPE) {
if (type->ctype.base_type == &int_type)
return TYPE_NUM;
if (type->ctype.base_type == &fp_type)
return TYPE_NUM | TYPE_FLOAT;
}
return type_class[type->type];
}
#define is_int(class) ((class & (TYPE_NUM | TYPE_FLOAT)) == TYPE_NUM)
static inline int is_string_type(struct symbol *type)
{
if (type->type == SYM_NODE)
type = type->ctype.base_type;
if (type->type != SYM_ARRAY)
return 0;
type = type->ctype.base_type;
return is_byte_type(type) || is_wchar_type(type);
}
static struct symbol *bad_expr_type(struct expression *expr)
{
switch (expr->type) {
case EXPR_BINOP:
case EXPR_COMPARE:
if (!valid_subexpr_type(expr))
break;
sparse_error(expr->pos, "incompatible types for operation (%s):", show_special(expr->op));
info(expr->pos, " %s", show_typename(expr->left->ctype));
info(expr->pos, " %s", show_typename(expr->right->ctype));
break;
case EXPR_PREOP:
case EXPR_POSTOP:
if (!valid_expr_type(expr->unop))
break;
sparse_error(expr->pos, "incompatible type for operation (%s):", show_special(expr->op));
info(expr->pos, " %s", show_typename(expr->unop->ctype));
break;
default:
break;
}
expr->flags = CEF_NONE;
return expr->ctype = &bad_ctype;
}
static int restricted_value(struct expression *v, struct symbol *type)
{
if (v->type != EXPR_VALUE)
return 1;
if (v->value != 0)
return 1;
return 0;
}
static int restricted_binop(int op, struct symbol *type)
{
switch (op) {
case '&':
case '=':
case SPECIAL_AND_ASSIGN:
case SPECIAL_OR_ASSIGN:
case SPECIAL_XOR_ASSIGN:
return 1; /* unfoul */
case '|':
case '^':
case '?':
return 2; /* keep fouled */
case SPECIAL_EQUAL:
case SPECIAL_NOTEQUAL:
return 3; /* warn if fouled */
default:
return 0; /* warn */
}
}
static int restricted_unop(int op, struct symbol **type)
{
if (op == '~') {
if ((*type)->bit_size < bits_in_int)
*type = befoul(*type);
return 0;
} if (op == '+')
return 0;
return 1;
}
/* type should be SYM_FOULED */
static inline struct symbol *unfoul(struct symbol *type)
{
return type->ctype.base_type;
}
static struct symbol *restricted_binop_type(int op,
struct expression *left,
struct expression *right,
int lclass, int rclass,
struct symbol *ltype,
struct symbol *rtype)
{
struct symbol *ctype = NULL;
if (lclass & TYPE_RESTRICT) {
if (rclass & TYPE_RESTRICT) {
if (ltype == rtype) {
ctype = ltype;
} else if (lclass & TYPE_FOULED) {
if (unfoul(ltype) == rtype)
ctype = ltype;
} else if (rclass & TYPE_FOULED) {
if (unfoul(rtype) == ltype)
ctype = rtype;
}
} else {
if (!restricted_value(right, ltype))
ctype = ltype;
}
} else if (!restricted_value(left, rtype))
ctype = rtype;
if (ctype) {
switch (restricted_binop(op, ctype)) {
case 1:
if ((lclass ^ rclass) & TYPE_FOULED)
ctype = unfoul(ctype);
break;
case 3:
if (!(lclass & rclass & TYPE_FOULED))
break;
case 0:
ctype = NULL;
default:
break;
}
}
return ctype;
}
static inline void unrestrict(struct expression *expr,
int class, struct symbol **ctype)
{
if (class & TYPE_RESTRICT) {
if (class & TYPE_FOULED)
*ctype = unfoul(*ctype);
warning(expr->pos, "%s degrades to integer",
show_typename(*ctype));
*ctype = (*ctype)->ctype.base_type; /* get to arithmetic type */
}
}
static struct symbol *usual_conversions(int op,
struct expression *left,
struct expression *right,
int lclass, int rclass,
struct symbol *ltype,
struct symbol *rtype)
{
struct symbol *ctype;
warn_for_different_enum_types(right->pos, left->ctype, right->ctype);
if ((lclass | rclass) & TYPE_RESTRICT)
goto Restr;
Normal:
if (!(lclass & TYPE_FLOAT)) {
if (!(rclass & TYPE_FLOAT))
return bigger_int_type(ltype, rtype);
else
return rtype;
} else if (rclass & TYPE_FLOAT) {
if (rtype->rank > ltype->rank)
return rtype;
else
return ltype;
} else
return ltype;
Restr:
ctype = restricted_binop_type(op, left, right,
lclass, rclass, ltype, rtype);
if (ctype)
return ctype;
unrestrict(left, lclass, <ype);
unrestrict(right, rclass, &rtype);
goto Normal;
}
static inline int lvalue_expression(struct expression *expr)
{
return expr->type == EXPR_PREOP && expr->op == '*';
}
static struct symbol *evaluate_ptr_add(struct expression *expr, struct symbol *itype)
{
struct expression *index = expr->right;
struct symbol *ctype, *base;
int multiply;
classify_type(degenerate(expr->left), &ctype);
base = examine_pointer_target(ctype);
/*
* An address constant +/- an integer constant expression
* yields an address constant again [6.6(7)].
*/
if ((expr->left->flags & CEF_ADDR) && (expr->right->flags & CEF_ICE))
expr->flags = CEF_ADDR;
if (!base) {
expression_error(expr, "missing type information");
return NULL;
}
if (is_function(base)) {
expression_error(expr, "arithmetics on pointers to functions");
return NULL;
}
/* Get the size of whatever the pointer points to */
multiply = is_void_type(base) ? 1 : bits_to_bytes(base->bit_size);
if (ctype == &null_ctype)
ctype = &ptr_ctype;
expr->ctype = ctype;
if (multiply == 1 && itype->bit_size == bits_in_pointer)
return ctype;
if (index->type == EXPR_VALUE) {
struct expression *val = alloc_expression(expr->pos, EXPR_VALUE);
unsigned long long v = index->value, mask;
mask = 1ULL << (itype->bit_size - 1);
if (v & mask)
v |= -mask;
else
v &= mask - 1;
v *= multiply;
mask = 1ULL << (bits_in_pointer - 1);
v &= mask | (mask - 1);
val->value = v;
val->ctype = ssize_t_ctype;
expr->right = val;
return ctype;
}
if (itype->bit_size != bits_in_pointer)
index = cast_to(index, ssize_t_ctype);
if (multiply > 1) {
struct expression *val = alloc_expression(expr->pos, EXPR_VALUE);
struct expression *mul = alloc_expression(expr->pos, EXPR_BINOP);
val->ctype = ssize_t_ctype;
val->value = multiply;
mul->op = '*';
mul->ctype = ssize_t_ctype;
mul->left = index;
mul->right = val;
index = mul;
}
expr->right = index;
return ctype;
}
static void examine_fn_arguments(struct symbol *fn);
#define MOD_IGN (MOD_QUALIFIER | MOD_FUN_ATTR)
const char *type_difference(struct ctype *c1, struct ctype *c2,
unsigned long mod1, unsigned long mod2)
{
struct ident *as1 = c1->as, *as2 = c2->as;
struct symbol *t1 = c1->base_type;
struct symbol *t2 = c2->base_type;
int move1 = 1, move2 = 1;
mod1 |= c1->modifiers;
mod2 |= c2->modifiers;
for (;;) {
unsigned long diff;
int type;
struct symbol *base1 = t1->ctype.base_type;
struct symbol *base2 = t2->ctype.base_type;
/*
* FIXME! Collect alignment and context too here!
*/
if (move1) {
if (t1 && t1->type != SYM_PTR) {
mod1 |= t1->ctype.modifiers;
combine_address_space(t1->pos, &as1, t1->ctype.as);
}
move1 = 0;
}
if (move2) {
if (t2 && t2->type != SYM_PTR) {
mod2 |= t2->ctype.modifiers;
combine_address_space(t2->pos, &as2, t2->ctype.as);
}
move2 = 0;
}
if (t1 == t2)
break;
if (!t1 || !t2)
return "different types";
if (t1->type == SYM_NODE || t1->type == SYM_ENUM) {
t1 = base1;
move1 = 1;
if (!t1)
return "bad types";
continue;
}
if (t2->type == SYM_NODE || t2->type == SYM_ENUM) {
t2 = base2;
move2 = 1;
if (!t2)
return "bad types";
continue;
}
move1 = move2 = 1;
type = t1->type;
if (type != t2->type)
return "different base types";
switch (type) {
default:
sparse_error(t1->pos,
"internal error: bad type in derived(%d)",
type);
return "bad types";
case SYM_RESTRICT:
return "different base types";
case SYM_UNION:
case SYM_STRUCT:
/* allow definition of incomplete structs and unions */
if (t1->ident == t2->ident)
return NULL;
return "different base types";
case SYM_ARRAY:
/* XXX: we ought to compare sizes */
break;
case SYM_PTR:
if (as1 != as2)
return "different address spaces";
/* MOD_SPECIFIER is due to idiocy in parse.c */
if ((mod1 ^ mod2) & ~MOD_IGNORE & ~MOD_SPECIFIER)
return "different modifiers";
/* we could be lazier here */
base1 = examine_pointer_target(t1);
base2 = examine_pointer_target(t2);
mod1 = t1->ctype.modifiers;
as1 = t1->ctype.as;
mod2 = t2->ctype.modifiers;
as2 = t2->ctype.as;
break;
case SYM_FN: {
struct symbol *arg1, *arg2;
int i;
if (as1 != as2)
return "different address spaces";
if ((mod1 ^ mod2) & ~MOD_IGNORE & ~MOD_SIGNEDNESS)
return "different modifiers";
mod1 = t1->ctype.modifiers;
as1 = t1->ctype.as;
mod2 = t2->ctype.modifiers;
as2 = t2->ctype.as;
if (t1->variadic != t2->variadic)
return "incompatible variadic arguments";
examine_fn_arguments(t1);
examine_fn_arguments(t2);
PREPARE_PTR_LIST(t1->arguments, arg1);
PREPARE_PTR_LIST(t2->arguments, arg2);
i = 1;
for (;;) {
const char *diffstr;
if (!arg1 && !arg2)
break;
if (!arg1 || !arg2)
return "different argument counts";
diffstr = type_difference(&arg1->ctype,
&arg2->ctype,
MOD_IGN, MOD_IGN);
if (diffstr) {
static char argdiff[80];
sprintf(argdiff, "incompatible argument %d (%s)", i, diffstr);
return argdiff;
}
NEXT_PTR_LIST(arg1);
NEXT_PTR_LIST(arg2);
i++;
}
FINISH_PTR_LIST(arg2);
FINISH_PTR_LIST(arg1);
break;
}
case SYM_BASETYPE:
if (as1 != as2)
return "different address spaces";
if (base1 != base2)
return "different base types";
if (t1->rank != t2->rank)
return "different type sizes";
diff = (mod1 ^ mod2) & ~MOD_IGNORE;
if (!diff)
return NULL;
else if (diff & ~MOD_SIGNEDNESS)
return "different modifiers";
else
return "different signedness";
}
t1 = base1;
t2 = base2;
}
if (as1 != as2)
return "different address spaces";
if ((mod1 ^ mod2) & ~MOD_IGNORE & ~MOD_SIGNEDNESS)
return "different modifiers";
return NULL;
}
static void bad_null(struct expression *expr)
{
if (Wnon_pointer_null)
warning(expr->pos, "Using plain integer as NULL pointer");
}
static unsigned long target_qualifiers(struct symbol *type)
{
unsigned long mod = type->ctype.modifiers & MOD_IGN;
if (type->ctype.base_type && type->ctype.base_type->type == SYM_ARRAY)
mod = 0;
return mod;
}
static struct symbol *evaluate_ptr_sub(struct expression *expr)
{
const char *typediff;
struct symbol *ltype, *rtype;
struct expression *l = expr->left;
struct expression *r = expr->right;
struct symbol *lbase;
classify_type(degenerate(l), <ype);
classify_type(degenerate(r), &rtype);
lbase = examine_pointer_target(ltype);
examine_pointer_target(rtype);
typediff = type_difference(<ype->ctype, &rtype->ctype,
target_qualifiers(rtype),
target_qualifiers(ltype));
if (typediff)
expression_error(expr, "subtraction of different types can't work (%s)", typediff);
if (is_function(lbase)) {
expression_error(expr, "subtraction of functions? Share your drugs");
return NULL;
}
expr->ctype = ssize_t_ctype;
if (lbase->bit_size > bits_in_char) {
struct expression *sub = alloc_expression(expr->pos, EXPR_BINOP);
struct expression *div = expr;
struct expression *val = alloc_expression(expr->pos, EXPR_VALUE);
unsigned long value = bits_to_bytes(lbase->bit_size);
val->ctype = size_t_ctype;
val->value = value;
if (value & (value-1)) {
if (Wptr_subtraction_blows) {
warning(expr->pos, "potentially expensive pointer subtraction");
info(expr->pos, " '%s' has a non-power-of-2 size: %lu", show_typename(lbase), value);
}
}
sub->op = '-';
sub->ctype = ssize_t_ctype;
sub->left = l;
sub->right = r;
div->op = '/';
div->left = sub;
div->right = val;
}
return ssize_t_ctype;
}
#define is_safe_type(type) ((type)->ctype.modifiers & MOD_SAFE)
static struct symbol *evaluate_conditional(struct expression *expr, int iterator)
{
struct symbol *ctype;
if (!expr)
return NULL;
if (!iterator && expr->type == EXPR_ASSIGNMENT && expr->op == '=')
warning(expr->pos, "assignment expression in conditional");
ctype = evaluate_expression(expr);
if (!valid_type(ctype))
return NULL;
if (is_safe_type(ctype))
warning(expr->pos, "testing a 'safe expression'");
if (is_func_type(ctype)) {
if (Waddress)
warning(expr->pos, "the address of %s will always evaluate as true", "a function");
} else if (is_array_type(ctype)) {
if (Waddress)
warning(expr->pos, "the address of %s will always evaluate as true", "an array");
} else if (!is_scalar_type(ctype)) {
sparse_error(expr->pos, "non-scalar type in conditional:");
info(expr->pos, " %s", show_typename(ctype));
return NULL;
}
ctype = degenerate(expr);
return ctype;
}
static struct symbol *evaluate_logical(struct expression *expr)
{
if (!evaluate_conditional(expr->left, 0))
return NULL;
if (!evaluate_conditional(expr->right, 0))
return NULL;
/* the result is int [6.5.13(3), 6.5.14(3)] */
expr->ctype = &int_ctype;
expr->flags = expr->left->flags & expr->right->flags;
expr->flags &= ~(CEF_CONST_MASK | CEF_ADDR);
return &int_ctype;
}
static struct symbol *evaluate_binop(struct expression *expr)
{
struct symbol *ltype, *rtype, *ctype;
int lclass = classify_type(expr->left->ctype, <ype);
int rclass = classify_type(expr->right->ctype, &rtype);
int op = expr->op;
/* number op number */
if (lclass & rclass & TYPE_NUM) {
expr->flags = expr->left->flags & expr->right->flags;
expr->flags &= ~CEF_CONST_MASK;
if ((lclass | rclass) & TYPE_FLOAT) {
switch (op) {
case '+': case '-': case '*': case '/':
break;
default:
return bad_expr_type(expr);
}
}
if (op == SPECIAL_LEFTSHIFT || op == SPECIAL_RIGHTSHIFT) {
// shifts do integer promotions, but that's it.
unrestrict(expr->left, lclass, <ype);
unrestrict(expr->right, rclass, &rtype);
ctype = ltype = integer_promotion(ltype);
rtype = integer_promotion(rtype);
} else {
// The rest do usual conversions
const unsigned left_not = expr->left->type == EXPR_PREOP
&& expr->left->op == '!';
const unsigned right_not = expr->right->type == EXPR_PREOP
&& expr->right->op == '!';
if ((op == '&' || op == '|') && (left_not || right_not))
warning(expr->pos, "dubious: %sx %c %sy",
left_not ? "!" : "",
op,
right_not ? "!" : "");
ltype = usual_conversions(op, expr->left, expr->right,
lclass, rclass, ltype, rtype);
ctype = rtype = ltype;
}
expr->left = cast_to(expr->left, ltype);
expr->right = cast_to(expr->right, rtype);
expr->ctype = ctype;
return ctype;
}
/* pointer (+|-) integer */
if (lclass & TYPE_PTR && is_int(rclass) && (op == '+' || op == '-')) {
unrestrict(expr->right, rclass, &rtype);
return evaluate_ptr_add(expr, rtype);
}
/* integer + pointer */
if (rclass & TYPE_PTR && is_int(lclass) && op == '+') {
struct expression *index = expr->left;
unrestrict(index, lclass, <ype);
expr->left = expr->right;
expr->right = index;
return evaluate_ptr_add(expr, ltype);
}
/* pointer - pointer */
if (lclass & rclass & TYPE_PTR && expr->op == '-')
return evaluate_ptr_sub(expr);
return bad_expr_type(expr);
}
static struct symbol *evaluate_comma(struct expression *expr)
{
expr->ctype = unqualify_type(degenerate(expr->right));
if (expr->ctype == &null_ctype)
expr->ctype = &ptr_ctype;
expr->flags &= expr->left->flags & expr->right->flags;
return expr->ctype;
}
static int modify_for_unsigned(int op)
{
if (op == '<')
op = SPECIAL_UNSIGNED_LT;
else if (op == '>')
op = SPECIAL_UNSIGNED_GT;
else if (op == SPECIAL_LTE)
op = SPECIAL_UNSIGNED_LTE;
else if (op == SPECIAL_GTE)
op = SPECIAL_UNSIGNED_GTE;
return op;
}
enum null_constant_type {
NON_NULL,
NULL_PTR,
NULL_ZERO,
};
static inline int is_null_pointer_constant(struct expression *e)
{
if (e->ctype == &null_ctype)
return NULL_PTR;
if (!(e->flags & CEF_ICE))
return NON_NULL;
return is_zero_constant(e) ? NULL_ZERO : NON_NULL;
}
static struct symbol *evaluate_compare(struct expression *expr)
{
struct expression *left = expr->left, *right = expr->right;
struct symbol *ltype, *rtype, *lbase, *rbase;
int lclass = classify_type(degenerate(left), <ype);
int rclass = classify_type(degenerate(right), &rtype);
struct symbol *ctype;
const char *typediff;
/* Type types? */
if (is_type_type(ltype) && is_type_type(rtype)) {
/*
* __builtin_types_compatible_p() yields an integer
* constant expression
*/
expr->flags = CEF_SET_ICE;
goto OK;
}
if (is_safe_type(left->ctype) || is_safe_type(right->ctype))
warning(expr->pos, "testing a 'safe expression'");
expr->flags = left->flags & right->flags & ~CEF_CONST_MASK & ~CEF_ADDR;
/* number on number */
if (lclass & rclass & TYPE_NUM) {
ctype = usual_conversions(expr->op, expr->left, expr->right,
lclass, rclass, ltype, rtype);
expr->left = cast_to(expr->left, ctype);
expr->right = cast_to(expr->right, ctype);
if (ctype->ctype.modifiers & MOD_UNSIGNED)
expr->op = modify_for_unsigned(expr->op);
goto OK;
}
/* at least one must be a pointer */
if (!((lclass | rclass) & TYPE_PTR))
return bad_expr_type(expr);
/* equality comparisons can be with null pointer constants */
if (expr->op == SPECIAL_EQUAL || expr->op == SPECIAL_NOTEQUAL) {
int is_null1 = is_null_pointer_constant(left);
int is_null2 = is_null_pointer_constant(right);
if (is_null1 == NULL_ZERO)
bad_null(left);
if (is_null2 == NULL_ZERO)
bad_null(right);
if (is_null1 && is_null2) {
int positive = expr->op == SPECIAL_EQUAL;
expr->type = EXPR_VALUE;
expr->value = positive;
goto OK;
}
if (is_null1 && (rclass & TYPE_PTR)) {
expr->left = cast_to(left, rtype);
goto OK;
}
if (is_null2 && (lclass & TYPE_PTR)) {
expr->right = cast_to(right, ltype);
goto OK;
}
}
/* both should be pointers */
if (!(lclass & rclass & TYPE_PTR))
return bad_expr_type(expr);
expr->op = modify_for_unsigned(expr->op);
lbase = examine_pointer_target(ltype);
rbase = examine_pointer_target(rtype);
/* they also have special treatment for pointers to void */
if (expr->op == SPECIAL_EQUAL || expr->op == SPECIAL_NOTEQUAL) {
if (ltype->ctype.as == rtype->ctype.as) {
if (lbase == &void_ctype) {
expr->right = cast_to(right, ltype);
goto OK;
}
if (rbase == &void_ctype) {
expr->left = cast_to(left, rtype);
goto OK;
}
}
}
typediff = type_difference(<ype->ctype, &rtype->ctype,
target_qualifiers(rtype),
target_qualifiers(ltype));
if (!typediff)
goto OK;
expression_error(expr, "incompatible types in comparison expression (%s):", typediff);
info(expr->pos, " %s", show_typename(ltype));
info(expr->pos, " %s", show_typename(rtype));
return NULL;
OK:
/* the result is int [6.5.8(6), 6.5.9(3)]*/
expr->ctype = &int_ctype;
return &int_ctype;
}
/*
* NOTE! The degenerate case of "x ? : y", where we don't
* have a true case, this will possibly promote "x" to the
* same type as "y", and thus _change_ the conditional
* test in the expression. But since promotion is "safe"
* for testing, that's OK.
*/
static struct symbol *evaluate_conditional_expression(struct expression *expr)
{
struct expression **cond;
struct symbol *ctype, *ltype, *rtype, *lbase, *rbase;
int lclass, rclass;
const char * typediff;
int qual;
if (!evaluate_conditional(expr->conditional, 0))
return NULL;
if (!evaluate_expression(expr->cond_false))
return NULL;
ctype = degenerate(expr->conditional);
rtype = degenerate(expr->cond_false);
cond = &expr->conditional;
ltype = ctype;
if (expr->cond_true) {
if (!evaluate_expression(expr->cond_true))
return NULL;
ltype = degenerate(expr->cond_true);
cond = &expr->cond_true;
}
expr->flags = (expr->conditional->flags & (*cond)->flags &
expr->cond_false->flags & ~CEF_CONST_MASK);
/*
* In the standard, it is defined that an integer constant expression
* shall only have operands that are themselves constant [6.6(6)].
* While this definition is very clear for expressions that need all
* their operands to be evaluated, for conditional expressions with a
* constant condition things are much less obvious.
* So, as an extension, do the same as GCC seems to do:
* Consider a conditional expression with a constant condition
* as having the same constantness as the argument corresponding
* to the truth value (including in the case of address constants
* which are defined more stricly [6.6(9)]).
*/
if (expr->conditional->flags & (CEF_ACE | CEF_ADDR)) {
int is_true = expr_truth_value(expr->conditional);
struct expression *arg = is_true ? *cond : expr->cond_false;
expr->flags = arg->flags & ~CEF_CONST_MASK;
}
lclass = classify_type(ltype, <ype);
rclass = classify_type(rtype, &rtype);
if (lclass & rclass & TYPE_NUM) {
ctype = usual_conversions('?', *cond, expr->cond_false,
lclass, rclass, ltype, rtype);
*cond = cast_to(*cond, ctype);
expr->cond_false = cast_to(expr->cond_false, ctype);
goto out;
}
if ((lclass | rclass) & TYPE_PTR) {
int is_null1 = is_null_pointer_constant(*cond);
int is_null2 = is_null_pointer_constant(expr->cond_false);
if (is_null1 && is_null2) {
*cond = cast_to(*cond, &ptr_ctype);
expr->cond_false = cast_to(expr->cond_false, &ptr_ctype);
ctype = &ptr_ctype;
goto out;
}
if (is_null1 && (rclass & TYPE_PTR)) {
if (is_null1 == NULL_ZERO)
bad_null(*cond);
*cond = cast_to(*cond, rtype);
ctype = rtype;
goto out;
}
if (is_null2 && (lclass & TYPE_PTR)) {
if (is_null2 == NULL_ZERO)
bad_null(expr->cond_false);
expr->cond_false = cast_to(expr->cond_false, ltype);
ctype = ltype;
goto out;
}
if (!(lclass & rclass & TYPE_PTR)) {
typediff = "different types";
goto Err;
}
/* OK, it's pointer on pointer */
if (ltype->ctype.as != rtype->ctype.as) {
typediff = "different address spaces";
goto Err;
}
/* need to be lazier here */
lbase = examine_pointer_target(ltype);
rbase = examine_pointer_target(rtype);
qual = target_qualifiers(ltype) | target_qualifiers(rtype);
if (lbase == &void_ctype) {
/* XXX: pointers to function should warn here */
ctype = ltype;
goto Qual;
}
if (rbase == &void_ctype) {
/* XXX: pointers to function should warn here */
ctype = rtype;
goto Qual;
}
/* XXX: that should be pointer to composite */
ctype = ltype;
typediff = type_difference(<ype->ctype, &rtype->ctype,
qual, qual);
if (!typediff)
goto Qual;
goto Err;
}
/* void on void, struct on same struct, union on same union */
if (ltype == rtype) {
ctype = ltype;
goto out;
}
typediff = "different base types";
Err:
expression_error(expr, "incompatible types in conditional expression (%s):", typediff);
info(expr->pos, " %s", show_typename(ltype));
info(expr->pos, " %s", show_typename(rtype));
/*
* if the condition is constant, the type is in fact known
* so use it, as gcc & clang do.
*/
switch (expr_truth_value(expr->conditional)) {
case 1: expr->ctype = ltype;
break;
case 0: expr->ctype = rtype;
break;
default:
break;
}
return NULL;
out:
expr->ctype = ctype;
return ctype;
Qual:
if (qual & ~ctype->ctype.modifiers) {
struct symbol *sym = alloc_symbol(ctype->pos, SYM_PTR);
*sym = *ctype;
sym->ctype.modifiers |= qual;
ctype = sym;
}
*cond = cast_to(*cond, ctype);
expr->cond_false = cast_to(expr->cond_false, ctype);
goto out;
}
/* FP assignments can not do modulo or bit operations */
static int compatible_float_op(int op)
{
return op == SPECIAL_ADD_ASSIGN ||
op == SPECIAL_SUB_ASSIGN ||
op == SPECIAL_MUL_ASSIGN ||
op == SPECIAL_DIV_ASSIGN;
}
static int evaluate_assign_op(struct expression *expr)
{
struct symbol *target = expr->left->ctype;
struct symbol *source = expr->right->ctype;
struct symbol *t, *s;
int tclass = classify_type(target, &t);
int sclass = classify_type(source, &s);
int op = expr->op;
if (tclass & sclass & TYPE_NUM) {
if (tclass & TYPE_FLOAT && !compatible_float_op(op)) {
expression_error(expr, "invalid assignment");
return 0;
}
if (tclass & TYPE_RESTRICT) {
if (!restricted_binop(op, t)) {
warning(expr->pos, "bad assignment (%s) to %s",
show_special(op), show_typename(t));
expr->right = cast_to(expr->right, target);
return 0;
}
/* allowed assignments unfoul */
if (sclass & TYPE_FOULED && unfoul(s) == t)
goto Cast;
if (!restricted_value(expr->right, t))
return 1;
} else if (op == SPECIAL_SHR_ASSIGN || op == SPECIAL_SHL_ASSIGN) {
// shifts do integer promotions, but that's it.
unrestrict(expr->left, tclass, &t);
target = integer_promotion(t);
unrestrict(expr->right, sclass, &s);
source = integer_promotion(s);
expr->right = cast_to(expr->right, source);
// both gcc & clang seems to do this, so ...
if (target->bit_size > source->bit_size)
expr->right = cast_to(expr->right, &uint_ctype);
goto Cast;
} else if (!(sclass & TYPE_RESTRICT))
goto usual;
/* source and target would better be identical restricted */
if (t == s)
return 1;
warning(expr->pos, "invalid assignment: %s", show_special(op));
info(expr->pos, " left side has type %s", show_typename(t));
info(expr->pos, " right side has type %s", show_typename(s));
expr->right = cast_to(expr->right, target);
return 0;
}
if (tclass == TYPE_PTR && is_int(sclass)) {
if (op == SPECIAL_ADD_ASSIGN || op == SPECIAL_SUB_ASSIGN) {
unrestrict(expr->right, sclass, &s);
evaluate_ptr_add(expr, s);
return 1;
}
expression_error(expr, "invalid pointer assignment");
return 0;
}
expression_error(expr, "invalid assignment");
return 0;
usual:
target = usual_conversions(op, expr->left, expr->right,
tclass, sclass, target, source);
Cast:
expr->right = cast_to(expr->right, target);
return 1;
}
static int whitelist_pointers(struct symbol *t1, struct symbol *t2)
{
if (t1 == t2)
return 0; /* yes, 0 - we don't want a cast_to here */
if (t1 == &void_ctype)
return 1;
if (t2 == &void_ctype)
return 1;
if (classify_type(t1, &t1) != TYPE_NUM)
return 0;
if (classify_type(t2, &t2) != TYPE_NUM)
return 0;
if (t1 == t2)
return 1;
if (t1->rank == -2 && t2->rank == -2)
return 1;
if (t1->rank != t2->rank)
return 0;
return !Wtypesign;
}
static int check_assignment_types(struct symbol *target, struct expression **rp,
const char **typediff)
{
struct symbol *source = degenerate(*rp);
struct symbol *t, *s;
int tclass = classify_type(target, &t);
int sclass = classify_type(source, &s);
if (tclass & sclass & TYPE_NUM) {
if (tclass & TYPE_RESTRICT) {
/* allowed assignments unfoul */
if (sclass & TYPE_FOULED && unfoul(s) == t)
goto Cast;
if (!restricted_value(*rp, target))
goto Cast;
if (s == t)
return 1;
} else if (!(sclass & TYPE_RESTRICT))
goto Cast;
if (t == &bool_ctype) {
if (is_fouled_type(s))
warning((*rp)->pos, "%s degrades to integer",
show_typename(s->ctype.base_type));
goto Cast;
}
*typediff = "different base types";
return 0;
}
if (tclass == TYPE_PTR) {
unsigned long mod1, mod2;
unsigned long modl, modr;
struct symbol *b1, *b2;
// NULL pointer is always OK
int is_null = is_null_pointer_constant(*rp);
if (is_null) {
if (is_null == NULL_ZERO)
bad_null(*rp);
goto Cast;
}
if (!(sclass & TYPE_PTR)) {
*typediff = "different base types";
return 0;
}
b1 = examine_pointer_target(t);
b2 = examine_pointer_target(s);
mod1 = t->ctype.modifiers & MOD_IGN;
mod2 = s->ctype.modifiers & MOD_IGN;
if (whitelist_pointers(b1, b2)) {
/*
* assignments to/from void * are OK, provided that
* we do not remove qualifiers from pointed to [C]
* or mix address spaces [sparse].
*/
if (t->ctype.as != s->ctype.as) {
*typediff = "different address spaces";
return 0;
}
/*
* If this is a function pointer assignment, it is
* actually fine to assign a pointer to const data to
* it, as a function pointer points to const data
* implicitly, i.e., dereferencing it does not produce
* an lvalue.
*/
if (b1->type == SYM_FN)
mod1 |= MOD_CONST;
if (mod2 & ~mod1 & ~MOD_FUN_ATTR) {
*typediff = "different modifiers";
return 0;
}
goto Cast;
}
/* It's OK if the target is more volatile or const than the source */
/* It's OK if the source is more pure/noreturn than the target */
modr = mod1 & ~MOD_REV_QUAL;
modl = mod2 & MOD_REV_QUAL;
*typediff = type_difference(&t->ctype, &s->ctype, modl, modr);
if (*typediff)
return 0;
return 1;
}
if ((tclass & TYPE_COMPOUND) && s == t)
return 1;
if (tclass & TYPE_NUM) {
/* XXX: need to turn into comparison with NULL */
if (t == &bool_ctype && (sclass & TYPE_PTR))
goto Cast;
*typediff = "different base types";
return 0;
}
*typediff = "invalid types";
return 0;
Cast:
*rp = cast_to(*rp, target);
return 1;
}
static int compatible_assignment_types(struct expression *expr, struct symbol *target,
struct expression **rp, const char *where)
{
const char *typediff;
if (!check_assignment_types(target, rp, &typediff)) {
struct symbol *source = *rp ? (*rp)->ctype : NULL;
warning(expr->pos, "incorrect type in %s (%s)", where, typediff);
info(expr->pos, " expected %s", show_typename(target));
info(expr->pos, " got %s", show_typename(source));
*rp = cast_to(*rp, target);
return 0;
}
return 1;
}
static int compatible_transparent_union(struct symbol *target,
struct expression **rp)
{
struct symbol *t, *member;
classify_type(target, &t);
if (t->type != SYM_UNION || !t->transparent_union)
return 0;
FOR_EACH_PTR(t->symbol_list, member) {
const char *typediff;
if (check_assignment_types(member, rp, &typediff))
return 1;
} END_FOR_EACH_PTR(member);
return 0;
}
static int compatible_argument_type(struct expression *expr, struct symbol *target,
struct expression **rp, const char *where)
{
if (compatible_transparent_union(target, rp))
return 1;
return compatible_assignment_types(expr, target, rp, where);
}
static void mark_addressable(struct expression *expr)
{
while (expr->type == EXPR_BINOP && expr->op == '+')
expr = expr->left;
if (expr->type == EXPR_SYMBOL) {
struct symbol *sym = expr->symbol;
sym->ctype.modifiers |= MOD_ADDRESSABLE;
}
}
static void mark_assigned(struct expression *expr)
{
struct symbol *sym;
if (!expr)
return;
switch (expr->type) {
case EXPR_SYMBOL:
sym = expr->symbol;
if (!sym)
return;
if (sym->type != SYM_NODE)
return;
sym->ctype.modifiers |= MOD_ASSIGNED;
return;
case EXPR_BINOP:
mark_assigned(expr->left);
mark_assigned(expr->right);
return;
case EXPR_CAST:
case EXPR_FORCE_CAST:
mark_assigned(expr->cast_expression);
return;
case EXPR_SLICE:
mark_assigned(expr->base);
return;
default:
/* Hmm? */
return;
}
}
static void evaluate_assign_to(struct expression *left, struct symbol *type)
{
if (type->ctype.modifiers & MOD_CONST)
expression_error(left, "assignment to const expression");
/* We know left is an lvalue, so it's a "preop-*" */
mark_assigned(left->unop);
}
static struct symbol *evaluate_assignment(struct expression *expr)
{
struct expression *left = expr->left;
struct symbol *ltype;
if (!lvalue_expression(left)) {
expression_error(expr, "not an lvalue");
return NULL;
}
ltype = left->ctype;
if (expr->op != '=') {
if (!evaluate_assign_op(expr))
return NULL;
} else {
if (!compatible_assignment_types(expr, ltype, &expr->right, "assignment"))
return NULL;
}
evaluate_assign_to(left, ltype);
expr->ctype = ltype;
return ltype;
}
static void examine_fn_arguments(struct symbol *fn)
{
struct symbol *s;
FOR_EACH_PTR(fn->arguments, s) {
struct symbol *arg = evaluate_symbol(s);
/* Array/function arguments silently degenerate into pointers */
if (arg) {
struct symbol *ptr;
switch(arg->type) {
case SYM_ARRAY:
case SYM_FN:
ptr = alloc_symbol(s->pos, SYM_PTR);
if (arg->type == SYM_ARRAY)
ptr->ctype = arg->ctype;
else
ptr->ctype.base_type = arg;
combine_address_space(s->pos, &ptr->ctype.as, s->ctype.as);
ptr->ctype.modifiers |= s->ctype.modifiers & MOD_PTRINHERIT;
s->ctype.base_type = ptr;
s->ctype.as = NULL;
s->ctype.modifiers &= ~MOD_PTRINHERIT;
s->bit_size = 0;
s->examined = 0;
examine_symbol_type(s);
break;
default:
/* nothing */
break;
}
}
} END_FOR_EACH_PTR(s);
}
static struct symbol *convert_to_as_mod(struct symbol *sym, struct ident *as, int mod)
{
/* Take the modifiers of the pointer, and apply them to the member */
mod |= sym->ctype.modifiers;
if (sym->ctype.as != as || sym->ctype.modifiers != mod) {
struct symbol *newsym = alloc_symbol(sym->pos, SYM_NODE);
*newsym = *sym;
newsym->ctype.as = as;
newsym->ctype.modifiers = mod;
sym = newsym;
}
return sym;
}
static struct symbol *create_pointer(struct expression *expr, struct symbol *sym, int degenerate)
{
struct symbol *node = alloc_symbol(expr->pos, SYM_NODE);
struct symbol *ptr = alloc_symbol(expr->pos, SYM_PTR);
node->ctype.base_type = ptr;
ptr->bit_size = bits_in_pointer;
ptr->ctype.alignment = pointer_alignment;
node->bit_size = bits_in_pointer;
node->ctype.alignment = pointer_alignment;
access_symbol(sym);
if (sym->ctype.modifiers & MOD_REGISTER) {
warning(expr->pos, "taking address of 'register' variable '%s'", show_ident(sym->ident));
sym->ctype.modifiers &= ~MOD_REGISTER;
}
if (sym->type == SYM_NODE) {
combine_address_space(sym->pos, &ptr->ctype.as, sym->ctype.as);
ptr->ctype.modifiers |= sym->ctype.modifiers & MOD_PTRINHERIT;
sym = sym->ctype.base_type;
}
if (degenerate && sym->type == SYM_ARRAY) {
combine_address_space(sym->pos, &ptr->ctype.as, sym->ctype.as);
ptr->ctype.modifiers |= sym->ctype.modifiers & MOD_PTRINHERIT;
sym = sym->ctype.base_type;
}
ptr->ctype.base_type = sym;
return node;
}
/* Arrays degenerate into pointers on pointer arithmetic */
static struct symbol *degenerate(struct expression *expr)
{
struct symbol *ctype, *base;
if (!expr)
return NULL;
ctype = expr->ctype;
if (!ctype)
return NULL;
base = examine_symbol_type(ctype);
if (ctype->type == SYM_NODE)
base = ctype->ctype.base_type;
/*
* Arrays degenerate into pointers to the entries, while
* functions degenerate into pointers to themselves.
* If array was part of non-lvalue compound, we create a copy
* of that compound first and then act as if we were dealing with
* the corresponding field in there.
*/
switch (base->type) {
case SYM_ARRAY:
if (expr->type == EXPR_SLICE) {
struct symbol *a = alloc_symbol(expr->pos, SYM_NODE);
struct expression *e0, *e1, *e2, *e3, *e4;
a->ctype.base_type = expr->base->ctype;
a->bit_size = expr->base->ctype->bit_size;
a->array_size = expr->base->ctype->array_size;
e0 = alloc_expression(expr->pos, EXPR_SYMBOL);
e0->symbol = a;
e0->ctype = &lazy_ptr_ctype;
e1 = alloc_expression(expr->pos, EXPR_PREOP);
e1->unop = e0;
e1->op = '*';
e1->ctype = expr->base->ctype; /* XXX */
e2 = alloc_expression(expr->pos, EXPR_ASSIGNMENT);
e2->left = e1;
e2->right = expr->base;
e2->op = '=';
e2->ctype = expr->base->ctype;
if (expr->r_bitpos) {
e3 = alloc_expression(expr->pos, EXPR_BINOP);
e3->op = '+';
e3->left = e0;
e3->right = alloc_const_expression(expr->pos,
bits_to_bytes(expr->r_bitpos));
e3->ctype = &lazy_ptr_ctype;
} else {
e3 = e0;
}
e4 = alloc_expression(expr->pos, EXPR_COMMA);
e4->left = e2;
e4->right = e3;
e4->ctype = &lazy_ptr_ctype;
expr->unop = e4;
expr->type = EXPR_PREOP;
expr->op = '*';
}
case SYM_FN:
if (expr->op != '*' || expr->type != EXPR_PREOP) {
expression_error(expr, "strange non-value function or array");
return &bad_ctype;
}
if (ctype->builtin)
sparse_error(expr->pos, "taking the address of built-in function '%s'", show_ident(ctype->ident));
*expr = *expr->unop;
ctype = create_pointer(expr, ctype, 1);
expr->ctype = ctype;
mark_addressable(expr);
default:
/* nothing */;
}
return ctype;
}
static struct symbol *evaluate_addressof(struct expression *expr)
{
struct expression *op = expr->unop;
struct symbol *ctype;
if (op->op != '*' || op->type != EXPR_PREOP) {
expression_error(expr, "not addressable");
return NULL;
}
ctype = op->ctype;
if (ctype->builtin)
sparse_error(expr->pos, "taking the address of built-in function '%s'", show_ident(ctype->ident));
*expr = *op->unop;
mark_addressable(expr);
/*
* symbol expression evaluation is lazy about the type
* of the sub-expression, so we may have to generate
* the type here if so..
*/
if (expr->ctype == &lazy_ptr_ctype) {
ctype = create_pointer(expr, ctype, 0);
expr->ctype = ctype;
}
return expr->ctype;
}
static struct symbol *evaluate_dereference(struct expression *expr)
{
struct expression *op = expr->unop;
struct symbol *ctype = op->ctype, *node, *target;
/* Simplify: *&(expr) => (expr) */
if (op->type == EXPR_PREOP && op->op == '&') {
*expr = *op->unop;
expr->flags = CEF_NONE;
return expr->ctype;
}
examine_symbol_type(ctype);
/* Dereferencing a node drops all the node information. */
if (ctype->type == SYM_NODE)
ctype = ctype->ctype.base_type;
target = ctype->ctype.base_type;
switch (ctype->type) {
default:
expression_error(expr, "cannot dereference this type");
return NULL;
case SYM_FN:
*expr = *op;
return expr->ctype;
case SYM_PTR:
examine_symbol_type(target);
node = alloc_symbol(expr->pos, SYM_NODE);
node->ctype.modifiers = target->ctype.modifiers & MOD_SPECIFIER;
merge_type(node, ctype);
break;
case SYM_ARRAY:
if (!lvalue_expression(op)) {
expression_error(op, "non-lvalue array??");
return NULL;
}
/* Do the implied "addressof" on the array */
*op = *op->unop;
/*
* When an array is dereferenced, we need to pick
* up the attributes of the original node too..
*/
node = alloc_symbol(expr->pos, SYM_NODE);
merge_type(node, op->ctype);
merge_type(node, ctype);
break;
}
node->bit_size = target->bit_size;
node->array_size = target->array_size;
expr->ctype = node;
return node;
}
/*
* Unary post-ops: x++ and x--
*/
static struct symbol *evaluate_postop(struct expression *expr)
{
struct expression *op = expr->unop;
struct symbol *ctype = op->ctype;
int class = classify_type(ctype, &ctype);
int multiply = 0;
if (!class || class & TYPE_COMPOUND) {
expression_error(expr, "need scalar for ++/--");
return NULL;
}
if (!lvalue_expression(expr->unop)) {
expression_error(expr, "need lvalue expression for ++/--");
return NULL;
}
unrestrict(expr, class, &ctype);
if (class & TYPE_NUM) {
multiply = 1;
} else if (class == TYPE_PTR) {
struct symbol *target = examine_pointer_target(ctype);
if (!is_function(target))
multiply = bits_to_bytes(target->bit_size);
}
if (multiply) {
evaluate_assign_to(op, op->ctype);
expr->op_value = multiply;
expr->ctype = ctype;
return ctype;
}
expression_error(expr, "bad argument type for ++/--");
return NULL;
}
static struct symbol *evaluate_sign(struct expression *expr)
{
struct symbol *ctype = expr->unop->ctype;
int class = classify_type(ctype, &ctype);
unsigned char flags = expr->unop->flags & ~CEF_CONST_MASK;
/* should be an arithmetic type */
if (!(class & TYPE_NUM))
return bad_expr_type(expr);
if (class & TYPE_RESTRICT)
goto Restr;
Normal:
if (!(class & TYPE_FLOAT)) {
ctype = integer_promotion(ctype);
expr->unop = cast_to(expr->unop, ctype);
} else if (expr->op != '~') {
/* no conversions needed */
} else {
return bad_expr_type(expr);
}
if (expr->op == '+')
*expr = *expr->unop;
expr->flags = flags;
expr->ctype = ctype;
return ctype;
Restr:
if (restricted_unop(expr->op, &ctype))
unrestrict(expr, class, &ctype);
goto Normal;
}
static struct symbol *evaluate_preop(struct expression *expr)
{
struct symbol *ctype = expr->unop->ctype;
switch (expr->op) {
case '(':
*expr = *expr->unop;
return ctype;
case '+':
case '-':
case '~':
return evaluate_sign(expr);
case '*':
return evaluate_dereference(expr);
case '&':
return evaluate_addressof(expr);
case SPECIAL_INCREMENT:
case SPECIAL_DECREMENT:
/*
* From a type evaluation standpoint the preops are
* the same as the postops
*/
return evaluate_postop(expr);
case '!':
ctype = degenerate(expr->unop);
expr->flags = expr->unop->flags & ~CEF_CONST_MASK;
/*
* A logical negation never yields an address constant
* [6.6(9)].
*/
expr->flags &= ~CEF_ADDR;
if (is_safe_type(ctype))
warning(expr->pos, "testing a 'safe expression'");
if (is_float_type(ctype)) {
struct expression *arg = expr->unop;
expr->type = EXPR_COMPARE;
expr->op = SPECIAL_EQUAL;
expr->left = arg;
expr->right = alloc_expression(expr->pos, EXPR_FVALUE);
expr->right->ctype = ctype;
expr->right->fvalue = 0;
} else if (is_fouled_type(ctype)) {
warning(expr->pos, "%s degrades to integer",
show_typename(ctype->ctype.base_type));
}
/* the result is int [6.5.3.3(5)]*/
ctype = &int_ctype;
break;
default:
break;
}
expr->ctype = ctype;
return ctype;
}
static struct symbol *find_identifier(struct ident *ident, struct symbol_list *_list, int *offset)
{
struct ptr_list *head = (struct ptr_list *)_list;
struct ptr_list *list = head;
if (!head)
return NULL;
do {
int i;
for (i = 0; i < list->nr; i++) {
struct symbol *sym = (struct symbol *) list->list[i];
if (sym->ident) {
if (sym->ident != ident)
continue;
*offset = sym->offset;
return sym;
} else {
struct symbol *ctype = sym->ctype.base_type;
struct symbol *sub;
if (!ctype)
continue;
if (ctype->type != SYM_UNION && ctype->type != SYM_STRUCT)
continue;
sub = find_identifier(ident, ctype->symbol_list, offset);
if (!sub)
continue;
*offset += sym->offset;
return sub;
}
}
} while ((list = list->next) != head);
return NULL;
}
static struct expression *evaluate_offset(struct expression *expr, unsigned long offset)
{
struct expression *add;
/*
* Create a new add-expression
*
* NOTE! Even if we just add zero, we need a new node
* for the member pointer, since it has a different
* type than the original pointer. We could make that
* be just a cast, but the fact is, a node is a node,
* so we might as well just do the "add zero" here.
*/
add = alloc_expression(expr->pos, EXPR_BINOP);
add->op = '+';
add->left = expr;
add->right = alloc_expression(expr->pos, EXPR_VALUE);
add->right->ctype = &int_ctype;
add->right->value = offset;
/*
* The ctype of the pointer will be lazily evaluated if
* we ever take the address of this member dereference..
*/
add->ctype = &lazy_ptr_ctype;
/*
* The resulting address of a member access through an address
* constant is an address constant again [6.6(9)].
*/
add->flags = expr->flags;
return add;
}
/* structure/union dereference */
static struct symbol *evaluate_member_dereference(struct expression *expr)
{
int offset;
struct symbol *ctype, *member;
struct expression *deref = expr->deref, *add;
struct ident *ident = expr->member;
struct ident *address_space;
unsigned int mod;
if (!evaluate_expression(deref))
return NULL;
if (!ident) {
expression_error(expr, "bad member name");
return NULL;
}
ctype = deref->ctype;
examine_symbol_type(ctype);
address_space = ctype->ctype.as;
mod = ctype->ctype.modifiers;
if (ctype->type == SYM_NODE) {
ctype = ctype->ctype.base_type;
combine_address_space(deref->pos, &address_space, ctype->ctype.as);
mod |= ctype->ctype.modifiers;
}
if (!ctype || (ctype->type != SYM_STRUCT && ctype->type != SYM_UNION)) {
expression_error(expr, "expected structure or union");
return NULL;
}
offset = 0;
member = find_identifier(ident, ctype->symbol_list, &offset);
if (!member) {
const char *type = ctype->type == SYM_STRUCT ? "struct" : "union";
const char *name = "";
int namelen = 9;
if (ctype->ident) {
name = ctype->ident->name;
namelen = ctype->ident->len;
}
if (ctype->symbol_list)
expression_error(expr, "no member '%s' in %s %.*s",
show_ident(ident), type, namelen, name);
else
expression_error(expr, "using member '%s' in "
"incomplete %s %.*s", show_ident(ident),
type, namelen, name);
return NULL;
}
/*
* The member needs to take on the address space and modifiers of
* the "parent" type.
*/
member = convert_to_as_mod(member, address_space, mod);
ctype = get_base_type(member);
if (!lvalue_expression(deref)) {
if (deref->type != EXPR_SLICE) {
expr->base = deref;
expr->r_bitpos = 0;
} else {
expr->base = deref->base;
expr->r_bitpos = deref->r_bitpos;
}
expr->r_bitpos += bytes_to_bits(offset);
expr->type = EXPR_SLICE;
expr->r_bitpos += member->bit_offset;
expr->ctype = member;
return member;
}
deref = deref->unop;
expr->deref = deref;
add = evaluate_offset(deref, offset);
expr->type = EXPR_PREOP;
expr->op = '*';
expr->unop = add;
expr->ctype = member;
return member;
}
static int is_promoted(struct expression *expr)
{
while (1) {
switch (expr->type) {
case EXPR_BINOP:
case EXPR_SELECT:
case EXPR_CONDITIONAL:
return 1;
case EXPR_COMMA:
expr = expr->right;
continue;
case EXPR_PREOP:
switch (expr->op) {
case '(':
expr = expr->unop;
continue;
case '+':
case '-':
case '~':
return 1;
default:
return 0;
}
default:
return 0;
}
}
}
static struct symbol *evaluate_type_information(struct expression *expr)
{
struct symbol *sym = expr->cast_type;
if (!sym) {
sym = evaluate_expression(expr->cast_expression);
if (!sym)
return NULL;
/*
* Expressions of restricted types will possibly get
* promoted - check that here
*/
if (is_restricted_type(sym)) {
if (sym->bit_size < bits_in_int && is_promoted(expr))
sym = &int_ctype;
} else if (is_fouled_type(sym)) {
sym = &int_ctype;
}
}
examine_symbol_type(sym);
if (is_bitfield_type(sym)) {
expression_error(expr, "trying to examine bitfield type");
return NULL;
}
return sym;
}
static struct symbol *evaluate_sizeof(struct expression *expr)
{
struct symbol *type;
int size;
type = evaluate_type_information(expr);
if (!type)
return NULL;
size = type->bit_size;
if (size < 0 && is_void_type(type)) {
if (Wpointer_arith)
warning(expr->pos, "expression using sizeof(void)");
size = bits_in_char;
}
if (is_bool_type(type)) {
if (Wsizeof_bool)
warning(expr->pos, "expression using sizeof _Bool");
size = bits_to_bytes(bits_in_bool) * bits_in_char;
}
if (is_function(type->ctype.base_type)) {
if (Wpointer_arith)
warning(expr->pos, "expression using sizeof on a function");
size = bits_in_char;
}
if (has_flexible_array(type) && Wflexible_array_sizeof)
warning(expr->pos, "using sizeof on a flexible structure");
if (is_array_type(type) && size < 0) { // VLA, 1-dimension only
struct expression *base, *size;
struct symbol *base_type;
if (type->type == SYM_NODE)
type = type->ctype.base_type; // strip the SYM_NODE
base_type = get_base_type(type);
if (!base_type)
goto error;
if (base_type->bit_size <= 0) {
base = alloc_expression(expr->pos, EXPR_SIZEOF);
base->cast_type = base_type;
if (!evaluate_sizeof(base))
goto error;
} else {
base = alloc_expression(expr->pos, EXPR_VALUE);
base->value = bits_to_bytes(base_type->bit_size);
base->ctype = size_t_ctype;
}
size = alloc_expression(expr->pos, EXPR_CAST);
size->cast_type = size_t_ctype;
size->cast_expression = type->array_size;
if (!evaluate_expression(size))
goto error;
expr->left = size;
expr->right = base;
expr->type = EXPR_BINOP;
expr->op = '*';
return expr->ctype = size_t_ctype;
}
error:
if ((size < 0) || (size & (bits_in_char - 1)))
expression_error(expr, "cannot size expression");
expr->type = EXPR_VALUE;
expr->value = bits_to_bytes(size);
expr->taint = 0;
expr->ctype = size_t_ctype;
return size_t_ctype;
}
static struct symbol *evaluate_ptrsizeof(struct expression *expr)
{
struct symbol *type;
int size;
type = evaluate_type_information(expr);
if (!type)
return NULL;
if (type->type == SYM_NODE)
type = type->ctype.base_type;
if (!type)
return NULL;
switch (type->type) {
case SYM_ARRAY:
break;
case SYM_PTR:
type = get_base_type(type);
if (type)
break;
default:
expression_error(expr, "expected pointer expression");
return NULL;
}
size = type->bit_size;
if (size & (bits_in_char-1))
size = 0;
expr->type = EXPR_VALUE;
expr->value = bits_to_bytes(size);
expr->taint = 0;
expr->ctype = size_t_ctype;
return size_t_ctype;
}
static struct symbol *evaluate_alignof(struct expression *expr)
{
struct symbol *type;
type = evaluate_type_information(expr);
if (!type)
return NULL;
expr->type = EXPR_VALUE;
expr->value = type->ctype.alignment;
expr->taint = 0;
expr->ctype = size_t_ctype;
return size_t_ctype;
}
int evaluate_arguments(struct symbol_list *argtypes, struct expression_list *head)
{
struct expression *expr;
struct symbol *argtype;
int i = 1;
PREPARE_PTR_LIST(argtypes, argtype);
FOR_EACH_PTR (head, expr) {
struct expression **p = THIS_ADDRESS(expr);
struct symbol *ctype, *target;
ctype = evaluate_expression(expr);
if (!ctype)
return 0;
target = argtype;
if (!target) {
struct symbol *type;
int class = classify_type(ctype, &type);
if (is_int(class)) {
*p = cast_to(expr, integer_promotion(type));
} else if (class & TYPE_FLOAT) {
if (type->rank < 0)
*p = cast_to(expr, &double_ctype);
} else if (class & TYPE_PTR) {
if (expr->ctype == &null_ctype)
*p = cast_to(expr, &ptr_ctype);
else
degenerate(expr);
}
} else if (!target->forced_arg){
static char where[30];
examine_symbol_type(target);
sprintf(where, "argument %d", i);
compatible_argument_type(expr, target, p, where);
}
i++;
NEXT_PTR_LIST(argtype);
} END_FOR_EACH_PTR(expr);
FINISH_PTR_LIST(argtype);
return 1;
}
static void convert_index(struct expression *e)
{
struct expression *child = e->idx_expression;
unsigned from = e->idx_from;
unsigned to = e->idx_to + 1;
e->type = EXPR_POS;
e->init_offset = from * bits_to_bytes(e->ctype->bit_size);
e->init_nr = to - from;
e->init_expr = child;
}
static void convert_ident(struct expression *e)
{
struct expression *child = e->ident_expression;
int offset = e->offset;
e->type = EXPR_POS;
e->init_offset = offset;
e->init_nr = 1;
e->init_expr = child;
}
static void convert_designators(struct expression *e)
{
while (e) {
if (e->type == EXPR_INDEX)
convert_index(e);
else if (e->type == EXPR_IDENTIFIER)
convert_ident(e);
else
break;
e = e->init_expr;
}
}
static void excess(struct expression *e, const char *s)
{
warning(e->pos, "excessive elements in %s initializer", s);
}
/*
* implicit designator for the first element
*/
static struct expression *first_subobject(struct symbol *ctype, int class,
struct expression **v)
{
struct expression *e = *v, *new;
if (ctype->type == SYM_NODE)
ctype = ctype->ctype.base_type;
if (class & TYPE_PTR) { /* array */
if (!ctype->bit_size)
return NULL;
new = alloc_expression(e->pos, EXPR_INDEX);
new->idx_expression = e;
new->ctype = ctype->ctype.base_type;
} else {
struct symbol *field, *p;
PREPARE_PTR_LIST(ctype->symbol_list, p);
while (p && !p->ident && is_bitfield_type(p))
NEXT_PTR_LIST(p);
field = p;
FINISH_PTR_LIST(p);
if (!field)
return NULL;
new = alloc_expression(e->pos, EXPR_IDENTIFIER);
new->ident_expression = e;
new->field = new->ctype = field;
new->offset = field->offset;
}
*v = new;
return new;
}
/*
* sanity-check explicit designators; return the innermost one or NULL
* in case of error. Assign types.
*/
static struct expression *check_designators(struct expression *e,
struct symbol *ctype)
{
struct expression *last = NULL;
const char *err;
while (1) {
if (ctype->type == SYM_NODE)
ctype = ctype->ctype.base_type;
if (e->type == EXPR_INDEX) {
struct symbol *type;
if (ctype->type != SYM_ARRAY) {
err = "array index in non-array";
break;
}
type = ctype->ctype.base_type;
if (ctype->bit_size >= 0 && type->bit_size >= 0) {
unsigned offset = array_element_offset(type->bit_size, e->idx_to);
if (offset >= ctype->bit_size) {
err = "index out of bounds in";
break;
}
}
e->ctype = ctype = type;
ctype = type;
last = e;
if (!e->idx_expression) {
err = "invalid";
break;
}
e = e->idx_expression;
} else if (e->type == EXPR_IDENTIFIER) {
int offset = 0;
if (ctype->type != SYM_STRUCT && ctype->type != SYM_UNION) {
err = "field name not in struct or union";
break;
}
ctype = find_identifier(e->expr_ident, ctype->symbol_list, &offset);
if (!ctype) {
err = "unknown field name in";
break;
}
e->offset = offset;
e->field = e->ctype = ctype;
last = e;
if (!e->ident_expression) {
err = "invalid";
break;
}
e = e->ident_expression;
} else if (e->type == EXPR_POS) {
err = "internal front-end error: EXPR_POS in";
break;
} else
return last;
}
expression_error(e, "%s initializer", err);
return NULL;
}
/*
* choose the next subobject to initialize.
*
* Get designators for next element, switch old ones to EXPR_POS.
* Return the resulting expression or NULL if we'd run out of subobjects.
* The innermost designator is returned in *v. Designators in old
* are assumed to be already sanity-checked.
*/
static struct expression *next_designators(struct expression *old,
struct symbol *ctype,
struct expression *e, struct expression **v)
{
struct expression *new = NULL;
if (!old)
return NULL;
if (old->type == EXPR_INDEX) {
struct expression *copy;
unsigned n;
copy = next_designators(old->idx_expression,
old->ctype, e, v);
if (!copy) {
n = old->idx_to + 1;
if (array_element_offset(old->ctype->bit_size, n) == ctype->bit_size) {
convert_index(old);
return NULL;
}
copy = e;
*v = new = alloc_expression(e->pos, EXPR_INDEX);
} else {
n = old->idx_to;
new = alloc_expression(e->pos, EXPR_INDEX);
}
new->idx_from = new->idx_to = n;
new->idx_expression = copy;
new->ctype = old->ctype;
convert_index(old);
} else if (old->type == EXPR_IDENTIFIER) {
struct expression *copy;
struct symbol *field;
int offset = 0;
copy = next_designators(old->ident_expression,
old->ctype, e, v);
if (!copy) {
field = old->field->next_subobject;
if (!field) {
convert_ident(old);
return NULL;
}
copy = e;
*v = new = alloc_expression(e->pos, EXPR_IDENTIFIER);
/*
* We can't necessarily trust "field->offset",
* because the field might be in an anonymous
* union, and the field offset is then the offset
* within that union.
*
* The "old->offset - old->field->offset"
* would be the offset of such an anonymous
* union.
*/
offset = old->offset - old->field->offset;
} else {
field = old->field;
new = alloc_expression(e->pos, EXPR_IDENTIFIER);
}
new->field = field;
new->expr_ident = field->ident;
new->ident_expression = copy;
new->ctype = field;
new->offset = field->offset + offset;
convert_ident(old);
}
return new;
}
static int handle_initializer(struct expression **ep, int nested,
int class, struct symbol *ctype, unsigned long mods);
/*
* deal with traversing subobjects [6.7.8(17,18,20)]
*/
static void handle_list_initializer(struct expression *expr,
int class, struct symbol *ctype, unsigned long mods)
{
struct expression *e, *last = NULL, *top = NULL, *next;
int jumped = 0; // has the last designator multiple levels?
if (expr->zero_init)
free_ptr_list(&expr->expr_list);
FOR_EACH_PTR(expr->expr_list, e) {
struct expression **v;
struct symbol *type;
int lclass;
if (e->type != EXPR_INDEX && e->type != EXPR_IDENTIFIER) {
struct symbol *struct_sym;
if (!top) {
top = e;
last = first_subobject(ctype, class, &top);
} else {
last = next_designators(last, ctype, e, &top);
}
if (!last) {
excess(e, class & TYPE_PTR ? "array" :
"struct or union");
DELETE_CURRENT_PTR(e);
continue;
}
struct_sym = ctype->type == SYM_NODE ? ctype->ctype.base_type : ctype;
if (Wdesignated_init && struct_sym->designated_init)
warning(e->pos, "%s%.*s%spositional init of field in %s %s, declared with attribute designated_init",
ctype->ident ? "in initializer for " : "",
ctype->ident ? ctype->ident->len : 0,
ctype->ident ? ctype->ident->name : "",
ctype->ident ? ": " : "",
get_type_name(struct_sym->type),
show_ident(struct_sym->ident));
if (jumped && Wpast_deep_designator) {
warning(e->pos, "advancing past deep designator");
jumped = 0;
}
REPLACE_CURRENT_PTR(e, last);
} else {
next = check_designators(e, ctype);
if (!next) {
DELETE_CURRENT_PTR(e);
continue;
}
top = next;
/* deeper than one designator? */
jumped = top != e;
convert_designators(last);
last = e;
}
found:
lclass = classify_type(top->ctype, &type);
if (top->type == EXPR_INDEX)
v = &top->idx_expression;
else
v = &top->ident_expression;
mods |= ctype->ctype.modifiers & MOD_STORAGE;
if (handle_initializer(v, 1, lclass, top->ctype, mods))
continue;
if (!(lclass & TYPE_COMPOUND)) {
warning(e->pos, "bogus scalar initializer");
DELETE_CURRENT_PTR(e);
continue;
}
next = first_subobject(type, lclass, v);
if (next) {
warning(e->pos, "missing braces around initializer");
top = next;
goto found;
}
DELETE_CURRENT_PTR(e);
excess(e, lclass & TYPE_PTR ? "array" : "struct or union");
} END_FOR_EACH_PTR(e);
convert_designators(last);
expr->ctype = ctype;
}
static int is_string_literal(struct expression **v)
{
struct expression *e = *v;
while (e && e->type == EXPR_PREOP && e->op == '(')
e = e->unop;
if (!e || e->type != EXPR_STRING)
return 0;
if (e != *v && Wparen_string)
warning(e->pos,
"array initialized from parenthesized string constant");
*v = e;
return 1;
}
/*
* We want a normal expression, possibly in one layer of braces. Warn
* if the latter happens inside a list (it's legal, but likely to be
* an effect of screwup). In case of anything not legal, we are definitely
* having an effect of screwup, so just fail and let the caller warn.
*/
static struct expression *handle_scalar(struct expression *e, int nested)
{
struct expression *v = NULL, *p;
int count = 0;
/* normal case */
if (e->type != EXPR_INITIALIZER)
return e;
FOR_EACH_PTR(e->expr_list, p) {
if (!v)
v = p;
count++;
} END_FOR_EACH_PTR(p);
if (count != 1)
return NULL;
switch(v->type) {
case EXPR_INITIALIZER:
case EXPR_INDEX:
case EXPR_IDENTIFIER:
return NULL;
default:
break;
}
if (nested)
warning(e->pos, "braces around scalar initializer");
return v;
}
/*
* deal with the cases that don't care about subobjects:
* scalar <- assignment expression, possibly in braces [6.7.8(11)]
* character array <- string literal, possibly in braces [6.7.8(14)]
* struct or union <- assignment expression of compatible type [6.7.8(13)]
* compound type <- initializer list in braces [6.7.8(16)]
* The last one punts to handle_list_initializer() which, in turn will call
* us for individual elements of the list.
*
* We do not handle 6.7.8(15) (wide char array <- wide string literal) for
* the lack of support of wide char stuff in general.
*
* One note: we need to take care not to evaluate a string literal until
* we know that we *will* handle it right here. Otherwise we would screw
* the cases like struct { struct {char s[10]; ...} ...} initialized with
* { "string", ...} - we need to preserve that string literal recognizable
* until we dig into the inner struct.
*/
static int handle_initializer(struct expression **ep, int nested,
int class, struct symbol *ctype, unsigned long mods)
{
struct expression *e = *ep, *p;
struct symbol *type;
if (!e)
return 0;
/* scalar */
if (!(class & TYPE_COMPOUND)) {
e = handle_scalar(e, nested);
if (!e)
return 0;
*ep = e;
if (!evaluate_expression(e))
return 1;
compatible_assignment_types(e, ctype, ep, "initializer");
/*
* Initializers for static storage duration objects
* shall be constant expressions or a string literal [6.7.8(4)].
*/
mods |= ctype->ctype.modifiers;
mods &= (MOD_TOPLEVEL | MOD_STATIC);
if (mods && !(e->flags & (CEF_ACE | CEF_ADDR)))
if (Wconstexpr_not_const)
warning(e->pos, "non-constant initializer for static object");
return 1;
}
/*
* sublist; either a string, or we dig in; the latter will deal with
* pathologies, so we don't need anything fancy here.
*/
if (e->type == EXPR_INITIALIZER) {
if (is_string_type(ctype)) {
struct expression *v = NULL;
int count = 0;
FOR_EACH_PTR(e->expr_list, p) {
if (!v)
v = p;
count++;
} END_FOR_EACH_PTR(p);
if (count == 1 && is_string_literal(&v)) {
*ep = e = v;
goto String;
}
}
handle_list_initializer(e, class, ctype, mods);
return 1;
}
/* string */
if (is_string_literal(&e)) {
/* either we are doing array of char, or we'll have to dig in */
if (is_string_type(ctype)) {
*ep = e;
goto String;
}
return 0;
}
/* struct or union can be initialized by compatible */
if (class != TYPE_COMPOUND)
return 0;
type = evaluate_expression(e);
if (!type)
return 0;
if (ctype->type == SYM_NODE)
ctype = ctype->ctype.base_type;
if (type->type == SYM_NODE)
type = type->ctype.base_type;
if (ctype == type)
return 1;
return 0;
String:
p = alloc_expression(e->pos, EXPR_STRING);
*p = *e;
type = evaluate_expression(p);
if (ctype->bit_size != -1) {
struct symbol *char_type = e->wide ? wchar_ctype : &char_ctype;
unsigned int size_with_null = ctype->bit_size + char_type->bit_size;
if (size_with_null < type->bit_size)
warning(e->pos,
"too long initializer-string for array of char");
else if (Winit_cstring && size_with_null == type->bit_size) {
warning(e->pos,
"too long initializer-string for array of char(no space for nul char)");
}
}
*ep = p;
return 1;
}
static void evaluate_initializer(struct symbol *ctype, struct expression **ep)
{
struct symbol *type;
int class = classify_type(ctype, &type);
if (!handle_initializer(ep, 0, class, ctype, 0))
expression_error(*ep, "invalid initializer");
}
static struct symbol *cast_to_bool(struct expression *expr)
{
struct expression *old = expr->cast_expression;
struct expression *zero;
struct symbol *otype;
int oclass = classify_type(degenerate(old), &otype);
struct symbol *ctype;
if (oclass & TYPE_COMPOUND)
return NULL;
zero = alloc_const_expression(expr->pos, 0);
if (oclass & TYPE_PTR)
zero->ctype = otype;
expr->op = SPECIAL_NOTEQUAL;
ctype = usual_conversions(expr->op, old, zero,
oclass, TYPE_NUM, otype, zero->ctype);
expr->type = EXPR_COMPARE;
expr->left = cast_to(old, ctype);
expr->right = cast_to(zero, ctype);
return expr->ctype;
}
static int cast_flags(struct expression *expr, struct expression *old)
{
struct symbol *t;
int class;
int flags = CEF_NONE;
class = classify_type(expr->ctype, &t);
if (class & TYPE_NUM) {
flags = old->flags & ~CEF_CONST_MASK;
/*
* Casts to numeric types never result in address
* constants [6.6(9)].
*/
flags &= ~CEF_ADDR;
/*
* As an extension, treat address constants cast to
* integer type as an arithmetic constant.
*/
if (old->flags & CEF_ADDR)
flags = CEF_ACE;
/*
* Cast to float type -> not an integer constant
* expression [6.6(6)].
*/
if (class & TYPE_FLOAT)
flags &= ~CEF_CLR_ICE;
/*
* Casts of float literals to integer type results in
* a constant integer expression [6.6(6)].
*/
else if (old->flags & CEF_FLOAT)
flags = CEF_SET_ICE;
} else if (class & TYPE_PTR) {
/*
* Casts of integer literals to pointer type yield
* address constants [6.6(9)].
*
* As an extension, treat address constants cast to a
* different pointer type as address constants again.
*
* As another extension, treat integer constant
* expressions (in contrast to literals) cast to
* pointer type as address constants.
*/
if (old->flags & (CEF_ICE | CEF_ADDR))
flags = CEF_ADDR;
}
return flags;
}
///
// check if a type matches one of the members of a union type
// @utype: the union type
// @type: to type to check
// @return: to identifier of the matching type in the union.
static struct symbol *find_member_type(struct symbol *utype, struct symbol *type)
{
struct symbol *t, *member;
if (utype->type != SYM_UNION)
return NULL;
FOR_EACH_PTR(utype->symbol_list, member) {
classify_type(member, &t);
if (type == t)
return member;
} END_FOR_EACH_PTR(member);
return NULL;
}
static struct symbol *evaluate_compound_literal(struct expression *expr, struct expression *source)
{
struct expression *addr = alloc_expression(expr->pos, EXPR_SYMBOL);
struct symbol *sym = expr->cast_type;
sym->initializer = source;
evaluate_symbol(sym);
addr->ctype = &lazy_ptr_ctype; /* Lazy eval */
addr->symbol = sym;
if (sym->ctype.modifiers & MOD_TOPLEVEL)
addr->flags |= CEF_ADDR;
expr->type = EXPR_PREOP;
expr->op = '*';
expr->deref = addr;
expr->ctype = sym;
return sym;
}
static struct symbol *evaluate_cast(struct expression *expr)
{
struct expression *source = expr->cast_expression;
struct symbol *ctype;
struct symbol *ttype, *stype;
struct symbol *member;
int tclass, sclass;
struct ident *tas = NULL, *sas = NULL;
if (!source)
return NULL;
/*
* Special case: a cast can be followed by an
* initializer, in which case we need to pass
* the type value down to that initializer rather
* than trying to evaluate it as an expression
* (cfr. compound literals: C99 & C11 6.5.2.5).
*
* A more complex case is when the initializer is
* dereferenced as part of a post-fix expression.
* We need to produce an expression that can be dereferenced.
*/
if (source->type == EXPR_INITIALIZER)
return evaluate_compound_literal(expr, source);
ctype = examine_symbol_type(expr->cast_type);
ctype = unqualify_type(ctype);
expr->ctype = ctype;
expr->cast_type = ctype;
evaluate_expression(source);
degenerate(source);
tclass = classify_type(ctype, &ttype);
expr->flags = cast_flags(expr, source);
/*
* You can always throw a value away by casting to
* "void" - that's an implicit "force". Note that
* the same is _not_ true of "void *".
*/
if (ttype == &void_ctype)
goto out;
stype = source->ctype;
if (!stype) {
expression_error(expr, "cast from unknown type");
goto out;
}
sclass = classify_type(stype, &stype);
if (expr->type == EXPR_FORCE_CAST)
goto out;
if (tclass & (TYPE_COMPOUND | TYPE_FN)) {
/*
* Special case: cast to union type (GCC extension)
* The effect is similar to a compound literal except
* that the result is a rvalue.
*/
if ((member = find_member_type(ttype, stype))) {
struct expression *item, *init;
if (Wunion_cast)
warning(expr->pos, "cast to union type");
item = alloc_expression(source->pos, EXPR_IDENTIFIER);
item->expr_ident = member->ident;
item->ident_expression = source;
init = alloc_expression(source->pos, EXPR_INITIALIZER);
add_expression(&init->expr_list, item);
// FIXME: this should be a rvalue
evaluate_compound_literal(expr, init);
return ctype;
}
warning(expr->pos, "cast to non-scalar");
}
if (sclass & TYPE_COMPOUND)
warning(expr->pos, "cast from non-scalar");
/* allowed cast unfouls */
if (sclass & TYPE_FOULED)
stype = unfoul(stype);
if (ttype != stype) {
if ((tclass & TYPE_RESTRICT) && restricted_value(source, ttype))
warning(expr->pos, "cast to %s",
show_typename(ttype));
if (sclass & TYPE_RESTRICT) {
if (ttype == &bool_ctype) {
if (sclass & TYPE_FOULED)
warning(expr->pos, "%s degrades to integer",
show_typename(stype));
} else {
warning(expr->pos, "cast from %s",
show_typename(stype));
}
}
}
if ((ttype == &ulong_ctype || ttype == uintptr_ctype) && !Wcast_from_as)
tas = &bad_address_space;
else if (tclass == TYPE_PTR) {
examine_pointer_target(ttype);
tas = ttype->ctype.as;
}
if ((stype == &ulong_ctype || stype == uintptr_ctype))
sas = &bad_address_space;
else if (sclass == TYPE_PTR) {
examine_pointer_target(stype);
sas = stype->ctype.as;
}
if (!tas && valid_as(sas))
warning(expr->pos, "cast removes address space '%s' of expression", show_as(sas));
if (valid_as(tas) && valid_as(sas) && tas != sas)
warning(expr->pos, "cast between address spaces (%s -> %s)", show_as(sas), show_as(tas));
if (valid_as(tas) && !sas &&
!is_null_pointer_constant(source) && Wcast_to_as)
warning(expr->pos,
"cast adds address space '%s' to expression", show_as(tas));
if (!(ttype->ctype.modifiers & MOD_PTRINHERIT) && tclass == TYPE_PTR &&
!tas && (source->flags & CEF_ICE)) {
if (ttype->ctype.base_type == &void_ctype) {
if (is_zero_constant(source)) {
/* NULL */
expr->type = EXPR_VALUE;
expr->ctype = &null_ctype;
expr->value = 0;
return expr->ctype;
}
}
}
if (ttype == &bool_ctype)
cast_to_bool(expr);
// checks pointers to restricted
while (Wbitwise_pointer && tclass == TYPE_PTR && sclass == TYPE_PTR) {
tclass = classify_type(ttype->ctype.base_type, &ttype);
sclass = classify_type(stype->ctype.base_type, &stype);
if (ttype == stype)
break;
if (!ttype || !stype)
break;
if (ttype == &void_ctype || stype == &void_ctype)
break;
if (tclass & TYPE_RESTRICT) {
warning(expr->pos, "cast to %s", show_typename(ctype));
break;
}
if (sclass & TYPE_RESTRICT) {
warning(expr->pos, "cast from %s", show_typename(source->ctype));
break;
}
}
out:
return ctype;
}
/*
* Evaluate a call expression with a symbol. This
* should expand inline functions, and evaluate
* builtins.
*/
static int evaluate_symbol_call(struct expression *expr)
{
struct expression *fn = expr->fn;
struct symbol *ctype = fn->ctype;
if (fn->type != EXPR_PREOP)
return 0;
if (ctype->op && ctype->op->evaluate)
return ctype->op->evaluate(expr);
return 0;
}
static struct symbol *evaluate_call(struct expression *expr)
{
int args, fnargs;
struct symbol *ctype, *sym;
struct expression *fn = expr->fn;
struct expression_list *arglist = expr->args;
if (!evaluate_expression(fn))
return NULL;
sym = ctype = fn->ctype;
if (ctype->type == SYM_NODE)
ctype = ctype->ctype.base_type;
if (ctype->type == SYM_PTR)
ctype = get_base_type(ctype);
if (ctype->type != SYM_FN) {
struct expression *arg;
if (fn->ctype == &bad_ctype)
return NULL;
expression_error(expr, "not a function %s",
show_ident(sym->ident));
/* do typechecking in arguments */
FOR_EACH_PTR (arglist, arg) {
evaluate_expression(arg);
} END_FOR_EACH_PTR(arg);
return NULL;
}
examine_fn_arguments(ctype);
if (sym->type == SYM_NODE && fn->type == EXPR_PREOP &&
sym->op && sym->op->args) {
if (!sym->op->args(expr))
return NULL;
} else {
if (!evaluate_arguments(ctype->arguments, arglist))
return NULL;
args = expression_list_size(expr->args);
fnargs = symbol_list_size(ctype->arguments);
if (args < fnargs) {
expression_error(expr,
"not enough arguments for function %s",
show_ident(sym->ident));
return NULL;
}
if (args > fnargs && !ctype->variadic)
expression_error(expr,
"too many arguments for function %s",
show_ident(sym->ident));
}
expr->ctype = ctype->ctype.base_type;
if (sym->type == SYM_NODE) {
if (evaluate_symbol_call(expr))
return expr->ctype;
}
return expr->ctype;
}
static struct symbol *evaluate_offsetof(struct expression *expr)
{
struct expression *e = expr->down;
struct symbol *ctype = expr->in;
int class;
if (expr->op == '.') {
struct symbol *field;
int offset = 0;
if (!ctype) {
expression_error(expr, "expected structure or union");
return NULL;
}
examine_symbol_type(ctype);
class = classify_type(ctype, &ctype);
if (class != TYPE_COMPOUND) {
expression_error(expr, "expected structure or union");
return NULL;
}
field = find_identifier(expr->ident, ctype->symbol_list, &offset);
if (!field) {
expression_error(expr, "unknown member");
return NULL;
}
ctype = field;
expr->type = EXPR_VALUE;
expr->flags = CEF_SET_ICE;
expr->value = offset;
expr->taint = 0;
expr->ctype = size_t_ctype;
} else {
if (!ctype) {
expression_error(expr, "expected structure or union");
return NULL;
}
examine_symbol_type(ctype);
class = classify_type(ctype, &ctype);
if (class != (TYPE_COMPOUND | TYPE_PTR)) {
expression_error(expr, "expected array");
return NULL;
}
ctype = ctype->ctype.base_type;
if (!expr->index) {
expr->type = EXPR_VALUE;
expr->flags = CEF_SET_ICE;
expr->value = 0;
expr->taint = 0;
expr->ctype = size_t_ctype;
} else {
struct expression *idx = expr->index, *m;
struct symbol *i_type = evaluate_expression(idx);
unsigned old_idx_flags;
int i_class = classify_type(i_type, &i_type);
if (!is_int(i_class)) {
expression_error(expr, "non-integer index");
return NULL;
}
unrestrict(idx, i_class, &i_type);
old_idx_flags = idx->flags;
idx = cast_to(idx, size_t_ctype);
idx->flags = old_idx_flags;
m = alloc_const_expression(expr->pos,
bits_to_bytes(ctype->bit_size));
m->ctype = size_t_ctype;
m->flags = CEF_SET_INT;
expr->type = EXPR_BINOP;
expr->left = idx;
expr->right = m;
expr->op = '*';
expr->ctype = size_t_ctype;
expr->flags = m->flags & idx->flags & ~CEF_CONST_MASK;
}
}
if (e) {
struct expression *copy = __alloc_expression(0);
*copy = *expr;
if (e->type == EXPR_OFFSETOF)
e->in = ctype;
if (!evaluate_expression(e))
return NULL;
expr->type = EXPR_BINOP;
expr->flags = e->flags & copy->flags & ~CEF_CONST_MASK;
expr->op = '+';
expr->ctype = size_t_ctype;
expr->left = copy;
expr->right = e;
}
return size_t_ctype;
}
static void check_label_declaration(struct position pos, struct symbol *label)
{
switch (label->namespace) {
case NS_LABEL:
if (label->stmt)
break;
sparse_error(pos, "label '%s' was not declared", show_ident(label->ident));
/* fallthrough */
case NS_NONE:
current_fn->bogus_linear = 1;
default:
break;
}
}
static int type_selection(struct symbol *ctrl, struct symbol *type)
{
struct ctype c = { .base_type = ctrl };
struct ctype t = { .base_type = type };
return !type_difference(&c, &t, 0, 0);
}
static struct symbol *evaluate_generic_selection(struct expression *expr)
{
struct type_expression *map;
struct expression *res;
struct symbol source;
struct symbol *ctrl;
if (!evaluate_expression(expr->control))
return NULL;
if (!(ctrl = degenerate(expr->control)))
return NULL;
source = *ctrl;
source.ctype.modifiers &= ~(MOD_QUALIFIER|MOD_ATOMIC);
for (map = expr->map; map; map = map->next) {
struct symbol *stype = map->type;
struct symbol *base;
if (!evaluate_symbol(stype))
continue;
base = stype->ctype.base_type;
if (base->type == SYM_ARRAY && base->array_size) {
get_expression_value_silent(base->array_size);
if (base->array_size->type == EXPR_VALUE)
continue;
sparse_error(stype->pos, "variable length array type in generic selection");
continue;
}
if (is_func_type(stype)) {
sparse_error(stype->pos, "function type in generic selection");
continue;
}
if (stype->bit_size <= 0 || is_void_type(stype)) {
sparse_error(stype->pos, "incomplete type in generic selection");
continue;
}
if (!type_selection(&source, stype))
continue;
res = map->expr;
goto found;
}
res = expr->def;
if (!res) {
sparse_error(expr->pos, "no generic selection for '%s'", show_typename(ctrl));
return NULL;
}
found:
*expr = *res;
return evaluate_expression(expr);
}
struct symbol *evaluate_expression(struct expression *expr)
{
if (!expr)
return NULL;
if (expr->ctype)
return expr->ctype;
switch (expr->type) {
case EXPR_VALUE:
case EXPR_FVALUE:
expression_error(expr, "value expression without a type");
return NULL;
case EXPR_STRING:
return evaluate_string(expr);
case EXPR_SYMBOL:
return evaluate_symbol_expression(expr);
case EXPR_BINOP:
evaluate_expression(expr->left);
evaluate_expression(expr->right);
if (!valid_subexpr_type(expr))
return NULL;
return evaluate_binop(expr);
case EXPR_LOGICAL:
return evaluate_logical(expr);
case EXPR_COMMA:
evaluate_expression(expr->left);
if (!evaluate_expression(expr->right))
return NULL;
return evaluate_comma(expr);
case EXPR_COMPARE:
evaluate_expression(expr->left);
evaluate_expression(expr->right);
if (!valid_subexpr_type(expr))
return NULL;
return evaluate_compare(expr);
case EXPR_ASSIGNMENT:
evaluate_expression(expr->left);
evaluate_expression(expr->right);
if (!valid_subexpr_type(expr))
return NULL;
return evaluate_assignment(expr);
case EXPR_PREOP:
if (!evaluate_expression(expr->unop))
return NULL;
return evaluate_preop(expr);
case EXPR_POSTOP:
if (!evaluate_expression(expr->unop))
return NULL;
return evaluate_postop(expr);
case EXPR_CAST:
case EXPR_FORCE_CAST:
case EXPR_IMPLIED_CAST:
return evaluate_cast(expr);
case EXPR_SIZEOF:
return evaluate_sizeof(expr);
case EXPR_PTRSIZEOF:
return evaluate_ptrsizeof(expr);
case EXPR_ALIGNOF:
return evaluate_alignof(expr);
case EXPR_DEREF:
return evaluate_member_dereference(expr);
case EXPR_CALL:
return evaluate_call(expr);
case EXPR_SELECT:
case EXPR_CONDITIONAL:
return evaluate_conditional_expression(expr);
case EXPR_STATEMENT:
expr->ctype = evaluate_statement(expr->statement);
return expr->ctype;
case EXPR_LABEL:
expr->ctype = &ptr_ctype;
check_label_declaration(expr->pos, expr->label_symbol);
return &ptr_ctype;
case EXPR_TYPE:
/* Evaluate the type of the symbol .. */
evaluate_symbol(expr->symbol);
/* .. but the type of the _expression_ is a "type" */
expr->ctype = &type_ctype;
return &type_ctype;
case EXPR_OFFSETOF:
return evaluate_offsetof(expr);
case EXPR_GENERIC:
return evaluate_generic_selection(expr);
/* These can not exist as stand-alone expressions */
case EXPR_INITIALIZER:
case EXPR_IDENTIFIER:
case EXPR_INDEX:
case EXPR_POS:
expression_error(expr, "internal front-end error: initializer in expression");
return NULL;
case EXPR_SLICE:
expression_error(expr, "internal front-end error: SLICE re-evaluated");
return NULL;
}
return NULL;
}
void check_duplicates(struct symbol *sym)
{
int declared = 0;
struct symbol *next = sym;
int initialized = sym->initializer != NULL;
while ((next = next->same_symbol) != NULL) {
const char *typediff;
evaluate_symbol(next);
if (initialized && next->initializer) {
sparse_error(sym->pos, "symbol '%s' has multiple initializers (originally initialized at %s:%d)",
show_ident(sym->ident),
stream_name(next->pos.stream), next->pos.line);
/* Only warn once */
initialized = 0;
}
declared++;
typediff = type_difference(&sym->ctype, &next->ctype, 0, 0);
if (typediff) {
sparse_error(sym->pos, "symbol '%s' redeclared with different type (%s):",
show_ident(sym->ident), typediff);
info(sym->pos, " %s", show_typename(sym));
info(next->pos, "note: previously declared as:");
info(next->pos, " %s", show_typename(next));
return;
}
}
if (!declared) {
unsigned long mod = sym->ctype.modifiers;
if (mod & (MOD_STATIC | MOD_REGISTER | MOD_EXT_VISIBLE))
return;
if (!(mod & MOD_TOPLEVEL))
return;
if (!Wdecl)
return;
if (sym->ident == &main_ident)
return;
warning(sym->pos, "symbol '%s' was not declared. Should it be static?", show_ident(sym->ident));
}
}
static struct symbol *evaluate_symbol(struct symbol *sym)
{
struct symbol *base_type;
if (!sym)
return sym;
if (sym->evaluated)
return sym;
sym->evaluated = 1;
sym = examine_symbol_type(sym);
base_type = get_base_type(sym);
if (!base_type)
return NULL;
/* Evaluate the initializers */
if (sym->initializer)
evaluate_initializer(sym, &sym->initializer);
/* And finally, evaluate the body of the symbol too */
if (base_type->type == SYM_FN) {
struct symbol *curr = current_fn;
if (sym->definition && sym->definition != sym)
return evaluate_symbol(sym->definition);
current_fn = sym;
examine_fn_arguments(base_type);
if (!base_type->stmt && base_type->inline_stmt)
uninline(sym);
if (base_type->stmt)
evaluate_statement(base_type->stmt);
current_fn = curr;
}
return base_type;
}
void evaluate_symbol_list(struct symbol_list *list)
{
struct symbol *sym;
FOR_EACH_PTR(list, sym) {
has_error &= ~ERROR_CURR_PHASE;
evaluate_symbol(sym);
check_duplicates(sym);
} END_FOR_EACH_PTR(sym);
}
static struct symbol *evaluate_return_expression(struct statement *stmt)
{
struct expression *expr = stmt->expression;
struct symbol *fntype, *rettype;
evaluate_expression(expr);
fntype = current_fn->ctype.base_type;
rettype = fntype->ctype.base_type;
if (!rettype || rettype == &void_ctype) {
if (expr && expr->ctype && !is_void_type(expr->ctype))
expression_error(expr, "return expression in %s function", rettype?"void":"typeless");
if (expr && Wreturn_void)
warning(stmt->pos, "returning void-valued expression");
return NULL;
}
if (!expr) {
sparse_error(stmt->pos, "return with no return value");
return NULL;
}
if (!expr->ctype)
return NULL;
compatible_assignment_types(expr, rettype, &stmt->expression, "return expression");
return NULL;
}
static void evaluate_if_statement(struct statement *stmt)
{
if (!stmt->if_conditional)
return;
evaluate_conditional(stmt->if_conditional, 0);
evaluate_statement(stmt->if_true);
evaluate_statement(stmt->if_false);
}
static void evaluate_iterator(struct statement *stmt)
{
evaluate_symbol_list(stmt->iterator_syms);
evaluate_conditional(stmt->iterator_pre_condition, 1);
evaluate_conditional(stmt->iterator_post_condition,1);
evaluate_statement(stmt->iterator_pre_statement);
evaluate_statement(stmt->iterator_statement);
evaluate_statement(stmt->iterator_post_statement);
}
static void parse_asm_constraint(struct asm_operand *op)
{
struct expression *constraint = op->constraint;
const char *str = constraint->string->data;
int c;
switch (str[0]) {
case '\0':
sparse_error(constraint->pos, "invalid ASM constraint (\"\")");
break;
case '+':
op->is_modify = true;
/* fall-through */
case '=':
op->is_assign = true;
str++;
break;
}
while ((c = *str++)) {
switch (c) {
case '=':
case '+':
sparse_error(constraint->pos, "invalid ASM constraint '%c'", c);
break;
case '&':
op->is_earlyclobber = true;
break;
case '%':
op->is_commutative = true;
break;
case 'r':
op->is_register = true;
break;
case 'm':
case 'o':
case 'V':
case 'Q':
op->is_memory = true;
break;
case '<':
case '>':
// FIXME: ignored for now
break;
case ',':
// FIXME: multiple alternative constraints
break;
case '0' ... '9':
// FIXME: numeric matching constraint?
break;
case '[':
// FIXME: symbolic matching constraint
return;
default:
if (arch_target->asm_constraint)
str = arch_target->asm_constraint(op, c, str);
// FIXME: multi-letter constraints
break;
}
}
// FIXME: how to deal with multi-constraint?
if (op->is_register)
op->is_memory = 0;
}
static void verify_output_constraint(struct asm_operand *op)
{
struct expression *expr = op->constraint;
const char *constraint = expr->string->data;
if (!op->is_assign)
expression_error(expr, "output constraint is not an assignment constraint (\"%s\")", constraint);
}
static void verify_input_constraint(struct asm_operand *op)
{
struct expression *expr = op->constraint;
const char *constraint = expr->string->data;
if (op->is_assign)
expression_error(expr, "input constraint with assignment (\"%s\")", constraint);
}
static void evaluate_asm_memop(struct asm_operand *op)
{
if (op->is_memory) {
struct expression *expr = op->expr;
struct expression *addr;
// implicit addressof
addr = alloc_expression(expr->pos, EXPR_PREOP);
addr->op = '&';
addr->unop = expr;
evaluate_addressof(addr);
op->expr = addr;
} else {
evaluate_expression(op->expr);
degenerate(op->expr);
}
}
static void evaluate_asm_statement(struct statement *stmt)
{
struct expression *expr;
struct asm_operand *op;
struct symbol *sym;
if (!stmt->asm_string)
return;
FOR_EACH_PTR(stmt->asm_outputs, op) {
/* Identifier */
/* Constraint */
if (op->constraint) {
parse_asm_constraint(op);
verify_output_constraint(op);
}
/* Expression */
expr = op->expr;
if (!evaluate_expression(expr))
return;
if (!lvalue_expression(expr))
warning(expr->pos, "asm output is not an lvalue");
evaluate_assign_to(expr, expr->ctype);
evaluate_asm_memop(op);
} END_FOR_EACH_PTR(op);
FOR_EACH_PTR(stmt->asm_inputs, op) {
/* Identifier */
/* Constraint */
if (op->constraint) {
parse_asm_constraint(op);
verify_input_constraint(op);
}
/* Expression */
if (!evaluate_expression(op->expr))
return;
evaluate_asm_memop(op);
} END_FOR_EACH_PTR(op);
FOR_EACH_PTR(stmt->asm_clobbers, expr) {
if (!expr) {
sparse_error(stmt->pos, "bad asm clobbers");
return;
}
if (expr->type == EXPR_STRING)
continue;
expression_error(expr, "asm clobber is not a string");
} END_FOR_EACH_PTR(expr);
FOR_EACH_PTR(stmt->asm_labels, sym) {
if (!sym || sym->type != SYM_LABEL) {
sparse_error(stmt->pos, "bad asm label");
return;
}
} END_FOR_EACH_PTR(sym);
}
static void evaluate_case_statement(struct statement *stmt)
{
evaluate_expression(stmt->case_expression);
evaluate_expression(stmt->case_to);
evaluate_statement(stmt->case_statement);
}
static void check_case_type(struct expression *switch_expr,
struct expression *case_expr,
struct expression **enumcase)
{
struct symbol *switch_type, *case_type;
int sclass, cclass;
if (!case_expr)
return;
switch_type = switch_expr->ctype;
case_type = evaluate_expression(case_expr);
if (!switch_type || !case_type)
goto Bad;
if (enumcase) {
if (*enumcase)
warn_for_different_enum_types(case_expr->pos, case_type, (*enumcase)->ctype);
else if (is_enum_type(case_type))
*enumcase = case_expr;
}
sclass = classify_type(switch_type, &switch_type);
cclass = classify_type(case_type, &case_type);
/* both should be arithmetic */
if (!(sclass & cclass & TYPE_NUM))
goto Bad;
/* neither should be floating */
if ((sclass | cclass) & TYPE_FLOAT)
goto Bad;
/* if neither is restricted, we are OK */
if (!((sclass | cclass) & TYPE_RESTRICT))
return;
if (!restricted_binop_type(SPECIAL_EQUAL, case_expr, switch_expr,
cclass, sclass, case_type, switch_type)) {
unrestrict(case_expr, cclass, &case_type);
unrestrict(switch_expr, sclass, &switch_type);
}
return;
Bad:
expression_error(case_expr, "incompatible types for 'case' statement");
}
static void evaluate_switch_statement(struct statement *stmt)
{
struct symbol *sym;
struct expression *enumcase = NULL;
struct expression **enumcase_holder = &enumcase;
struct expression *sel = stmt->switch_expression;
evaluate_expression(sel);
evaluate_statement(stmt->switch_statement);
if (!sel)
return;
if (sel->ctype && is_enum_type(sel->ctype))
enumcase_holder = NULL; /* Only check cases against switch */
FOR_EACH_PTR(stmt->switch_case->symbol_list, sym) {
struct statement *case_stmt = sym->stmt;
check_case_type(sel, case_stmt->case_expression, enumcase_holder);
check_case_type(sel, case_stmt->case_to, enumcase_holder);
} END_FOR_EACH_PTR(sym);
}
static void evaluate_goto_statement(struct statement *stmt)
{
struct symbol *label = stmt->goto_label;
if (!label) {
// no label associated, may be a computed goto
evaluate_expression(stmt->goto_expression);
return;
}
check_label_declaration(stmt->pos, label);
}
struct symbol *evaluate_statement(struct statement *stmt)
{
if (!stmt)
return NULL;
switch (stmt->type) {
case STMT_DECLARATION: {
struct symbol *s;
FOR_EACH_PTR(stmt->declaration, s) {
evaluate_symbol(s);
} END_FOR_EACH_PTR(s);
return NULL;
}
case STMT_RETURN:
return evaluate_return_expression(stmt);
case STMT_EXPRESSION:
if (!evaluate_expression(stmt->expression))
return NULL;
if (stmt->expression->ctype == &null_ctype)
stmt->expression = cast_to(stmt->expression, &ptr_ctype);
return unqualify_type(degenerate(stmt->expression));
case STMT_COMPOUND: {
struct statement *s;
struct symbol *type = NULL;
/* Evaluate the return symbol in the compound statement */
evaluate_symbol(stmt->ret);
/*
* Then, evaluate each statement, making the type of the
* compound statement be the type of the last statement
*/
type = evaluate_statement(stmt->args);
FOR_EACH_PTR(stmt->stmts, s) {
type = evaluate_statement(s);
} END_FOR_EACH_PTR(s);
if (!type)
type = &void_ctype;
return type;
}
case STMT_IF:
evaluate_if_statement(stmt);
return NULL;
case STMT_ITERATOR:
evaluate_iterator(stmt);
return NULL;
case STMT_SWITCH:
evaluate_switch_statement(stmt);
return NULL;
case STMT_CASE:
evaluate_case_statement(stmt);
return NULL;
case STMT_LABEL:
return evaluate_statement(stmt->label_statement);
case STMT_GOTO:
evaluate_goto_statement(stmt);
return NULL;
case STMT_NONE:
break;
case STMT_ASM:
evaluate_asm_statement(stmt);
return NULL;
case STMT_CONTEXT:
evaluate_expression(stmt->expression);
return NULL;
case STMT_RANGE:
evaluate_expression(stmt->range_expression);
evaluate_expression(stmt->range_low);
evaluate_expression(stmt->range_high);
return NULL;
}
return NULL;
}
sparse-0.6.4/evaluate.h 0000664 0000000 0000000 00000001715 14115310122 0014767 0 ustar 00root root 0000000 0000000 #ifndef EVALUATE_H
#define EVALUATE_H
struct expression;
struct expression_list;
struct statement;
struct symbol;
struct symbol_list;
///
// evaluate the type of an expression
// @expr: the expression to be evaluated
// @return: the type of the expression or ``NULL``
// if the expression can't be evaluated
struct symbol *evaluate_expression(struct expression *expr);
///
// evaluate the type of a statement
// @stmt: the statement to be evaluated
// @return: the type of the statement or ``NULL``
// if it can't be evaluated
struct symbol *evaluate_statement(struct statement *stmt);
///
// evaluate the type of a set of symbols
// @list: the list of the symbol to be evaluated
void evaluate_symbol_list(struct symbol_list *list);
///
// evaluate the arguments of a function
// @argtypes: the list of the types in the prototype
// @args: the list of the effective arguments
int evaluate_arguments(struct symbol_list *argtypes, struct expression_list *args);
#endif
sparse-0.6.4/example.c 0000664 0000000 0000000 00000130177 14115310122 0014614 0 ustar 00root root 0000000 0000000 /*
* Example of how to write a compiler with sparse
*/
#include
#include
#include
#include
#include
#include "symbol.h"
#include "expression.h"
#include "linearize.h"
#include "flow.h"
#include "storage.h"
#include "target.h"
static const char *opcodes[] = {
[OP_BADOP] = "bad_op",
/* Fn entrypoint */
[OP_ENTRY] = "",
/* Terminator */
[OP_RET] = "ret",
[OP_BR] = "br",
[OP_CBR] = "cbr",
[OP_SWITCH] = "switch",
[OP_COMPUTEDGOTO] = "jmp *",
/* Binary */
[OP_ADD] = "add",
[OP_SUB] = "sub",
[OP_MUL] = "mul",
[OP_DIVU] = "divu",
[OP_DIVS] = "divs",
[OP_MODU] = "modu",
[OP_MODS] = "mods",
[OP_SHL] = "shl",
[OP_LSR] = "lsr",
[OP_ASR] = "asr",
/* Logical */
[OP_AND] = "and",
[OP_OR] = "or",
[OP_XOR] = "xor",
/* Binary comparison */
[OP_SET_EQ] = "seteq",
[OP_SET_NE] = "setne",
[OP_SET_LE] = "setle",
[OP_SET_GE] = "setge",
[OP_SET_LT] = "setlt",
[OP_SET_GT] = "setgt",
[OP_SET_B] = "setb",
[OP_SET_A] = "seta",
[OP_SET_BE] = "setbe",
[OP_SET_AE] = "setae",
/* Uni */
[OP_NOT] = "not",
[OP_NEG] = "neg",
/* Special three-input */
[OP_SEL] = "select",
/* Memory */
[OP_LOAD] = "load",
[OP_STORE] = "store",
[OP_LABEL] = "label",
[OP_SETVAL] = "set",
/* Other */
[OP_PHI] = "phi",
[OP_PHISOURCE] = "phisrc",
[OP_COPY] = "copy",
[OP_SEXT] = "sext",
[OP_ZEXT] = "zext",
[OP_TRUNC] = "trunc",
[OP_FCVTU] = "fcvtu",
[OP_FCVTS] = "fcvts",
[OP_UCVTF] = "ucvtf",
[OP_SCVTF] = "scvtf",
[OP_FCVTF] = "fcvtf",
[OP_UTPTR] = "utptr",
[OP_PTRTU] = "utptr",
[OP_PTRCAST] = "ptrcast",
[OP_CALL] = "call",
[OP_SLICE] = "slice",
[OP_NOP] = "nop",
[OP_DEATHNOTE] = "dead",
[OP_ASM] = "asm",
/* Sparse tagging (line numbers, context, whatever) */
[OP_CONTEXT] = "context",
};
static int last_reg, stack_offset;
struct hardreg {
const char *name;
struct pseudo_list *contains;
unsigned busy:16,
dead:8,
used:1;
};
#define TAG_DEAD 1
#define TAG_DIRTY 2
/* Our "switch" generation is very very stupid. */
#define SWITCH_REG (1)
static void output_bb(struct basic_block *bb, unsigned long generation);
/*
* We only know about the caller-clobbered registers
* right now.
*/
static struct hardreg hardregs[] = {
{ .name = "%eax" },
{ .name = "%edx" },
{ .name = "%ecx" },
{ .name = "%ebx" },
{ .name = "%esi" },
{ .name = "%edi" },
{ .name = "%ebp" },
{ .name = "%esp" },
};
#define REGNO 6
#define REG_EBP 6
#define REG_ESP 7
struct bb_state {
struct position pos;
struct storage_hash_list *inputs;
struct storage_hash_list *outputs;
struct storage_hash_list *internal;
/* CC cache.. */
int cc_opcode, cc_dead;
pseudo_t cc_target;
};
enum optype {
OP_UNDEF,
OP_REG,
OP_VAL,
OP_MEM,
OP_ADDR,
};
struct operand {
enum optype type;
int size;
union {
struct hardreg *reg;
long long value;
struct /* OP_MEM and OP_ADDR */ {
unsigned int offset;
unsigned int scale;
struct symbol *sym;
struct hardreg *base;
struct hardreg *index;
};
};
};
static const char *show_op(struct bb_state *state, struct operand *op)
{
static char buf[256][4];
static int bufnr;
char *p, *ret;
int nr;
nr = (bufnr + 1) & 3;
bufnr = nr;
ret = p = buf[nr];
switch (op->type) {
case OP_UNDEF:
return "undef";
case OP_REG:
return op->reg->name;
case OP_VAL:
sprintf(p, "$%lld", op->value);
break;
case OP_MEM:
case OP_ADDR:
if (op->offset)
p += sprintf(p, "%d", op->offset);
if (op->sym)
p += sprintf(p, "%s%s",
op->offset ? "+" : "",
show_ident(op->sym->ident));
if (op->base || op->index) {
p += sprintf(p, "(%s%s%s",
op->base ? op->base->name : "",
(op->base && op->index) ? "," : "",
op->index ? op->index->name : "");
if (op->scale > 1)
p += sprintf(p, ",%d", op->scale);
*p++ = ')';
*p = '\0';
}
break;
}
return ret;
}
static struct storage_hash *find_storage_hash(pseudo_t pseudo, struct storage_hash_list *list)
{
struct storage_hash *entry;
FOR_EACH_PTR(list, entry) {
if (entry->pseudo == pseudo)
return entry;
} END_FOR_EACH_PTR(entry);
return NULL;
}
static struct storage_hash *find_or_create_hash(pseudo_t pseudo, struct storage_hash_list **listp)
{
struct storage_hash *entry;
entry = find_storage_hash(pseudo, *listp);
if (!entry) {
entry = alloc_storage_hash(alloc_storage());
entry->pseudo = pseudo;
add_ptr_list(listp, entry);
}
return entry;
}
/* Eventually we should just build it up in memory */
static void FORMAT_ATTR(2) output_line(struct bb_state *state, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vprintf(fmt, args);
va_end(args);
}
static void FORMAT_ATTR(2) output_label(struct bb_state *state, const char *fmt, ...)
{
static char buffer[512];
va_list args;
va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args);
output_line(state, "%s:\n", buffer);
}
static void FORMAT_ATTR(2) output_insn(struct bb_state *state, const char *fmt, ...)
{
static char buffer[512];
va_list args;
va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args);
output_line(state, "\t%s\n", buffer);
}
#define output_insn(state, fmt, arg...) \
output_insn(state, fmt "\t\t# %s" , ## arg , __FUNCTION__)
static void FORMAT_ATTR(2) output_comment(struct bb_state *state, const char *fmt, ...)
{
static char buffer[512];
va_list args;
if (!verbose)
return;
va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args);
output_line(state, "\t# %s\n", buffer);
}
static const char *show_memop(struct storage *storage)
{
static char buffer[1000];
if (!storage)
return "undef";
switch (storage->type) {
case REG_FRAME:
sprintf(buffer, "%d(FP)", storage->offset);
break;
case REG_STACK:
sprintf(buffer, "%d(SP)", storage->offset);
break;
case REG_REG:
return hardregs[storage->regno].name;
default:
return show_storage(storage);
}
return buffer;
}
static int alloc_stack_offset(int size)
{
int ret = stack_offset;
stack_offset = ret + size;
return ret;
}
static void alloc_stack(struct bb_state *state, struct storage *storage)
{
storage->type = REG_STACK;
storage->offset = alloc_stack_offset(4);
}
/*
* Can we re-generate the pseudo, so that we don't need to
* flush it to memory? We can regenerate:
* - immediates and symbol addresses
* - pseudos we got as input in non-registers
* - pseudos we've already saved off earlier..
*/
static int can_regenerate(struct bb_state *state, pseudo_t pseudo)
{
struct storage_hash *in;
switch (pseudo->type) {
case PSEUDO_VAL:
case PSEUDO_SYM:
return 1;
default:
in = find_storage_hash(pseudo, state->inputs);
if (in && in->storage->type != REG_REG)
return 1;
in = find_storage_hash(pseudo, state->internal);
if (in)
return 1;
}
return 0;
}
static void flush_one_pseudo(struct bb_state *state, struct hardreg *hardreg, pseudo_t pseudo)
{
struct storage_hash *out;
struct storage *storage;
if (can_regenerate(state, pseudo))
return;
output_comment(state, "flushing %s from %s", show_pseudo(pseudo), hardreg->name);
out = find_storage_hash(pseudo, state->internal);
if (!out) {
out = find_storage_hash(pseudo, state->outputs);
if (!out)
out = find_or_create_hash(pseudo, &state->internal);
}
storage = out->storage;
switch (storage->type) {
default:
/*
* Aieee - the next user wants it in a register, but we
* need to flush it to memory in between. Which means that
* we need to allocate an internal one, dammit..
*/
out = find_or_create_hash(pseudo, &state->internal);
storage = out->storage;
/* Fall through */
case REG_UDEF:
alloc_stack(state, storage);
/* Fall through */
case REG_STACK:
output_insn(state, "movl %s,%s", hardreg->name, show_memop(storage));
break;
}
}
/* Flush a hardreg out to the storage it has.. */
static void flush_reg(struct bb_state *state, struct hardreg *reg)
{
pseudo_t pseudo;
if (reg->busy)
output_comment(state, "reg %s flushed while busy is %d!", reg->name, reg->busy);
if (!reg->contains)
return;
reg->dead = 0;
reg->used = 1;
FOR_EACH_PTR_TAG(reg->contains, pseudo) {
if (CURRENT_TAG(pseudo) & TAG_DEAD)
continue;
if (!(CURRENT_TAG(pseudo) & TAG_DIRTY))
continue;
flush_one_pseudo(state, reg, pseudo);
} END_FOR_EACH_PTR(pseudo);
free_ptr_list(®->contains);
}
static struct storage_hash *find_pseudo_storage(struct bb_state *state, pseudo_t pseudo, struct hardreg *reg)
{
struct storage_hash *src;
src = find_storage_hash(pseudo, state->internal);
if (!src) {
src = find_storage_hash(pseudo, state->inputs);
if (!src) {
src = find_storage_hash(pseudo, state->outputs);
/* Undefined? Screw it! */
if (!src)
return NULL;
/*
* If we found output storage, it had better be local stack
* that we flushed to earlier..
*/
if (src->storage->type != REG_STACK)
return NULL;
}
}
/*
* Incoming pseudo with out any pre-set storage allocation?
* We can make up our own, and obviously prefer to get it
* in the register we already selected (if it hasn't been
* used yet).
*/
if (src->storage->type == REG_UDEF) {
if (reg && !reg->used) {
src->storage->type = REG_REG;
src->storage->regno = reg - hardregs;
return NULL;
}
alloc_stack(state, src->storage);
}
return src;
}
static void mark_reg_dead(struct bb_state *state, pseudo_t pseudo, struct hardreg *reg)
{
pseudo_t p;
FOR_EACH_PTR_TAG(reg->contains, p) {
if (p != pseudo)
continue;
if (CURRENT_TAG(p) & TAG_DEAD)
continue;
output_comment(state, "marking pseudo %s in reg %s dead", show_pseudo(pseudo), reg->name);
TAG_CURRENT(p, TAG_DEAD);
reg->dead++;
} END_FOR_EACH_PTR(p);
}
static void add_pseudo_reg(struct bb_state *state, pseudo_t pseudo, struct hardreg *reg)
{
output_comment(state, "added pseudo %s to reg %s", show_pseudo(pseudo), reg->name);
add_ptr_list_tag(®->contains, pseudo, TAG_DIRTY);
}
static struct hardreg *preferred_reg(struct bb_state *state, pseudo_t target)
{
struct storage_hash *dst;
dst = find_storage_hash(target, state->outputs);
if (dst) {
struct storage *storage = dst->storage;
if (storage->type == REG_REG)
return hardregs + storage->regno;
}
return NULL;
}
static struct hardreg *empty_reg(struct bb_state *state)
{
int i;
struct hardreg *reg = hardregs;
for (i = 0; i < REGNO; i++, reg++) {
if (!reg->contains)
return reg;
}
return NULL;
}
static struct hardreg *target_reg(struct bb_state *state, pseudo_t pseudo, pseudo_t target)
{
int i;
int unable_to_find_reg = 0;
struct hardreg *reg;
/* First, see if we have a preferred target register.. */
reg = preferred_reg(state, target);
if (reg && !reg->contains)
goto found;
reg = empty_reg(state);
if (reg)
goto found;
i = last_reg;
do {
i++;
if (i >= REGNO)
i = 0;
reg = hardregs + i;
if (!reg->busy) {
flush_reg(state, reg);
last_reg = i;
goto found;
}
} while (i != last_reg);
assert(unable_to_find_reg);
found:
add_pseudo_reg(state, pseudo, reg);
return reg;
}
static struct hardreg *find_in_reg(struct bb_state *state, pseudo_t pseudo)
{
int i;
struct hardreg *reg;
for (i = 0; i < REGNO; i++) {
pseudo_t p;
reg = hardregs + i;
FOR_EACH_PTR_TAG(reg->contains, p) {
if (p == pseudo) {
last_reg = i;
output_comment(state, "found pseudo %s in reg %s (busy=%d)", show_pseudo(pseudo), reg->name, reg->busy);
return reg;
}
} END_FOR_EACH_PTR(p);
}
return NULL;
}
static void flush_pseudo(struct bb_state *state, pseudo_t pseudo, struct storage *storage)
{
struct hardreg *reg = find_in_reg(state, pseudo);
if (reg)
flush_reg(state, reg);
}
static void flush_cc_cache_to_reg(struct bb_state *state, pseudo_t pseudo, struct hardreg *reg)
{
int opcode = state->cc_opcode;
state->cc_opcode = 0;
state->cc_target = NULL;
output_insn(state, "%s %s", opcodes[opcode], reg->name);
}
static void flush_cc_cache(struct bb_state *state)
{
pseudo_t pseudo = state->cc_target;
if (pseudo) {
struct hardreg *dst;
state->cc_target = NULL;
if (!state->cc_dead) {
dst = target_reg(state, pseudo, pseudo);
flush_cc_cache_to_reg(state, pseudo, dst);
}
}
}
static void add_cc_cache(struct bb_state *state, int opcode, pseudo_t pseudo)
{
assert(!state->cc_target);
state->cc_target = pseudo;
state->cc_opcode = opcode;
state->cc_dead = 0;
output_comment(state, "caching %s", opcodes[opcode]);
}
/* Fill a hardreg with the pseudo it has */
static struct hardreg *fill_reg(struct bb_state *state, struct hardreg *hardreg, pseudo_t pseudo)
{
struct storage_hash *src;
struct instruction *def;
if (state->cc_target == pseudo) {
flush_cc_cache_to_reg(state, pseudo, hardreg);
return hardreg;
}
switch (pseudo->type) {
case PSEUDO_VAL:
output_insn(state, "movl $%lld,%s", pseudo->value, hardreg->name);
break;
case PSEUDO_SYM:
src = find_pseudo_storage(state, pseudo, NULL);
/* Static thing? */
if (!src) {
output_insn(state, "movl $<%s>,%s", show_pseudo(pseudo), hardreg->name);
break;
}
switch (src->storage->type) {
case REG_REG:
/* Aiaiaiaiaii! Need to flush it to temporary memory */
src = find_or_create_hash(pseudo, &state->internal);
/* Fall through */
default:
alloc_stack(state, src->storage);
/* Fall through */
case REG_STACK:
case REG_FRAME:
flush_pseudo(state, pseudo, src->storage);
output_insn(state, "leal %s,%s", show_memop(src->storage), hardreg->name);
break;
}
break;
case PSEUDO_ARG:
case PSEUDO_REG:
def = pseudo->def;
if (def && (def->opcode == OP_SETVAL || def->opcode == OP_LABEL)) {
output_insn(state, "movl $<%s>,%s", show_pseudo(def->target), hardreg->name);
break;
}
src = find_pseudo_storage(state, pseudo, hardreg);
if (!src)
break;
if (src->flags & TAG_DEAD)
mark_reg_dead(state, pseudo, hardreg);
output_insn(state, "mov.%d %s,%s", 32, show_memop(src->storage), hardreg->name);
break;
default:
output_insn(state, "reload %s from %s", hardreg->name, show_pseudo(pseudo));
break;
}
return hardreg;
}
static struct hardreg *getreg(struct bb_state *state, pseudo_t pseudo, pseudo_t target)
{
struct hardreg *reg;
reg = find_in_reg(state, pseudo);
if (reg)
return reg;
reg = target_reg(state, pseudo, target);
return fill_reg(state, reg, pseudo);
}
static void move_reg(struct bb_state *state, struct hardreg *src, struct hardreg *dst)
{
output_insn(state, "movl %s,%s", src->name, dst->name);
}
static struct hardreg *copy_reg(struct bb_state *state, struct hardreg *src, pseudo_t target)
{
int i;
struct hardreg *reg;
/* If the container has been killed off, just re-use it */
if (!src->contains)
return src;
/* If "src" only has one user, and the contents are dead, we can re-use it */
if (src->busy == 1 && src->dead == 1)
return src;
reg = preferred_reg(state, target);
if (reg && !reg->contains) {
output_comment(state, "copying %s to preferred target %s", show_pseudo(target), reg->name);
move_reg(state, src, reg);
return reg;
}
for (i = 0; i < REGNO; i++) {
reg = hardregs + i;
if (!reg->contains) {
output_comment(state, "copying %s to %s", show_pseudo(target), reg->name);
output_insn(state, "movl %s,%s", src->name, reg->name);
return reg;
}
}
flush_reg(state, src);
return src;
}
static void put_operand(struct bb_state *state, struct operand *op)
{
switch (op->type) {
case OP_REG:
op->reg->busy--;
break;
case OP_ADDR:
case OP_MEM:
if (op->base)
op->base->busy--;
if (op->index)
op->index->busy--;
break;
default:
break;
}
}
static struct operand *alloc_op(void)
{
struct operand *op = malloc(sizeof(*op));
memset(op, 0, sizeof(*op));
return op;
}
static struct operand *get_register_operand(struct bb_state *state, pseudo_t pseudo, pseudo_t target)
{
struct operand *op = alloc_op();
op->type = OP_REG;
op->reg = getreg(state, pseudo, target);
op->reg->busy++;
return op;
}
static int get_sym_frame_offset(struct bb_state *state, pseudo_t pseudo)
{
int offset = pseudo->nr;
if (offset < 0) {
offset = alloc_stack_offset(4);
pseudo->nr = offset;
}
return offset;
}
static struct operand *get_generic_operand(struct bb_state *state, pseudo_t pseudo)
{
struct hardreg *reg;
struct storage *src;
struct storage_hash *hash;
struct operand *op = malloc(sizeof(*op));
memset(op, 0, sizeof(*op));
switch (pseudo->type) {
case PSEUDO_VAL:
op->type = OP_VAL;
op->value = pseudo->value;
break;
case PSEUDO_SYM: {
struct symbol *sym = pseudo->sym;
op->type = OP_ADDR;
if (sym->ctype.modifiers & MOD_NONLOCAL) {
op->sym = sym;
break;
}
op->base = hardregs + REG_EBP;
op->offset = get_sym_frame_offset(state, pseudo);
break;
}
default:
reg = find_in_reg(state, pseudo);
if (reg) {
op->type = OP_REG;
op->reg = reg;
reg->busy++;
break;
}
hash = find_pseudo_storage(state, pseudo, NULL);
if (!hash)
break;
src = hash->storage;
switch (src->type) {
case REG_REG:
op->type = OP_REG;
op->reg = hardregs + src->regno;
op->reg->busy++;
break;
case REG_FRAME:
op->type = OP_MEM;
op->offset = src->offset;
op->base = hardregs + REG_EBP;
break;
case REG_STACK:
op->type = OP_MEM;
op->offset = src->offset;
op->base = hardregs + REG_ESP;
break;
default:
break;
}
}
return op;
}
/* Callers should be made to use the proper "operand" formats */
static const char *generic(struct bb_state *state, pseudo_t pseudo)
{
struct hardreg *reg;
struct operand *op = get_generic_operand(state, pseudo);
static char buf[100];
const char *str;
switch (op->type) {
case OP_ADDR:
if (!op->offset && op->base && !op->sym)
return op->base->name;
if (op->sym && !op->base) {
int len = sprintf(buf, "$ %s", show_op(state, op));
if (op->offset)
sprintf(buf + len, " + %d", op->offset);
return buf;
}
str = show_op(state, op);
put_operand(state, op);
reg = target_reg(state, pseudo, NULL);
output_insn(state, "lea %s,%s", show_op(state, op), reg->name);
return reg->name;
default:
str = show_op(state, op);
}
put_operand(state, op);
return str;
}
static struct operand *get_address_operand(struct bb_state *state, struct instruction *memop)
{
struct hardreg *base;
struct operand *op = get_generic_operand(state, memop->src);
switch (op->type) {
case OP_ADDR:
op->offset += memop->offset;
break;
default:
put_operand(state, op);
base = getreg(state, memop->src, NULL);
op->type = OP_ADDR;
op->base = base;
base->busy++;
op->offset = memop->offset;
op->sym = NULL;
}
return op;
}
static const char *address(struct bb_state *state, struct instruction *memop)
{
struct operand *op = get_address_operand(state, memop);
const char *str = show_op(state, op);
put_operand(state, op);
return str;
}
static const char *reg_or_imm(struct bb_state *state, pseudo_t pseudo)
{
switch(pseudo->type) {
case PSEUDO_VAL:
return show_pseudo(pseudo);
default:
return getreg(state, pseudo, NULL)->name;
}
}
static void kill_dead_reg(struct hardreg *reg)
{
if (reg->dead) {
pseudo_t p;
FOR_EACH_PTR_TAG(reg->contains, p) {
if (CURRENT_TAG(p) & TAG_DEAD) {
DELETE_CURRENT_PTR(p);
reg->dead--;
}
} END_FOR_EACH_PTR(p);
PACK_PTR_LIST(®->contains);
assert(!reg->dead);
}
}
static struct hardreg *target_copy_reg(struct bb_state *state, struct hardreg *src, pseudo_t target)
{
kill_dead_reg(src);
return copy_reg(state, src, target);
}
static void do_binop(struct bb_state *state, struct instruction *insn, pseudo_t val1, pseudo_t val2)
{
const char *op = opcodes[insn->opcode];
struct operand *src = get_register_operand(state, val1, insn->target);
struct operand *src2 = get_generic_operand(state, val2);
struct hardreg *dst;
dst = target_copy_reg(state, src->reg, insn->target);
output_insn(state, "%s.%d %s,%s", op, insn->size, show_op(state, src2), dst->name);
put_operand(state, src);
put_operand(state, src2);
add_pseudo_reg(state, insn->target, dst);
}
static void generate_binop(struct bb_state *state, struct instruction *insn)
{
flush_cc_cache(state);
do_binop(state, insn, insn->src1, insn->src2);
}
static int is_dead_reg(struct bb_state *state, pseudo_t pseudo, struct hardreg *reg)
{
pseudo_t p;
FOR_EACH_PTR_TAG(reg->contains, p) {
if (p == pseudo)
return CURRENT_TAG(p) & TAG_DEAD;
} END_FOR_EACH_PTR(p);
return 0;
}
/*
* Commutative binops are much more flexible, since we can switch the
* sources around to satisfy the target register, or to avoid having
* to load one of them into a register..
*/
static void generate_commutative_binop(struct bb_state *state, struct instruction *insn)
{
pseudo_t src1, src2;
struct hardreg *reg1, *reg2;
flush_cc_cache(state);
src1 = insn->src1;
src2 = insn->src2;
reg2 = find_in_reg(state, src2);
if (!reg2)
goto dont_switch;
reg1 = find_in_reg(state, src1);
if (!reg1)
goto do_switch;
if (!is_dead_reg(state, src2, reg2))
goto dont_switch;
if (!is_dead_reg(state, src1, reg1))
goto do_switch;
/* Both are dead. Is one preferable? */
if (reg2 != preferred_reg(state, insn->target))
goto dont_switch;
do_switch:
src1 = src2;
src2 = insn->src1;
dont_switch:
do_binop(state, insn, src1, src2);
}
/*
* This marks a pseudo dead. It still stays on the hardreg list (the hardreg
* still has its value), but it's scheduled to be killed after the next
* "sequence point" when we call "kill_read_pseudos()"
*/
static void mark_pseudo_dead(struct bb_state *state, pseudo_t pseudo)
{
int i;
struct storage_hash *src;
if (state->cc_target == pseudo)
state->cc_dead = 1;
src = find_pseudo_storage(state, pseudo, NULL);
if (src)
src->flags |= TAG_DEAD;
for (i = 0; i < REGNO; i++)
mark_reg_dead(state, pseudo, hardregs + i);
}
static void kill_dead_pseudos(struct bb_state *state)
{
int i;
for (i = 0; i < REGNO; i++) {
kill_dead_reg(hardregs + i);
}
}
static void generate_store(struct instruction *insn, struct bb_state *state)
{
output_insn(state, "mov.%d %s,%s", insn->size, reg_or_imm(state, insn->target), address(state, insn));
}
static void generate_load(struct instruction *insn, struct bb_state *state)
{
const char *input = address(state, insn);
struct hardreg *dst;
kill_dead_pseudos(state);
dst = target_reg(state, insn->target, NULL);
output_insn(state, "mov.%d %s,%s", insn->size, input, dst->name);
}
static void kill_pseudo(struct bb_state *state, pseudo_t pseudo)
{
int i;
struct hardreg *reg;
output_comment(state, "killing pseudo %s", show_pseudo(pseudo));
for (i = 0; i < REGNO; i++) {
pseudo_t p;
reg = hardregs + i;
FOR_EACH_PTR_TAG(reg->contains, p) {
if (p != pseudo)
continue;
if (CURRENT_TAG(p) & TAG_DEAD)
reg->dead--;
output_comment(state, "removing pseudo %s from reg %s",
show_pseudo(pseudo), reg->name);
DELETE_CURRENT_PTR(p);
} END_FOR_EACH_PTR(p);
PACK_PTR_LIST(®->contains);
}
}
static void generate_copy(struct bb_state *state, struct instruction *insn)
{
struct hardreg *src = getreg(state, insn->src, insn->target);
kill_pseudo(state, insn->target);
add_pseudo_reg(state, insn->target, src);
}
static void generate_cast(struct bb_state *state, struct instruction *insn)
{
struct hardreg *src = getreg(state, insn->src, insn->target);
struct hardreg *dst;
unsigned int old = insn->orig_type ? insn->orig_type->bit_size : 0;
unsigned int new = insn->size;
/*
* Cast to smaller type? Ignore the high bits, we
* just keep both pseudos in the same register.
*/
if (old >= new) {
add_pseudo_reg(state, insn->target, src);
return;
}
dst = target_copy_reg(state, src, insn->target);
if (insn->orig_type && (insn->orig_type->ctype.modifiers & MOD_SIGNED)) {
output_insn(state, "sext.%d.%d %s", old, new, dst->name);
} else {
unsigned long long mask;
mask = ~(~0ULL << old);
mask &= ~(~0ULL << new);
output_insn(state, "andl.%d $%#llx,%s", insn->size, mask, dst->name);
}
add_pseudo_reg(state, insn->target, dst);
}
static void generate_output_storage(struct bb_state *state);
static const char *conditional[] = {
[OP_SET_EQ] = "e",
[OP_SET_NE] = "ne",
[OP_SET_LE] = "le",
[OP_SET_GE] = "ge",
[OP_SET_LT] = "lt",
[OP_SET_GT] = "gt",
[OP_SET_B] = "b",
[OP_SET_A] = "a",
[OP_SET_BE] = "be",
[OP_SET_AE] = "ae"
};
static void generate_branch(struct bb_state *state, struct instruction *br)
{
const char *cond = "XXX";
struct basic_block *target;
if (br->cond) {
if (state->cc_target == br->cond) {
cond = conditional[state->cc_opcode];
} else {
struct hardreg *reg = getreg(state, br->cond, NULL);
output_insn(state, "testl %s,%s", reg->name, reg->name);
cond = "ne";
}
}
generate_output_storage(state);
target = br->bb_true;
if (br->cond) {
output_insn(state, "j%s .L%p", cond, target);
target = br->bb_false;
}
output_insn(state, "jmp .L%p", target);
}
/* We've made sure that there is a dummy reg live for the output */
static void generate_switch(struct bb_state *state, struct instruction *insn)
{
struct hardreg *reg = hardregs + SWITCH_REG;
generate_output_storage(state);
output_insn(state, "switch on %s", reg->name);
output_insn(state, "unimplemented: %s", show_instruction(insn));
}
static void generate_ret(struct bb_state *state, struct instruction *ret)
{
if (ret->src && ret->src != VOID) {
struct hardreg *wants = hardregs+0;
struct hardreg *reg = getreg(state, ret->src, NULL);
if (reg != wants)
output_insn(state, "movl %s,%s", reg->name, wants->name);
}
output_insn(state, "ret");
}
/*
* Fake "call" linearization just as a taster..
*/
static void generate_call(struct bb_state *state, struct instruction *insn)
{
int offset = 0;
pseudo_t arg;
FOR_EACH_PTR(insn->arguments, arg) {
output_insn(state, "pushl %s", generic(state, arg));
offset += 4;
} END_FOR_EACH_PTR(arg);
flush_reg(state, hardregs+0);
flush_reg(state, hardregs+1);
flush_reg(state, hardregs+2);
output_insn(state, "call %s", show_pseudo(insn->func));
if (offset)
output_insn(state, "addl $%d,%%esp", offset);
if (insn->target && insn->target != VOID)
add_pseudo_reg(state, insn->target, hardregs+0);
}
static void generate_select(struct bb_state *state, struct instruction *insn)
{
const char *cond;
struct hardreg *src1, *src2, *dst;
src1 = getreg(state, insn->src2, NULL);
dst = copy_reg(state, src1, insn->target);
add_pseudo_reg(state, insn->target, dst);
src2 = getreg(state, insn->src3, insn->target);
if (state->cc_target == insn->src1) {
cond = conditional[state->cc_opcode];
} else {
struct hardreg *reg = getreg(state, insn->src1, NULL);
output_insn(state, "testl %s,%s", reg->name, reg->name);
cond = "ne";
}
output_insn(state, "sel%s %s,%s", cond, src2->name, dst->name);
}
struct asm_arg {
const struct ident *name;
const char *value;
pseudo_t pseudo;
struct hardreg *reg;
};
static void replace_asm_arg(char **dst_p, struct asm_arg *arg)
{
char *dst = *dst_p;
int len = strlen(arg->value);
memcpy(dst, arg->value, len);
*dst_p = dst + len;
}
static void replace_asm_percent(const char **src_p, char **dst_p, struct asm_arg *args, int nr)
{
const char *src = *src_p;
char c;
int index;
c = *src++;
switch (c) {
case '0' ... '9':
index = c - '0';
if (index < nr)
replace_asm_arg(dst_p, args+index);
break;
}
*src_p = src;
return;
}
static void replace_asm_named(const char **src_p, char **dst_p, struct asm_arg *args, int nr)
{
const char *src = *src_p;
const char *end = src;
for(;;) {
char c = *end++;
if (!c)
return;
if (c == ']') {
int i;
*src_p = end;
for (i = 0; i < nr; i++) {
const struct ident *ident = args[i].name;
int len;
if (!ident)
continue;
len = ident->len;
if (memcmp(src, ident->name, len))
continue;
replace_asm_arg(dst_p, args+i);
return;
}
}
}
}
static const char *replace_asm_args(const char *str, struct asm_arg *args, int nr)
{
static char buffer[1000];
char *p = buffer;
for (;;) {
char c = *str;
*p = c;
if (!c)
return buffer;
str++;
switch (c) {
case '%':
if (*str == '%') {
str++;
p++;
continue;
}
replace_asm_percent(&str, &p, args, nr);
continue;
case '[':
replace_asm_named(&str, &p, args, nr);
continue;
default:
break;
}
p++;
}
}
#define MAX_ASM_ARG (50)
static struct asm_arg asm_arguments[MAX_ASM_ARG];
static struct asm_arg *generate_asm_inputs(struct bb_state *state, struct asm_constraint_list *list, struct asm_arg *arg)
{
struct asm_constraint *entry;
FOR_EACH_PTR(list, entry) {
const char *constraint = entry->constraint;
pseudo_t pseudo = entry->pseudo;
struct hardreg *reg, *orig;
const char *string;
int index;
string = "undef";
switch (*constraint) {
case 'r':
string = getreg(state, pseudo, NULL)->name;
break;
case '0' ... '9':
index = *constraint - '0';
reg = asm_arguments[index].reg;
orig = find_in_reg(state, pseudo);
if (orig)
move_reg(state, orig, reg);
else
fill_reg(state, reg, pseudo);
string = reg->name;
break;
default:
string = generic(state, pseudo);
break;
}
output_insn(state, "# asm input \"%s\": %s : %s", constraint, show_pseudo(pseudo), string);
arg->name = entry->ident;
arg->value = string;
arg->pseudo = NULL;
arg->reg = NULL;
arg++;
} END_FOR_EACH_PTR(entry);
return arg;
}
static struct asm_arg *generate_asm_outputs(struct bb_state *state, struct asm_constraint_list *list, struct asm_arg *arg)
{
struct asm_constraint *entry;
FOR_EACH_PTR(list, entry) {
const char *constraint = entry->constraint;
pseudo_t pseudo = entry->pseudo;
struct hardreg *reg;
const char *string;
while (*constraint == '=' || *constraint == '+')
constraint++;
string = "undef";
switch (*constraint) {
case 'r':
default:
reg = target_reg(state, pseudo, NULL);
arg->pseudo = pseudo;
arg->reg = reg;
string = reg->name;
break;
}
output_insn(state, "# asm output \"%s\": %s : %s", constraint, show_pseudo(pseudo), string);
arg->name = entry->ident;
arg->value = string;
arg++;
} END_FOR_EACH_PTR(entry);
return arg;
}
static void generate_asm(struct bb_state *state, struct instruction *insn)
{
const char *str = insn->string;
if (insn->asm_rules->outputs || insn->asm_rules->inputs) {
struct asm_arg *arg;
arg = generate_asm_outputs(state, insn->asm_rules->outputs, asm_arguments);
arg = generate_asm_inputs(state, insn->asm_rules->inputs, arg);
str = replace_asm_args(str, asm_arguments, arg - asm_arguments);
}
output_insn(state, "%s", str);
}
static void generate_compare(struct bb_state *state, struct instruction *insn)
{
struct hardreg *src;
const char *src2;
int opcode;
flush_cc_cache(state);
opcode = insn->opcode;
/*
* We should try to switch these around if necessary,
* and update the opcode to match..
*/
src = getreg(state, insn->src1, insn->target);
src2 = generic(state, insn->src2);
output_insn(state, "cmp.%d %s,%s", insn->size, src2, src->name);
add_cc_cache(state, opcode, insn->target);
}
static void generate_one_insn(struct instruction *insn, struct bb_state *state)
{
if (verbose)
output_comment(state, "%s", show_instruction(insn));
switch (insn->opcode) {
case OP_ENTRY: {
struct symbol *sym = insn->bb->ep->name;
const char *name = show_ident(sym->ident);
if (sym->ctype.modifiers & MOD_STATIC)
printf("\n\n%s:\n", name);
else
printf("\n\n.globl %s\n%s:\n", name, name);
break;
}
/*
* OP_LABEL & OP_SETVAL likewise doesn't actually generate any
* code. On use, the "def" of the pseudo will be
* looked up.
*/
case OP_LABEL:
case OP_SETVAL:
break;
case OP_STORE:
generate_store(insn, state);
break;
case OP_LOAD:
generate_load(insn, state);
break;
case OP_DEATHNOTE:
mark_pseudo_dead(state, insn->target);
return;
case OP_COPY:
generate_copy(state, insn);
break;
case OP_ADD: case OP_MUL:
case OP_AND: case OP_OR: case OP_XOR:
generate_commutative_binop(state, insn);
break;
case OP_SUB: case OP_DIVU: case OP_DIVS:
case OP_MODU: case OP_MODS:
case OP_SHL: case OP_LSR: case OP_ASR:
generate_binop(state, insn);
break;
case OP_BINCMP ... OP_BINCMP_END:
generate_compare(state, insn);
break;
case OP_SEXT: case OP_ZEXT:
case OP_TRUNC:
case OP_PTRCAST:
case OP_UTPTR:
case OP_PTRTU:
case OP_FCVTU: case OP_FCVTS:
case OP_UCVTF: case OP_SCVTF:
case OP_FCVTF:
generate_cast(state, insn);
break;
case OP_SEL:
generate_select(state, insn);
break;
case OP_BR:
case OP_CBR:
generate_branch(state, insn);
break;
case OP_SWITCH:
generate_switch(state, insn);
break;
case OP_CALL:
generate_call(state, insn);
break;
case OP_RET:
generate_ret(state, insn);
break;
case OP_ASM:
generate_asm(state, insn);
break;
case OP_PHI:
case OP_PHISOURCE:
default:
output_insn(state, "unimplemented: %s", show_instruction(insn));
break;
}
kill_dead_pseudos(state);
}
#define VERY_BUSY 1000
#define REG_FIXED 2000
static void write_reg_to_storage(struct bb_state *state, struct hardreg *reg, pseudo_t pseudo, struct storage *storage)
{
int i;
struct hardreg *out;
switch (storage->type) {
case REG_REG:
out = hardregs + storage->regno;
if (reg == out)
return;
output_insn(state, "movl %s,%s", reg->name, out->name);
return;
case REG_UDEF:
if (reg->busy < VERY_BUSY) {
storage->type = REG_REG;
storage->regno = reg - hardregs;
reg->busy = REG_FIXED;
return;
}
/* Try to find a non-busy register.. */
for (i = 0; i < REGNO; i++) {
out = hardregs + i;
if (out->contains)
continue;
output_insn(state, "movl %s,%s", reg->name, out->name);
storage->type = REG_REG;
storage->regno = i;
out->busy = REG_FIXED;
return;
}
/* Fall back on stack allocation ... */
alloc_stack(state, storage);
/* Fall through */
default:
output_insn(state, "movl %s,%s", reg->name, show_memop(storage));
return;
}
}
static void write_val_to_storage(struct bb_state *state, pseudo_t src, struct storage *storage)
{
struct hardreg *out;
switch (storage->type) {
case REG_UDEF:
alloc_stack(state, storage);
default:
output_insn(state, "movl %s,%s", show_pseudo(src), show_memop(storage));
break;
case REG_REG:
out = hardregs + storage->regno;
output_insn(state, "movl %s,%s", show_pseudo(src), out->name);
}
}
static void fill_output(struct bb_state *state, pseudo_t pseudo, struct storage *out)
{
int i;
struct storage_hash *in;
struct instruction *def;
/* Is that pseudo a constant value? */
switch (pseudo->type) {
case PSEUDO_VAL:
write_val_to_storage(state, pseudo, out);
return;
case PSEUDO_REG:
def = pseudo->def;
if (def && (def->opcode == OP_SETVAL || def->opcode == OP_LABEL)) {
write_val_to_storage(state, pseudo, out);
return;
}
default:
break;
}
/* See if we have that pseudo in a register.. */
for (i = 0; i < REGNO; i++) {
struct hardreg *reg = hardregs + i;
pseudo_t p;
FOR_EACH_PTR_TAG(reg->contains, p) {
if (p == pseudo) {
write_reg_to_storage(state, reg, pseudo, out);
return;
}
} END_FOR_EACH_PTR(p);
}
/* Do we have it in another storage? */
in = find_storage_hash(pseudo, state->internal);
if (!in) {
in = find_storage_hash(pseudo, state->inputs);
/* Undefined? */
if (!in)
return;
}
switch (out->type) {
case REG_UDEF:
*out = *in->storage;
break;
case REG_REG:
output_insn(state, "movl %s,%s", show_memop(in->storage), hardregs[out->regno].name);
break;
default:
if (out == in->storage)
break;
if ((out->type == in->storage->type) && (out->regno == in->storage->regno))
break;
output_insn(state, "movl %s,%s", show_memop(in->storage), show_memop(out));
break;
}
return;
}
static int final_pseudo_flush(struct bb_state *state, pseudo_t pseudo, struct hardreg *reg)
{
struct storage_hash *hash;
struct storage *out;
struct hardreg *dst;
/*
* Since this pseudo is live at exit, we'd better have output
* storage for it..
*/
hash = find_storage_hash(pseudo, state->outputs);
if (!hash)
return 1;
out = hash->storage;
/* If the output is in a register, try to get it there.. */
if (out->type == REG_REG) {
dst = hardregs + out->regno;
/*
* Two good cases: nobody is using the right register,
* or we've already set it aside for output..
*/
if (!dst->contains || dst->busy > VERY_BUSY)
goto copy_to_dst;
/* Aiee. Try to keep it in a register.. */
dst = empty_reg(state);
if (dst)
goto copy_to_dst;
return 0;
}
/* If the output is undefined, let's see if we can put it in a register.. */
if (out->type == REG_UDEF) {
dst = empty_reg(state);
if (dst) {
out->type = REG_REG;
out->regno = dst - hardregs;
goto copy_to_dst;
}
/* Uhhuh. Not so good. No empty registers right now */
return 0;
}
/* If we know we need to flush it, just do so already .. */
output_insn(state, "movl %s,%s", reg->name, show_memop(out));
return 1;
copy_to_dst:
if (reg == dst)
return 1;
output_insn(state, "movl %s,%s", reg->name, dst->name);
add_pseudo_reg(state, pseudo, dst);
return 1;
}
/*
* This tries to make sure that we put all the pseudos that are
* live on exit into the proper storage
*/
static void generate_output_storage(struct bb_state *state)
{
struct storage_hash *entry;
/* Go through the fixed outputs, making sure we have those regs free */
FOR_EACH_PTR(state->outputs, entry) {
struct storage *out = entry->storage;
if (out->type == REG_REG) {
struct hardreg *reg = hardregs + out->regno;
pseudo_t p;
int flushme = 0;
reg->busy = REG_FIXED;
FOR_EACH_PTR_TAG(reg->contains, p) {
if (p == entry->pseudo) {
flushme = -100;
continue;
}
if (CURRENT_TAG(p) & TAG_DEAD)
continue;
/* Try to write back the pseudo to where it should go ... */
if (final_pseudo_flush(state, p, reg)) {
DELETE_CURRENT_PTR(p);
continue;
}
flushme++;
} END_FOR_EACH_PTR(p);
PACK_PTR_LIST(®->contains);
if (flushme > 0)
flush_reg(state, reg);
}
} END_FOR_EACH_PTR(entry);
FOR_EACH_PTR(state->outputs, entry) {
fill_output(state, entry->pseudo, entry->storage);
} END_FOR_EACH_PTR(entry);
}
static void generate(struct basic_block *bb, struct bb_state *state)
{
int i;
struct storage_hash *entry;
struct instruction *insn;
for (i = 0; i < REGNO; i++) {
free_ptr_list(&hardregs[i].contains);
hardregs[i].busy = 0;
hardregs[i].dead = 0;
hardregs[i].used = 0;
}
FOR_EACH_PTR(state->inputs, entry) {
struct storage *storage = entry->storage;
const char *name = show_storage(storage);
output_comment(state, "incoming %s in %s", show_pseudo(entry->pseudo), name);
if (storage->type == REG_REG) {
int regno = storage->regno;
add_pseudo_reg(state, entry->pseudo, hardregs + regno);
name = hardregs[regno].name;
}
} END_FOR_EACH_PTR(entry);
output_label(state, ".L%p", bb);
FOR_EACH_PTR(bb->insns, insn) {
if (!insn->bb)
continue;
generate_one_insn(insn, state);
} END_FOR_EACH_PTR(insn);
if (verbose) {
output_comment(state, "--- in ---");
FOR_EACH_PTR(state->inputs, entry) {
output_comment(state, "%s <- %s", show_pseudo(entry->pseudo), show_storage(entry->storage));
} END_FOR_EACH_PTR(entry);
output_comment(state, "--- spill ---");
FOR_EACH_PTR(state->internal, entry) {
output_comment(state, "%s <-> %s", show_pseudo(entry->pseudo), show_storage(entry->storage));
} END_FOR_EACH_PTR(entry);
output_comment(state, "--- out ---");
FOR_EACH_PTR(state->outputs, entry) {
output_comment(state, "%s -> %s", show_pseudo(entry->pseudo), show_storage(entry->storage));
} END_FOR_EACH_PTR(entry);
}
printf("\n");
}
static void generate_list(struct basic_block_list *list, unsigned long generation)
{
struct basic_block *bb;
FOR_EACH_PTR(list, bb) {
if (bb->generation == generation)
continue;
output_bb(bb, generation);
} END_FOR_EACH_PTR(bb);
}
/*
* Mark all the output registers of all the parents
* as being "used" - this does not mean that we cannot
* re-use them, but it means that we cannot ask the
* parents to pass in another pseudo in one of those
* registers that it already uses for another child.
*/
static void mark_used_registers(struct basic_block *bb, struct bb_state *state)
{
struct basic_block *parent;
FOR_EACH_PTR(bb->parents, parent) {
struct storage_hash_list *outputs = gather_storage(parent, STOR_OUT);
struct storage_hash *entry;
FOR_EACH_PTR(outputs, entry) {
struct storage *s = entry->storage;
if (s->type == REG_REG) {
struct hardreg *reg = hardregs + s->regno;
reg->used = 1;
}
} END_FOR_EACH_PTR(entry);
} END_FOR_EACH_PTR(parent);
}
static void output_bb(struct basic_block *bb, unsigned long generation)
{
struct bb_state state;
bb->generation = generation;
/* Make sure all parents have been generated first */
generate_list(bb->parents, generation);
state.pos = bb->pos;
state.inputs = gather_storage(bb, STOR_IN);
state.outputs = gather_storage(bb, STOR_OUT);
state.internal = NULL;
state.cc_opcode = 0;
state.cc_target = NULL;
/* Mark incoming registers used */
mark_used_registers(bb, &state);
generate(bb, &state);
free_ptr_list(&state.inputs);
free_ptr_list(&state.outputs);
/* Generate all children... */
generate_list(bb->children, generation);
}
/*
* We should set up argument sources here..
*
* Things like "first three arguments in registers" etc
* are all for this place.
*
* On x86, we default to stack, unless it's a static
* function that doesn't have its address taken.
*
* I should implement the -mregparm=X cmd line option.
*/
static void set_up_arch_entry(struct entrypoint *ep, struct instruction *entry)
{
pseudo_t arg;
struct symbol *sym, *argtype;
int i, offset, regparm;
sym = ep->name;
regparm = 0;
if (!(sym->ctype.modifiers & MOD_ADDRESSABLE))
regparm = 3;
sym = sym->ctype.base_type;
i = 0;
offset = 0;
PREPARE_PTR_LIST(sym->arguments, argtype);
FOR_EACH_PTR(entry->arg_list, arg) {
struct storage *in = lookup_storage(entry->bb, arg, STOR_IN);
if (!in) {
in = alloc_storage();
add_storage(in, entry->bb, arg, STOR_IN);
}
if (i < regparm) {
in->type = REG_REG;
in->regno = i;
} else {
int bits = argtype ? argtype->bit_size : 0;
if (bits < bits_in_int)
bits = bits_in_int;
in->type = REG_FRAME;
in->offset = offset;
offset += bits_to_bytes(bits);
}
i++;
NEXT_PTR_LIST(argtype);
} END_FOR_EACH_PTR(arg);
FINISH_PTR_LIST(argtype);
}
/*
* Set up storage information for "return"
*
* Not strictly necessary, since the code generator will
* certainly move the return value to the right register,
* but it can help register allocation if the allocator
* sees that the target register is going to return in %eax.
*/
static void set_up_arch_exit(struct basic_block *bb, struct instruction *ret)
{
pseudo_t pseudo = ret->src;
if (pseudo && pseudo != VOID) {
struct storage *out = lookup_storage(bb, pseudo, STOR_OUT);
if (!out) {
out = alloc_storage();
add_storage(out, bb, pseudo, STOR_OUT);
}
out->type = REG_REG;
out->regno = 0;
}
}
/*
* Set up dummy/silly output storage information for a switch
* instruction. We need to make sure that a register is available
* when we generate code for switch, so force that by creating
* a dummy output rule.
*/
static void set_up_arch_switch(struct basic_block *bb, struct instruction *insn)
{
pseudo_t pseudo = insn->cond;
struct storage *out = lookup_storage(bb, pseudo, STOR_OUT);
if (!out) {
out = alloc_storage();
add_storage(out, bb, pseudo, STOR_OUT);
}
out->type = REG_REG;
out->regno = SWITCH_REG;
}
static void arch_set_up_storage(struct entrypoint *ep)
{
struct basic_block *bb;
/* Argument storage etc.. */
set_up_arch_entry(ep, ep->entry);
FOR_EACH_PTR(ep->bbs, bb) {
struct instruction *insn = last_instruction(bb->insns);
if (!insn)
continue;
switch (insn->opcode) {
case OP_RET:
set_up_arch_exit(bb, insn);
break;
case OP_SWITCH:
set_up_arch_switch(bb, insn);
break;
default:
/* nothing */;
}
} END_FOR_EACH_PTR(bb);
}
static void output(struct entrypoint *ep)
{
unsigned long generation = ++bb_generation;
last_reg = -1;
stack_offset = 0;
/* Get rid of SSA form (phinodes etc) */
unssa(ep);
/* Set up initial inter-bb storage links */
set_up_storage(ep);
/* Architecture-specific storage rules.. */
arch_set_up_storage(ep);
/* Show the results ... */
output_bb(ep->entry->bb, generation);
/* Clear the storage hashes for the next function.. */
free_storage();
}
static int compile(struct symbol_list *list)
{
struct symbol *sym;
FOR_EACH_PTR(list, sym) {
struct entrypoint *ep;
expand_symbol(sym);
ep = linearize_symbol(sym);
if (ep)
output(ep);
} END_FOR_EACH_PTR(sym);
return 0;
}
int main(int argc, char **argv)
{
struct string_list *filelist = NULL;
char *file;
compile(sparse_initialize(argc, argv, &filelist));
dbg_dead = 1;
FOR_EACH_PTR(filelist, file) {
compile(sparse(file));
} END_FOR_EACH_PTR(file);
return 0;
}
sparse-0.6.4/expand.c 0000664 0000000 0000000 00000101146 14115310122 0014432 0 ustar 00root root 0000000 0000000 /*
* sparse/expand.c
*
* Copyright (C) 2003 Transmeta Corp.
* 2003-2004 Linus Torvalds
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* expand constant expressions.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "lib.h"
#include "allocate.h"
#include "parse.h"
#include "token.h"
#include "symbol.h"
#include "target.h"
#include "expression.h"
#include "evaluate.h"
#include "expand.h"
static int expand_expression(struct expression *);
static int expand_statement(struct statement *);
// If set, don't issue a warning on divide-by-0, invalid shift, ...
// and don't mark the expression as erroneous but leave it as-is.
// This allows testing some characteristics of the expression
// without creating any side-effects (e.g.: is_zero_constant()).
static int conservative;
static int expand_symbol_expression(struct expression *expr)
{
struct symbol *sym = expr->symbol;
if (sym == &zero_int) {
if (Wundef)
warning(expr->pos, "undefined preprocessor identifier '%s'", show_ident(expr->symbol_name));
expr->type = EXPR_VALUE;
expr->value = 0;
expr->taint = 0;
return 0;
}
// expand compound literals (C99 & C11 6.5.2.5)
// FIXME: is this the correct way to identify them?
// All compound literals are anonymous but is
// the reverse true?
if (sym->initializer && !expr->symbol_name)
return expand_expression(sym->initializer);
/* The cost of a symbol expression is lower for on-stack symbols */
return (sym->ctype.modifiers & (MOD_STATIC | MOD_EXTERN)) ? 2 : 1;
}
static long long get_longlong(struct expression *expr)
{
int no_expand = expr->ctype->ctype.modifiers & MOD_UNSIGNED;
long long mask = 1ULL << (expr->ctype->bit_size - 1);
long long value = expr->value;
long long ormask, andmask;
if (!(value & mask))
no_expand = 1;
andmask = mask | (mask-1);
ormask = ~andmask;
if (no_expand)
ormask = 0;
return (value & andmask) | ormask;
}
void cast_value(struct expression *expr, struct symbol *newtype,
struct expression *old, struct symbol *oldtype)
{
int old_size = oldtype->bit_size;
int new_size = newtype->bit_size;
long long value, mask, signmask;
long long oldmask, oldsignmask, dropped;
if (is_float_type(newtype) || is_float_type(oldtype))
goto Float;
// For pointers and integers, we can just move the value around
expr->type = EXPR_VALUE;
expr->taint = old->taint;
if (old_size == new_size) {
expr->value = old->value;
return;
}
// expand it to the full "long long" value
value = get_longlong(old);
Int:
// _Bool requires a zero test rather than truncation.
if (is_bool_type(newtype)) {
expr->value = !!value;
if (!conservative && value != 0 && value != 1)
warning(old->pos, "odd constant _Bool cast (%llx becomes 1)", value);
return;
}
// Truncate it to the new size
signmask = 1ULL << (new_size-1);
mask = signmask | (signmask-1);
expr->value = value & mask;
// Stop here unless checking for truncation
if (!Wcast_truncate || conservative)
return;
// Check if we dropped any bits..
oldsignmask = 1ULL << (old_size-1);
oldmask = oldsignmask | (oldsignmask-1);
dropped = oldmask & ~mask;
// OK if the bits were (and still are) purely sign bits
if (value & dropped) {
if (!(value & oldsignmask) || !(value & signmask) || (value & dropped) != dropped)
warning(old->pos, "cast truncates bits from constant value (%llx becomes %llx)",
value & oldmask,
value & mask);
}
return;
Float:
if (!is_float_type(newtype)) {
value = (long long)old->fvalue;
expr->type = EXPR_VALUE;
expr->taint = 0;
goto Int;
}
if (!is_float_type(oldtype))
expr->fvalue = (long double)get_longlong(old);
else
expr->fvalue = old->fvalue;
if (newtype->rank <= 0) {
if (newtype->rank == 0)
expr->fvalue = (double)expr->fvalue;
else
expr->fvalue = (float)expr->fvalue;
}
expr->type = EXPR_FVALUE;
}
/* Return true if constant shift size is valid */
static bool check_shift_count(struct expression *expr, struct expression *right)
{
struct symbol *ctype = expr->ctype;
long long count = get_longlong(right);
if (count >= 0 && count < ctype->bit_size)
return true;
return false;
}
/*
* CAREFUL! We need to get the size and sign of the
* result right!
*/
#define CONVERT(op,s) (((op)<<1)+(s))
#define SIGNED(op) CONVERT(op, 1)
#define UNSIGNED(op) CONVERT(op, 0)
static int simplify_int_binop(struct expression *expr, struct symbol *ctype)
{
struct expression *left = expr->left, *right = expr->right;
unsigned long long v, l, r, mask;
signed long long sl, sr;
int is_signed;
if (right->type != EXPR_VALUE)
return 0;
r = right->value;
if (expr->op == SPECIAL_LEFTSHIFT || expr->op == SPECIAL_RIGHTSHIFT) {
if (!check_shift_count(expr, right))
return 0;
}
if (left->type != EXPR_VALUE)
return 0;
l = left->value; r = right->value;
is_signed = !(ctype->ctype.modifiers & MOD_UNSIGNED);
mask = 1ULL << (ctype->bit_size-1);
sl = l; sr = r;
if (is_signed && (sl & mask))
sl |= ~(mask-1);
if (is_signed && (sr & mask))
sr |= ~(mask-1);
switch (CONVERT(expr->op,is_signed)) {
case SIGNED('+'):
case UNSIGNED('+'):
v = l + r;
break;
case SIGNED('-'):
case UNSIGNED('-'):
v = l - r;
break;
case SIGNED('&'):
case UNSIGNED('&'):
v = l & r;
break;
case SIGNED('|'):
case UNSIGNED('|'):
v = l | r;
break;
case SIGNED('^'):
case UNSIGNED('^'):
v = l ^ r;
break;
case SIGNED('*'):
v = sl * sr;
break;
case UNSIGNED('*'):
v = l * r;
break;
case SIGNED('/'):
if (!r)
goto Div;
if (l == mask && sr == -1)
goto Overflow;
v = sl / sr;
break;
case UNSIGNED('/'):
if (!r) goto Div;
v = l / r;
break;
case SIGNED('%'):
if (!r)
goto Div;
if (l == mask && sr == -1)
goto Overflow;
v = sl % sr;
break;
case UNSIGNED('%'):
if (!r) goto Div;
v = l % r;
break;
case SIGNED(SPECIAL_LEFTSHIFT):
case UNSIGNED(SPECIAL_LEFTSHIFT):
v = l << r;
break;
case SIGNED(SPECIAL_RIGHTSHIFT):
v = sl >> r;
break;
case UNSIGNED(SPECIAL_RIGHTSHIFT):
v = l >> r;
break;
default:
return 0;
}
mask = mask | (mask-1);
expr->value = v & mask;
expr->type = EXPR_VALUE;
expr->taint = left->taint | right->taint;
return 1;
Div:
if (!conservative)
warning(expr->pos, "division by zero");
return 0;
Overflow:
if (!conservative)
warning(expr->pos, "constant integer operation overflow");
return 0;
}
static int simplify_cmp_binop(struct expression *expr, struct symbol *ctype)
{
struct expression *left = expr->left, *right = expr->right;
unsigned long long l, r, mask;
signed long long sl, sr;
if (left->type != EXPR_VALUE || right->type != EXPR_VALUE)
return 0;
l = left->value; r = right->value;
mask = 1ULL << (ctype->bit_size-1);
sl = l; sr = r;
if (sl & mask)
sl |= ~(mask-1);
if (sr & mask)
sr |= ~(mask-1);
switch (expr->op) {
case '<': expr->value = sl < sr; break;
case '>': expr->value = sl > sr; break;
case SPECIAL_LTE: expr->value = sl <= sr; break;
case SPECIAL_GTE: expr->value = sl >= sr; break;
case SPECIAL_EQUAL: expr->value = l == r; break;
case SPECIAL_NOTEQUAL: expr->value = l != r; break;
case SPECIAL_UNSIGNED_LT:expr->value = l < r; break;
case SPECIAL_UNSIGNED_GT:expr->value = l > r; break;
case SPECIAL_UNSIGNED_LTE:expr->value = l <= r; break;
case SPECIAL_UNSIGNED_GTE:expr->value = l >= r; break;
}
expr->type = EXPR_VALUE;
expr->taint = left->taint | right->taint;
return 1;
}
static int simplify_float_binop(struct expression *expr)
{
struct expression *left = expr->left, *right = expr->right;
int rank = expr->ctype->rank;
long double l, r, res;
if (left->type != EXPR_FVALUE || right->type != EXPR_FVALUE)
return 0;
l = left->fvalue;
r = right->fvalue;
if (rank > 0) {
switch (expr->op) {
case '+': res = l + r; break;
case '-': res = l - r; break;
case '*': res = l * r; break;
case '/': if (!r) goto Div;
res = l / r; break;
default: return 0;
}
} else if (rank == 0) {
switch (expr->op) {
case '+': res = (double) l + (double) r; break;
case '-': res = (double) l - (double) r; break;
case '*': res = (double) l * (double) r; break;
case '/': if (!r) goto Div;
res = (double) l / (double) r; break;
default: return 0;
}
} else {
switch (expr->op) {
case '+': res = (float)l + (float)r; break;
case '-': res = (float)l - (float)r; break;
case '*': res = (float)l * (float)r; break;
case '/': if (!r) goto Div;
res = (float)l / (float)r; break;
default: return 0;
}
}
expr->type = EXPR_FVALUE;
expr->fvalue = res;
return 1;
Div:
if (!conservative)
warning(expr->pos, "division by zero");
return 0;
}
static int simplify_float_cmp(struct expression *expr, struct symbol *ctype)
{
struct expression *left = expr->left, *right = expr->right;
long double l, r;
if (left->type != EXPR_FVALUE || right->type != EXPR_FVALUE)
return 0;
l = left->fvalue;
r = right->fvalue;
switch (expr->op) {
case '<': expr->value = l < r; break;
case '>': expr->value = l > r; break;
case SPECIAL_LTE: expr->value = l <= r; break;
case SPECIAL_GTE: expr->value = l >= r; break;
case SPECIAL_EQUAL: expr->value = l == r; break;
case SPECIAL_NOTEQUAL: expr->value = l != r; break;
}
expr->type = EXPR_VALUE;
expr->taint = 0;
return 1;
}
static int expand_binop(struct expression *expr)
{
int cost;
cost = expand_expression(expr->left);
cost += expand_expression(expr->right);
if (simplify_int_binop(expr, expr->ctype))
return 0;
if (simplify_float_binop(expr))
return 0;
return cost + 1;
}
static int expand_logical(struct expression *expr)
{
struct expression *left = expr->left;
struct expression *right;
int cost, rcost;
/* Do immediate short-circuiting ... */
cost = expand_expression(left);
if (left->type == EXPR_VALUE) {
if (expr->op == SPECIAL_LOGICAL_AND) {
if (!left->value) {
expr->type = EXPR_VALUE;
expr->value = 0;
expr->taint = left->taint;
return 0;
}
} else {
if (left->value) {
expr->type = EXPR_VALUE;
expr->value = 1;
expr->taint = left->taint;
return 0;
}
}
}
right = expr->right;
rcost = expand_expression(right);
if (left->type == EXPR_VALUE && right->type == EXPR_VALUE) {
/*
* We know the left value doesn't matter, since
* otherwise we would have short-circuited it..
*/
expr->type = EXPR_VALUE;
expr->value = right->value != 0;
expr->taint = left->taint | right->taint;
return 0;
}
/*
* If the right side is safe and cheaper than a branch,
* just avoid the branch and turn it into a regular binop
* style SAFELOGICAL.
*/
if (rcost < BRANCH_COST) {
expr->type = EXPR_BINOP;
rcost -= BRANCH_COST - 1;
}
return cost + BRANCH_COST + rcost;
}
static int expand_comma(struct expression *expr)
{
int cost;
cost = expand_expression(expr->left);
cost += expand_expression(expr->right);
if (expr->left->type == EXPR_VALUE || expr->left->type == EXPR_FVALUE) {
unsigned flags = expr->flags;
unsigned taint;
taint = expr->left->type == EXPR_VALUE ? expr->left->taint : 0;
*expr = *expr->right;
expr->flags = flags;
if (expr->type == EXPR_VALUE)
expr->taint |= Taint_comma | taint;
}
return cost;
}
#define MOD_IGN (MOD_QUALIFIER)
static int compare_types(int op, struct symbol *left, struct symbol *right)
{
struct ctype c1 = {.base_type = left};
struct ctype c2 = {.base_type = right};
switch (op) {
case SPECIAL_EQUAL:
return !type_difference(&c1, &c2, MOD_IGN, MOD_IGN);
case SPECIAL_NOTEQUAL:
return type_difference(&c1, &c2, MOD_IGN, MOD_IGN) != NULL;
case '<':
return left->bit_size < right->bit_size;
case '>':
return left->bit_size > right->bit_size;
case SPECIAL_LTE:
return left->bit_size <= right->bit_size;
case SPECIAL_GTE:
return left->bit_size >= right->bit_size;
}
return 0;
}
static int expand_compare(struct expression *expr)
{
struct expression *left = expr->left, *right = expr->right;
int cost;
cost = expand_expression(left);
cost += expand_expression(right);
if (left && right) {
/* Type comparison? */
if (left->type == EXPR_TYPE && right->type == EXPR_TYPE) {
int op = expr->op;
expr->type = EXPR_VALUE;
expr->value = compare_types(op, left->symbol, right->symbol);
expr->taint = 0;
return 0;
}
if (simplify_cmp_binop(expr, left->ctype))
return 0;
if (simplify_float_cmp(expr, left->ctype))
return 0;
}
return cost + 1;
}
static int expand_conditional(struct expression *expr)
{
struct expression *cond = expr->conditional;
struct expression *valt = expr->cond_true;
struct expression *valf = expr->cond_false;
int cost, cond_cost;
cond_cost = expand_expression(cond);
if (cond->type == EXPR_VALUE) {
unsigned flags = expr->flags;
if (!cond->value)
valt = valf;
if (!valt)
valt = cond;
cost = expand_expression(valt);
*expr = *valt;
expr->flags = flags;
if (expr->type == EXPR_VALUE)
expr->taint |= cond->taint;
return cost;
}
cost = expand_expression(valt);
cost += expand_expression(valf);
if (cost < SELECT_COST) {
expr->type = EXPR_SELECT;
cost -= BRANCH_COST - 1;
}
return cost + cond_cost + BRANCH_COST;
}
static void check_assignment(struct expression *expr)
{
struct expression *right;
switch (expr->op) {
case SPECIAL_SHL_ASSIGN:
case SPECIAL_SHR_ASSIGN:
right = expr->right;
if (right->type != EXPR_VALUE)
break;
check_shift_count(expr, right);
break;
}
return;
}
static int expand_assignment(struct expression *expr)
{
expand_expression(expr->left);
expand_expression(expr->right);
if (!conservative)
check_assignment(expr);
return SIDE_EFFECTS;
}
static int expand_addressof(struct expression *expr)
{
return expand_expression(expr->unop);
}
///
// lookup the type of a struct's memeber at the requested offset
static struct symbol *find_member(struct symbol *sym, int offset)
{
struct ptr_list *head, *list;
head = (struct ptr_list *) sym->symbol_list;
list = head;
if (!head)
return NULL;
do {
int nr = list->nr;
int i;
for (i = 0; i < nr; i++) {
struct symbol *ent = (struct symbol *) list->list[i];
int curr = ent->offset;
if (curr == offset)
return ent;
if (curr > offset)
return NULL;
}
} while ((list = list->next) != head);
return NULL;
}
///
// lookup a suitable default initializer value at the requested offset
static struct expression *default_initializer(struct symbol *sym, int offset)
{
static struct expression value;
struct symbol *type;
redo:
switch (sym->type) {
case SYM_NODE:
sym = sym->ctype.base_type;
goto redo;
case SYM_STRUCT:
type = find_member(sym, offset);
if (!type)
return NULL;
break;
case SYM_ARRAY:
type = sym->ctype.base_type;
break;
default:
return NULL;
}
if (is_integral_type(type))
value.type = EXPR_VALUE;
else if (is_float_type(type))
value.type = EXPR_FVALUE;
else
return NULL;
value.ctype = type;
return &value;
}
/*
* Look up a trustable initializer value at the requested offset.
*
* Return NULL if no such value can be found or statically trusted.
*/
static struct expression *constant_symbol_value(struct symbol *sym, int offset)
{
struct expression *value;
if (sym->ctype.modifiers & MOD_ACCESS)
return NULL;
value = sym->initializer;
if (!value)
return NULL;
if (value->type == EXPR_INITIALIZER) {
struct expression *entry;
FOR_EACH_PTR(value->expr_list, entry) {
if (entry->type != EXPR_POS) {
if (offset)
continue;
return entry;
}
if (entry->init_offset < offset)
continue;
if (entry->init_offset > offset)
break;
return entry->init_expr;
} END_FOR_EACH_PTR(entry);
value = default_initializer(sym, offset);
}
return value;
}
static int expand_dereference(struct expression *expr)
{
struct expression *unop = expr->unop;
unsigned int offset;
expand_expression(unop);
/*
* NOTE! We get a bogus warning right now for some special
* cases: apparently I've screwed up the optimization of
* a zero-offset dereference, and the ctype is wrong.
*
* Leave the warning in anyway, since this is also a good
* test for me to get the type evaluation right..
*/
if (expr->ctype->ctype.modifiers & MOD_NODEREF)
warning(unop->pos, "dereference of noderef expression");
/*
* Is it "symbol" or "symbol + offset"?
*/
offset = 0;
while (unop->type == EXPR_BINOP && unop->op == '+') {
struct expression *right = unop->right;
if (right->type != EXPR_VALUE)
break;
offset += right->value;
unop = unop->left;
}
if (unop->type == EXPR_SYMBOL) {
struct symbol *sym = unop->symbol;
struct symbol *ctype = expr->ctype;
struct expression *value = constant_symbol_value(sym, offset);
/* Const symbol with a constant initializer? */
if (value && value->ctype) {
if (ctype->bit_size != value->ctype->bit_size)
return UNSAFE;
if (value->type == EXPR_VALUE) {
if (!is_integral_type(ctype))
return UNSAFE;
if (is_bitfield_type(value->ctype))
return UNSAFE;
expr->type = EXPR_VALUE;
expr->value = value->value;
expr->taint = 0;
return 0;
} else if (value->type == EXPR_FVALUE) {
if (!is_float_type(ctype))
return UNSAFE;
expr->type = EXPR_FVALUE;
expr->fvalue = value->fvalue;
return 0;
}
}
/* Direct symbol dereference? Cheap and safe */
return (sym->ctype.modifiers & (MOD_STATIC | MOD_EXTERN)) ? 2 : 1;
}
return UNSAFE;
}
static int simplify_preop(struct expression *expr)
{
struct expression *op = expr->unop;
unsigned long long v, mask;
if (op->type != EXPR_VALUE)
return 0;
mask = 1ULL << (expr->ctype->bit_size-1);
v = op->value;
switch (expr->op) {
case '+': break;
case '-':
if (v == mask && !(expr->ctype->ctype.modifiers & MOD_UNSIGNED))
goto Overflow;
v = -v;
break;
case '!': v = !v; break;
case '~': v = ~v; break;
default: return 0;
}
mask = mask | (mask-1);
expr->value = v & mask;
expr->type = EXPR_VALUE;
expr->taint = op->taint;
return 1;
Overflow:
if (!conservative)
warning(expr->pos, "constant integer operation overflow");
return 0;
}
static int simplify_float_preop(struct expression *expr)
{
struct expression *op = expr->unop;
long double v;
if (op->type != EXPR_FVALUE)
return 0;
v = op->fvalue;
switch (expr->op) {
case '+': break;
case '-': v = -v; break;
default: return 0;
}
expr->fvalue = v;
expr->type = EXPR_FVALUE;
return 1;
}
/*
* Unary post-ops: x++ and x--
*/
static int expand_postop(struct expression *expr)
{
expand_expression(expr->unop);
return SIDE_EFFECTS;
}
static int expand_preop(struct expression *expr)
{
int cost;
switch (expr->op) {
case '*':
return expand_dereference(expr);
case '&':
return expand_addressof(expr);
case SPECIAL_INCREMENT:
case SPECIAL_DECREMENT:
/*
* From a type evaluation standpoint the preops are
* the same as the postops
*/
return expand_postop(expr);
default:
break;
}
cost = expand_expression(expr->unop);
if (simplify_preop(expr))
return 0;
if (simplify_float_preop(expr))
return 0;
return cost + 1;
}
static int expand_arguments(struct expression_list *head)
{
int cost = 0;
struct expression *expr;
FOR_EACH_PTR (head, expr) {
cost += expand_expression(expr);
} END_FOR_EACH_PTR(expr);
return cost;
}
static int expand_cast(struct expression *expr)
{
int cost;
struct expression *target = expr->cast_expression;
cost = expand_expression(target);
/* Simplify normal integer casts.. */
if (target->type == EXPR_VALUE || target->type == EXPR_FVALUE) {
cast_value(expr, expr->ctype, target, target->ctype);
return 0;
}
return cost + 1;
}
/*
* expand a call expression with a symbol. This
* should expand builtins.
*/
static int expand_symbol_call(struct expression *expr, int cost)
{
struct expression *fn = expr->fn;
struct symbol *ctype = fn->ctype;
expand_expression(fn);
if (fn->type != EXPR_PREOP)
return SIDE_EFFECTS;
if (ctype->ctype.modifiers & MOD_INLINE) {
struct symbol *def;
def = ctype->definition ? ctype->definition : ctype;
if (inline_function(expr, def)) {
struct symbol *fn = def->ctype.base_type;
struct symbol *curr = current_fn;
current_fn = def;
evaluate_statement(expr->statement);
current_fn = curr;
fn->expanding = 1;
cost = expand_expression(expr);
fn->expanding = 0;
return cost;
}
}
if (ctype->op && ctype->op->expand)
return ctype->op->expand(expr, cost);
if (ctype->ctype.modifiers & MOD_PURE)
return cost + 1;
return SIDE_EFFECTS;
}
static int expand_call(struct expression *expr)
{
int cost;
struct symbol *sym;
struct expression *fn = expr->fn;
cost = expand_arguments(expr->args);
sym = fn->ctype;
if (!sym) {
expression_error(expr, "function has no type");
return SIDE_EFFECTS;
}
if (sym->type == SYM_NODE)
return expand_symbol_call(expr, cost);
return SIDE_EFFECTS;
}
static int expand_expression_list(struct expression_list *list)
{
int cost = 0;
struct expression *expr;
FOR_EACH_PTR(list, expr) {
cost += expand_expression(expr);
} END_FOR_EACH_PTR(expr);
return cost;
}
/*
* We can simplify nested position expressions if
* this is a simple (single) positional expression.
*/
static int expand_pos_expression(struct expression *expr)
{
struct expression *nested = expr->init_expr;
unsigned long offset = expr->init_offset;
int nr = expr->init_nr;
if (nr == 1) {
switch (nested->type) {
case EXPR_POS:
offset += nested->init_offset;
*expr = *nested;
expr->init_offset = offset;
nested = expr;
break;
case EXPR_INITIALIZER: {
struct expression *reuse = nested, *entry;
*expr = *nested;
FOR_EACH_PTR(expr->expr_list, entry) {
if (entry->type == EXPR_POS) {
entry->init_offset += offset;
} else {
if (!reuse) {
/*
* This happens rarely, but it can happen
* with bitfields that are all at offset
* zero..
*/
reuse = alloc_expression(entry->pos, EXPR_POS);
}
reuse->type = EXPR_POS;
reuse->ctype = entry->ctype;
reuse->init_offset = offset;
reuse->init_nr = 1;
reuse->init_expr = entry;
REPLACE_CURRENT_PTR(entry, reuse);
reuse = NULL;
}
} END_FOR_EACH_PTR(entry);
nested = expr;
break;
}
default:
break;
}
}
return expand_expression(nested);
}
static unsigned long bit_offset(const struct expression *expr)
{
unsigned long offset = 0;
while (expr->type == EXPR_POS) {
offset += bytes_to_bits(expr->init_offset);
expr = expr->init_expr;
}
if (expr && expr->ctype)
offset += expr->ctype->bit_offset;
return offset;
}
static unsigned long bit_range(const struct expression *expr)
{
unsigned long range = 0;
unsigned long size = 0;
while (expr->type == EXPR_POS) {
unsigned long nr = expr->init_nr;
size = expr->ctype->bit_size;
range += (nr - 1) * size;
expr = expr->init_expr;
}
range += size;
return range;
}
static int compare_expressions(const void *_a, const void *_b)
{
const struct expression *a = _a;
const struct expression *b = _b;
unsigned long a_pos = bit_offset(a);
unsigned long b_pos = bit_offset(b);
return (a_pos < b_pos) ? -1 : (a_pos == b_pos) ? 0 : 1;
}
static void sort_expression_list(struct expression_list **list)
{
sort_list((struct ptr_list **)list, compare_expressions);
}
static void verify_nonoverlapping(struct expression_list **list, struct expression *expr)
{
struct expression *a = NULL;
unsigned long max = 0;
unsigned long whole = expr->ctype->bit_size;
struct expression *b;
if (!Woverride_init)
return;
FOR_EACH_PTR(*list, b) {
unsigned long off, end;
if (!b->ctype || !b->ctype->bit_size)
continue;
off = bit_offset(b);
if (a && off < max) {
warning(a->pos, "Initializer entry defined twice");
info(b->pos, " also defined here");
if (!Woverride_init_all)
return;
}
end = off + bit_range(b);
if (!a && !Woverride_init_whole_range) {
// If first entry is the whole range, do not let
// any warning about it (this allow to initialize
// an array with some default value and then override
// some specific entries).
if (off == 0 && end == whole)
continue;
}
if (end > max) {
max = end;
a = b;
}
} END_FOR_EACH_PTR(b);
}
static int expand_expression(struct expression *expr)
{
if (!expr)
return 0;
if (!expr->ctype || expr->ctype == &bad_ctype)
return UNSAFE;
switch (expr->type) {
case EXPR_VALUE:
case EXPR_FVALUE:
case EXPR_STRING:
return 0;
case EXPR_TYPE:
case EXPR_SYMBOL:
return expand_symbol_expression(expr);
case EXPR_BINOP:
return expand_binop(expr);
case EXPR_LOGICAL:
return expand_logical(expr);
case EXPR_COMMA:
return expand_comma(expr);
case EXPR_COMPARE:
return expand_compare(expr);
case EXPR_ASSIGNMENT:
return expand_assignment(expr);
case EXPR_PREOP:
return expand_preop(expr);
case EXPR_POSTOP:
return expand_postop(expr);
case EXPR_CAST:
case EXPR_FORCE_CAST:
case EXPR_IMPLIED_CAST:
return expand_cast(expr);
case EXPR_CALL:
return expand_call(expr);
case EXPR_DEREF:
warning(expr->pos, "we should not have an EXPR_DEREF left at expansion time");
return UNSAFE;
case EXPR_SELECT:
case EXPR_CONDITIONAL:
return expand_conditional(expr);
case EXPR_STATEMENT: {
struct statement *stmt = expr->statement;
int cost = expand_statement(stmt);
if (stmt->type == STMT_EXPRESSION && stmt->expression)
*expr = *stmt->expression;
return cost;
}
case EXPR_LABEL:
return 0;
case EXPR_INITIALIZER:
sort_expression_list(&expr->expr_list);
verify_nonoverlapping(&expr->expr_list, expr);
return expand_expression_list(expr->expr_list);
case EXPR_IDENTIFIER:
return UNSAFE;
case EXPR_INDEX:
return UNSAFE;
case EXPR_SLICE:
return expand_expression(expr->base) + 1;
case EXPR_POS:
return expand_pos_expression(expr);
case EXPR_GENERIC:
case EXPR_SIZEOF:
case EXPR_PTRSIZEOF:
case EXPR_ALIGNOF:
case EXPR_OFFSETOF:
expression_error(expr, "internal front-end error: sizeof in expansion?");
return UNSAFE;
}
return SIDE_EFFECTS;
}
static void expand_const_expression(struct expression *expr, const char *where)
{
if (expr) {
expand_expression(expr);
if (expr->type != EXPR_VALUE) {
expression_error(expr, "Expected constant expression in %s", where);
expr->ctype = &int_ctype;
expr->type = EXPR_VALUE;
expr->value = 0;
}
}
}
int expand_symbol(struct symbol *sym)
{
int retval;
struct symbol *base_type;
if (!sym)
return 0;
base_type = sym->ctype.base_type;
if (!base_type)
return 0;
retval = expand_expression(sym->initializer);
/* expand the body of the symbol */
if (base_type->type == SYM_FN) {
if (base_type->stmt)
expand_statement(base_type->stmt);
}
return retval;
}
static void expand_return_expression(struct statement *stmt)
{
expand_expression(stmt->expression);
}
static int expand_if_statement(struct statement *stmt)
{
struct expression *expr = stmt->if_conditional;
if (!expr || !expr->ctype || expr->ctype == &bad_ctype)
return UNSAFE;
expand_expression(expr);
/* This is only valid if nobody jumps into the "dead" side */
#if 0
/* Simplify constant conditionals without even evaluating the false side */
if (expr->type == EXPR_VALUE) {
struct statement *simple;
simple = expr->value ? stmt->if_true : stmt->if_false;
/* Nothing? */
if (!simple) {
stmt->type = STMT_NONE;
return 0;
}
expand_statement(simple);
*stmt = *simple;
return SIDE_EFFECTS;
}
#endif
expand_statement(stmt->if_true);
expand_statement(stmt->if_false);
return SIDE_EFFECTS;
}
static int expand_asm_statement(struct statement *stmt)
{
struct asm_operand *op;
int cost = 0;
FOR_EACH_PTR(stmt->asm_outputs, op) {
cost += expand_expression(op->expr);
} END_FOR_EACH_PTR(op);
FOR_EACH_PTR(stmt->asm_inputs, op) {
cost += expand_expression(op->expr);
} END_FOR_EACH_PTR(op);
return cost;
}
/*
* Expanding a compound statement is really just
* about adding up the costs of each individual
* statement.
*
* We also collapse a simple compound statement:
* this would trigger for simple inline functions,
* except we would have to check the "return"
* symbol usage. Next time.
*/
static int expand_compound(struct statement *stmt)
{
struct statement *s, *last;
int cost, statements;
if (stmt->ret)
expand_symbol(stmt->ret);
last = stmt->args;
cost = expand_statement(last);
statements = last != NULL;
FOR_EACH_PTR(stmt->stmts, s) {
statements++;
last = s;
cost += expand_statement(s);
} END_FOR_EACH_PTR(s);
if (statements == 1 && !stmt->ret)
*stmt = *last;
return cost;
}
static int expand_statement(struct statement *stmt)
{
if (!stmt)
return 0;
switch (stmt->type) {
case STMT_DECLARATION: {
struct symbol *sym;
FOR_EACH_PTR(stmt->declaration, sym) {
expand_symbol(sym);
} END_FOR_EACH_PTR(sym);
return SIDE_EFFECTS;
}
case STMT_RETURN:
expand_return_expression(stmt);
return SIDE_EFFECTS;
case STMT_EXPRESSION:
return expand_expression(stmt->expression);
case STMT_COMPOUND:
return expand_compound(stmt);
case STMT_IF:
return expand_if_statement(stmt);
case STMT_ITERATOR:
expand_expression(stmt->iterator_pre_condition);
expand_expression(stmt->iterator_post_condition);
expand_statement(stmt->iterator_pre_statement);
expand_statement(stmt->iterator_statement);
expand_statement(stmt->iterator_post_statement);
return SIDE_EFFECTS;
case STMT_SWITCH:
expand_expression(stmt->switch_expression);
expand_statement(stmt->switch_statement);
return SIDE_EFFECTS;
case STMT_CASE:
expand_const_expression(stmt->case_expression, "case statement");
expand_const_expression(stmt->case_to, "case statement");
expand_statement(stmt->case_statement);
return SIDE_EFFECTS;
case STMT_LABEL:
expand_statement(stmt->label_statement);
return SIDE_EFFECTS;
case STMT_GOTO:
expand_expression(stmt->goto_expression);
return SIDE_EFFECTS;
case STMT_NONE:
break;
case STMT_ASM:
expand_asm_statement(stmt);
break;
case STMT_CONTEXT:
expand_expression(stmt->expression);
break;
case STMT_RANGE:
expand_expression(stmt->range_expression);
expand_expression(stmt->range_low);
expand_expression(stmt->range_high);
break;
}
return SIDE_EFFECTS;
}
static inline int bad_integer_constant_expression(struct expression *expr)
{
if (!(expr->flags & CEF_ICE))
return 1;
if (expr->taint & Taint_comma)
return 1;
return 0;
}
static long long __get_expression_value(struct expression *expr, int strict)
{
long long value, mask;
struct symbol *ctype;
if (!expr)
return 0;
ctype = evaluate_expression(expr);
if (!ctype) {
expression_error(expr, "bad constant expression type");
return 0;
}
expand_expression(expr);
if (expr->type != EXPR_VALUE) {
if (strict != 2)
expression_error(expr, "bad constant expression");
return 0;
}
if ((strict == 1) && bad_integer_constant_expression(expr)) {
expression_error(expr, "bad integer constant expression");
return 0;
}
value = expr->value;
mask = 1ULL << (ctype->bit_size-1);
if (value & mask) {
while (ctype->type != SYM_BASETYPE)
ctype = ctype->ctype.base_type;
if (!(ctype->ctype.modifiers & MOD_UNSIGNED))
value = value | mask | ~(mask-1);
}
return value;
}
long long get_expression_value(struct expression *expr)
{
return __get_expression_value(expr, 0);
}
long long const_expression_value(struct expression *expr)
{
return __get_expression_value(expr, 1);
}
long long get_expression_value_silent(struct expression *expr)
{
return __get_expression_value(expr, 2);
}
int expr_truth_value(struct expression *expr)
{
const int saved = conservative;
struct symbol *ctype;
if (!expr)
return 0;
ctype = evaluate_expression(expr);
if (!ctype)
return -1;
conservative = 1;
expand_expression(expr);
conservative = saved;
redo:
switch (expr->type) {
case EXPR_COMMA:
expr = expr->right;
goto redo;
case EXPR_VALUE:
return expr->value != 0;
case EXPR_FVALUE:
return expr->fvalue != 0;
default:
return -1;
}
}
int is_zero_constant(struct expression *expr)
{
const int saved = conservative;
conservative = 1;
expand_expression(expr);
conservative = saved;
return expr->type == EXPR_VALUE && !expr->value;
}
sparse-0.6.4/expand.h 0000664 0000000 0000000 00000003000 14115310122 0014425 0 ustar 00root root 0000000 0000000 #ifndef EXPAND_H
#define EXPAND_H
/*
* sparse/expand.h
*
* Copyright (C) 2003 Transmeta Corp.
* 2003 Linus Torvalds
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/* Random cost numbers */
#define SIDE_EFFECTS 10000 /* The expression has side effects */
#define UNSAFE 100 /* The expression may be "infinitely costly" due to exceptions */
#define SELECT_COST 20 /* Cut-off for turning a conditional into a select */
#define BRANCH_COST 10 /* Cost of a conditional branch */
#endif
sparse-0.6.4/expression.c 0000664 0000000 0000000 00000064002 14115310122 0015351 0 ustar 00root root 0000000 0000000 /*
* sparse/expression.c
*
* Copyright (C) 2003 Transmeta Corp.
* 2003-2004 Linus Torvalds
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* This is the expression parsing part of parsing C.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "lib.h"
#include "allocate.h"
#include "token.h"
#include "parse.h"
#include "symbol.h"
#include "scope.h"
#include "expression.h"
#include "target.h"
#include "char.h"
ALLOCATOR(type_expression, "type-expr-maps");
static int match_oplist(int op, ...)
{
va_list args;
int nextop;
va_start(args, op);
do {
nextop = va_arg(args, int);
} while (nextop != 0 && nextop != op);
va_end(args);
return nextop != 0;
}
static struct token *comma_expression(struct token *, struct expression **);
struct token *parens_expression(struct token *token, struct expression **expr, const char *where)
{
struct token *p;
token = expect(token, '(', where);
p = token;
if (match_op(token, '{')) {
struct expression *e = alloc_expression(token->pos, EXPR_STATEMENT);
struct statement *stmt = alloc_statement(token->pos, STMT_COMPOUND);
*expr = e;
e->statement = stmt;
start_label_scope();
token = compound_statement(token->next, stmt);
end_label_scope();
token = expect(token, '}', "at end of statement expression");
} else
token = parse_expression(token, expr);
if (token == p)
sparse_error(token->pos, "an expression is expected before ')'");
return expect(token, ')', where);
}
struct token *string_expression(struct token *token, struct expression **expr, const char *where)
{
struct token *next = primary_expression(token, expr);
if (!*expr || (*expr)->type != EXPR_STRING) {
sparse_error(token->pos, "string literal expected for %s", where);
*expr = NULL;
}
return next;
}
/*
* Handle __func__, __FUNCTION__ and __PRETTY_FUNCTION__ token
* conversion
*/
static struct symbol *handle_func(struct token *token)
{
struct ident *ident = token->ident;
struct symbol *decl, *array;
struct string *string;
int len;
if (ident != &__func___ident &&
ident != &__FUNCTION___ident &&
ident != &__PRETTY_FUNCTION___ident)
return NULL;
if (!current_fn || !current_fn->ident)
return NULL;
/* OK, it's one of ours */
array = alloc_symbol(token->pos, SYM_ARRAY);
array->ctype.base_type = &char_ctype;
array->ctype.alignment = 1;
array->endpos = token->pos;
decl = alloc_symbol(token->pos, SYM_NODE);
decl->ctype.base_type = array;
decl->ctype.alignment = 1;
decl->ctype.modifiers = MOD_STATIC;
decl->endpos = token->pos;
/* NS_SYMBOL but in function-scope */
bind_symbol_with_scope(decl, ident, NS_SYMBOL, function_scope);
len = current_fn->ident->len;
string = __alloc_string(len + 1);
memcpy(string->data, current_fn->ident->name, len);
string->data[len] = 0;
string->length = len + 1;
decl->initializer = alloc_expression(token->pos, EXPR_STRING);
decl->initializer->string = string;
decl->initializer->ctype = decl;
decl->array_size = alloc_const_expression(token->pos, len + 1);
array->array_size = decl->array_size;
decl->bit_size = array->bit_size = bytes_to_bits(len + 1);
return decl;
}
static struct token *parse_type(struct token *token, struct expression **tree)
{
struct symbol *sym;
*tree = alloc_expression(token->pos, EXPR_TYPE);
token = typename(token, &sym, NULL);
if (sym->ident)
sparse_error(token->pos,
"type expression should not include identifier "
"\"%s\"", sym->ident->name);
(*tree)->symbol = sym;
return token;
}
static struct token *builtin_types_compatible_p_expr(struct token *token,
struct expression **tree)
{
struct expression *expr = alloc_expression(
token->pos, EXPR_COMPARE);
expr->op = SPECIAL_EQUAL;
token = token->next;
if (!match_op(token, '('))
return expect(token, '(',
"after __builtin_types_compatible_p");
token = token->next;
token = parse_type(token, &expr->left);
if (!match_op(token, ','))
return expect(token, ',',
"in __builtin_types_compatible_p");
token = token->next;
token = parse_type(token, &expr->right);
if (!match_op(token, ')'))
return expect(token, ')',
"at end of __builtin_types_compatible_p");
token = token->next;
*tree = expr;
return token;
}
static struct token *builtin_offsetof_expr(struct token *token,
struct expression **tree)
{
struct expression *expr = NULL;
struct expression **p = &expr;
struct symbol *sym;
int op = '.';
token = token->next;
if (!match_op(token, '('))
return expect(token, '(', "after __builtin_offset");
token = token->next;
token = typename(token, &sym, NULL);
if (sym->ident)
sparse_error(token->pos,
"type expression should not include identifier "
"\"%s\"", sym->ident->name);
if (!match_op(token, ','))
return expect(token, ',', "in __builtin_offset");
while (1) {
struct expression *e;
switch (op) {
case ')':
expr->in = sym;
*tree = expr;
default:
return expect(token, ')', "at end of __builtin_offset");
case SPECIAL_DEREFERENCE:
e = alloc_expression(token->pos, EXPR_OFFSETOF);
e->op = '[';
*p = e;
p = &e->down;
/* fall through */
case '.':
token = token->next;
e = alloc_expression(token->pos, EXPR_OFFSETOF);
e->op = '.';
if (token_type(token) != TOKEN_IDENT) {
sparse_error(token->pos, "Expected member name");
return token;
}
e->ident = token->ident;
token = token->next;
break;
case '[':
token = token->next;
e = alloc_expression(token->pos, EXPR_OFFSETOF);
e->op = '[';
token = parse_expression(token, &e->index);
token = expect(token, ']',
"at end of array dereference");
if (!e->index)
return token;
}
*p = e;
p = &e->down;
op = token_type(token) == TOKEN_SPECIAL ? token->special : 0;
}
}
#ifndef ULLONG_MAX
#define ULLONG_MAX (~0ULL)
#endif
static unsigned long long parse_num(const char *nptr, char **end)
{
if (nptr[0] == '0' && tolower((unsigned char)nptr[1]) == 'b')
return strtoull(&nptr[2], end, 2);
return strtoull(nptr, end, 0);
}
static void get_number_value(struct expression *expr, struct token *token)
{
const char *str = token->number;
unsigned long long value;
char *end;
int size = 0, want_unsigned = 0;
int overflow = 0, do_warn = 0;
int try_unsigned = 1;
int bits;
errno = 0;
value = parse_num(str, &end);
if (end == str)
goto Float;
if (value == ULLONG_MAX && errno == ERANGE)
overflow = 1;
while (1) {
char c = *end++;
if (!c) {
break;
} else if (c == 'u' || c == 'U') {
if (want_unsigned)
goto Enoint;
want_unsigned = 1;
} else if (c == 'l' || c == 'L') {
if (size)
goto Enoint;
size = 1;
if (*end == c) {
size = 2;
end++;
}
} else
goto Float;
}
if (overflow)
goto Eoverflow;
/* OK, it's a valid integer */
/* decimals can be unsigned only if directly specified as such */
if (str[0] != '0' && !want_unsigned)
try_unsigned = 0;
if (!size) {
bits = bits_in_int - 1;
if (!(value & (~1ULL << bits))) {
if (!(value & (1ULL << bits))) {
goto got_it;
} else if (try_unsigned) {
want_unsigned = 1;
goto got_it;
}
}
size = 1;
do_warn = 1;
}
if (size < 2) {
bits = bits_in_long - 1;
if (!(value & (~1ULL << bits))) {
if (!(value & (1ULL << bits))) {
goto got_it;
} else if (try_unsigned) {
want_unsigned = 1;
goto got_it;
}
do_warn |= 2;
}
size = 2;
do_warn |= 1;
}
bits = bits_in_longlong - 1;
if (value & (~1ULL << bits))
goto Eoverflow;
if (!(value & (1ULL << bits)))
goto got_it;
if (!try_unsigned)
warning(expr->pos, "decimal constant %s is too big for long long",
show_token(token));
want_unsigned = 1;
got_it:
if (do_warn && Wconstant_suffix)
warning(expr->pos, "constant %s is so big it is%s%s%s",
show_token(token),
want_unsigned ? " unsigned":"",
size > 0 ? " long":"",
size > 1 ? " long":"");
if (do_warn & 2)
warning(expr->pos,
"decimal constant %s is between LONG_MAX and ULONG_MAX."
" For C99 that means long long, C90 compilers are very "
"likely to produce unsigned long (and a warning) here",
show_token(token));
expr->type = EXPR_VALUE;
expr->flags = CEF_SET_INT;
expr->ctype = ctype_integer(size, want_unsigned);
expr->value = value;
return;
Eoverflow:
error_die(expr->pos, "constant %s is too big even for unsigned long long",
show_token(token));
return;
Float:
expr->fvalue = string_to_ld(str, &end);
if (str == end)
goto Enoint;
if (*end && end[1])
goto Enoint;
if (*end == 'f' || *end == 'F')
expr->ctype = &float_ctype;
else if (*end == 'l' || *end == 'L')
expr->ctype = &ldouble_ctype;
else if (!*end)
expr->ctype = &double_ctype;
else
goto Enoint;
expr->flags = CEF_SET_FLOAT;
expr->type = EXPR_FVALUE;
return;
Enoint:
sparse_error(expr->pos, "constant %s is not a valid number", show_token(token));
expr->type = EXPR_VALUE;
expr->value = 0;
expr->ctype = &int_ctype;
}
static struct token *generic_selection(struct token *token, struct expression **tree)
{
struct expression *expr = alloc_expression(token->pos, EXPR_GENERIC);
struct type_expression **last = &expr->map;
token = expect(token, '(', "after '_Generic'");
token = assignment_expression(token, &expr->control);
if (!match_op(token, ',')) {
goto end;
}
while (match_op(token, ',')) {
token = token->next;
if (lookup_type(token)) {
struct type_expression *map = __alloc_type_expression(0);
token = typename(token, &map->type, NULL);
token = expect(token, ':', "after typename");
token = assignment_expression(token, &map->expr);
*last = map;
last = &map->next;
} else if (match_ident(token, &default_ident)) {
if (expr->def) {
warning(token->pos, "multiple default in generic expression");
info(expr->def->pos, "note: previous was here");
}
token = token->next;
token = expect(token, ':', "after typename");
token = assignment_expression(token, &expr->def);
}
}
end:
*tree = expr;
return expect(token, ')', "after expression");
}
struct token *primary_expression(struct token *token, struct expression **tree)
{
struct expression *expr = NULL;
switch (token_type(token)) {
case TOKEN_CHAR ... TOKEN_WIDE_CHAR_EMBEDDED_3:
expr = alloc_expression(token->pos, EXPR_VALUE);
expr->flags = CEF_SET_CHAR;
expr->ctype = token_type(token) < TOKEN_WIDE_CHAR ? &int_ctype : &long_ctype;
get_char_constant(token, &expr->value);
token = token->next;
break;
case TOKEN_NUMBER:
expr = alloc_expression(token->pos, EXPR_VALUE);
get_number_value(expr, token); /* will see if it's an integer */
token = token->next;
break;
case TOKEN_ZERO_IDENT: {
expr = alloc_expression(token->pos, EXPR_SYMBOL);
expr->flags = CEF_SET_INT;
expr->ctype = &int_ctype;
expr->symbol = &zero_int;
expr->symbol_name = token->ident;
token = token->next;
break;
}
case TOKEN_IDENT: {
struct symbol *sym = lookup_symbol(token->ident, NS_SYMBOL | NS_TYPEDEF);
struct token *next = token->next;
if (!sym) {
sym = handle_func(token);
if (token->ident == &__builtin_types_compatible_p_ident) {
token = builtin_types_compatible_p_expr(token, &expr);
break;
}
if (token->ident == &__builtin_offsetof_ident) {
token = builtin_offsetof_expr(token, &expr);
break;
}
if (token->ident == &_Generic_ident) {
token = generic_selection(token->next, &expr);
break;
}
} else if (sym->enum_member) {
expr = alloc_expression(token->pos, EXPR_VALUE);
*expr = *sym->initializer;
/* we want the right position reported, thus the copy */
expr->pos = token->pos;
expr->flags = CEF_SET_ENUM;
token = next;
break;
}
expr = alloc_expression(token->pos, EXPR_SYMBOL);
/*
* We support types as real first-class citizens, with type
* comparisons etc:
*
* if (typeof(a) == int) ..
*/
if (sym && sym->namespace == NS_TYPEDEF) {
sparse_error(token->pos, "typename in expression");
sym = NULL;
}
expr->symbol_name = token->ident;
expr->symbol = sym;
/*
* A pointer to an lvalue designating a static storage
* duration object is an address constant [6.6(9)].
*/
if (sym && (sym->ctype.modifiers & (MOD_TOPLEVEL | MOD_STATIC)))
expr->flags = CEF_ADDR;
token = next;
break;
}
case TOKEN_STRING:
case TOKEN_WIDE_STRING:
expr = alloc_expression(token->pos, EXPR_STRING);
token = get_string_constant(token, expr);
break;
case TOKEN_SPECIAL:
if (token->special == '(') {
expr = alloc_expression(token->pos, EXPR_PREOP);
expr->op = '(';
token = parens_expression(token, &expr->unop, "in expression");
break;
}
if (token->special == '[' && lookup_type(token->next)) {
expr = alloc_expression(token->pos, EXPR_TYPE);
token = typename(token->next, &expr->symbol, NULL);
token = expect(token, ']', "in type expression");
break;
}
default:
;
}
*tree = expr;
return token;
}
static struct token *expression_list(struct token *token, struct expression_list **list)
{
while (!match_op(token, ')')) {
struct expression *expr = NULL;
token = assignment_expression(token, &expr);
if (!expr)
break;
add_expression(list, expr);
if (!match_op(token, ','))
break;
token = token->next;
}
return token;
}
/*
* extend to deal with the ambiguous C grammar for parsing
* a cast expressions followed by an initializer.
*/
static struct token *postfix_expression(struct token *token, struct expression **tree, struct expression *cast_init_expr)
{
struct expression *expr = cast_init_expr;
if (!expr)
token = primary_expression(token, &expr);
while (expr && token_type(token) == TOKEN_SPECIAL) {
switch (token->special) {
case '[': { /* Array dereference */
struct expression *deref = alloc_expression(token->pos, EXPR_PREOP);
struct expression *add = alloc_expression(token->pos, EXPR_BINOP);
deref->op = '*';
deref->unop = add;
add->op = '+';
add->left = expr;
token = parse_expression(token->next, &add->right);
token = expect(token, ']', "at end of array dereference");
expr = deref;
continue;
}
case SPECIAL_INCREMENT: /* Post-increment */
case SPECIAL_DECREMENT: { /* Post-decrement */
struct expression *post = alloc_expression(token->pos, EXPR_POSTOP);
post->op = token->special;
post->unop = expr;
expr = post;
token = token->next;
continue;
}
case SPECIAL_DEREFERENCE: { /* Structure pointer member dereference */
/* "x->y" is just shorthand for "(*x).y" */
struct expression *inner = alloc_expression(token->pos, EXPR_PREOP);
inner->op = '*';
inner->unop = expr;
expr = inner;
}
/* Fall through!! */
case '.': { /* Structure member dereference */
struct expression *deref = alloc_expression(token->pos, EXPR_DEREF);
deref->op = '.';
deref->deref = expr;
token = token->next;
if (token_type(token) != TOKEN_IDENT) {
sparse_error(token->pos, "Expected member name");
break;
}
deref->member = token->ident;
token = token->next;
expr = deref;
continue;
}
case '(': { /* Function call */
struct expression *call = alloc_expression(token->pos, EXPR_CALL);
call->op = '(';
call->fn = expr;
token = expression_list(token->next, &call->args);
token = expect(token, ')', "in function call");
expr = call;
continue;
}
default:
break;
}
break;
}
*tree = expr;
return token;
}
static struct token *cast_expression(struct token *token, struct expression **tree);
static struct token *unary_expression(struct token *token, struct expression **tree);
static struct token *type_info_expression(struct token *token,
struct expression **tree, int type)
{
struct expression *expr = alloc_expression(token->pos, type);
struct token *p;
*tree = expr;
expr->flags = CEF_SET_ICE; /* XXX: VLA support will need that changed */
token = token->next;
if (!match_op(token, '(') || !lookup_type(token->next))
return unary_expression(token, &expr->cast_expression);
p = token;
token = typename(token->next, &expr->cast_type, NULL);
if (!match_op(token, ')')) {
static const char * error[] = {
[EXPR_SIZEOF] = "at end of sizeof",
[EXPR_ALIGNOF] = "at end of __alignof__",
[EXPR_PTRSIZEOF] = "at end of __sizeof_ptr__"
};
return expect(token, ')', error[type]);
}
token = token->next;
/*
* C99 ambiguity: the typename might have been the beginning
* of a typed initializer expression..
*/
if (match_op(token, '{')) {
struct expression *cast = alloc_expression(p->pos, EXPR_CAST);
cast->cast_type = expr->cast_type;
expr->cast_type = NULL;
expr->cast_expression = cast;
token = initializer(&cast->cast_expression, token);
token = postfix_expression(token, &expr->cast_expression, cast);
}
return token;
}
static struct token *unary_expression(struct token *token, struct expression **tree)
{
if (token_type(token) == TOKEN_IDENT) {
struct ident *ident = token->ident;
if (ident->reserved) {
static const struct {
struct ident *id;
int type;
} type_information[] = {
{ &sizeof_ident, EXPR_SIZEOF },
{ &__alignof___ident, EXPR_ALIGNOF },
{ &__alignof_ident, EXPR_ALIGNOF },
{ &_Alignof_ident, EXPR_ALIGNOF },
{ &__sizeof_ptr___ident, EXPR_PTRSIZEOF },
};
int i;
for (i = 0; i < ARRAY_SIZE(type_information); i++) {
if (ident == type_information[i].id)
return type_info_expression(token, tree, type_information[i].type);
}
}
}
if (token_type(token) == TOKEN_SPECIAL) {
if (match_oplist(token->special,
SPECIAL_INCREMENT, SPECIAL_DECREMENT,
'&', '*', 0)) {
struct expression *unop;
struct expression *unary;
struct token *next;
next = cast_expression(token->next, &unop);
if (!unop) {
sparse_error(token->pos, "Syntax error in unary expression");
*tree = NULL;
return next;
}
unary = alloc_expression(token->pos, EXPR_PREOP);
unary->op = token->special;
unary->unop = unop;
*tree = unary;
return next;
}
/* possibly constant ones */
if (match_oplist(token->special, '+', '-', '~', '!', 0)) {
struct expression *unop;
struct expression *unary;
struct token *next;
next = cast_expression(token->next, &unop);
if (!unop) {
sparse_error(token->pos, "Syntax error in unary expression");
*tree = NULL;
return next;
}
unary = alloc_expression(token->pos, EXPR_PREOP);
unary->op = token->special;
unary->unop = unop;
*tree = unary;
return next;
}
/* Gcc extension: &&label gives the address of a label */
if (match_op(token, SPECIAL_LOGICAL_AND) &&
token_type(token->next) == TOKEN_IDENT) {
struct expression *label = alloc_expression(token->pos, EXPR_LABEL);
struct symbol *sym = label_symbol(token->next, 1);
if (!(sym->ctype.modifiers & MOD_ADDRESSABLE)) {
sym->ctype.modifiers |= MOD_ADDRESSABLE;
add_symbol(&function_computed_target_list, sym);
}
check_label_usage(sym, token->pos);
label->flags = CEF_ADDR;
label->label_symbol = sym;
*tree = label;
return token->next->next;
}
}
return postfix_expression(token, tree, NULL);
}
/*
* Ambiguity: a '(' can be either a cast-expression or
* a primary-expression depending on whether it is followed
* by a type or not.
*
* additional ambiguity: a "cast expression" followed by
* an initializer is really a postfix-expression.
*/
static struct token *cast_expression(struct token *token, struct expression **tree)
{
if (match_op(token, '(')) {
struct token *next = token->next;
if (lookup_type(next)) {
struct expression *cast = alloc_expression(next->pos, EXPR_CAST);
struct expression *v;
struct symbol *sym;
int is_force;
token = typename(next, &sym, &is_force);
cast->cast_type = sym;
token = expect(token, ')', "at end of cast operator");
if (match_op(token, '{')) {
if (toplevel(block_scope))
sym->ctype.modifiers |= MOD_TOPLEVEL;
if (is_force)
warning(sym->pos,
"[force] in compound literal");
token = initializer(&cast->cast_expression, token);
return postfix_expression(token, tree, cast);
}
*tree = cast;
if (is_force)
cast->type = EXPR_FORCE_CAST;
token = cast_expression(token, &v);
if (!v)
return token;
cast->cast_expression = v;
return token;
}
}
return unary_expression(token, tree);
}
/*
* Generic left-to-right binop parsing
*
* This _really_ needs to be inlined, because that makes the inner
* function call statically deterministic rather than a totally
* unpredictable indirect call. But gcc-3 is so "clever" that it
* doesn't do so by default even when you tell it to inline it.
*
* Making it a macro avoids the inlining problem, and also means
* that we can pass in the op-comparison as an expression rather
* than create a data structure for it.
*/
#define LR_BINOP_EXPRESSION(__token, tree, type, inner, compare) \
struct expression *left = NULL; \
struct token * next = inner(__token, &left); \
\
if (left) { \
while (token_type(next) == TOKEN_SPECIAL) { \
struct expression *top, *right = NULL; \
int op = next->special; \
\
if (!(compare)) \
goto out; \
top = alloc_expression(next->pos, type); \
next = inner(next->next, &right); \
if (!right) { \
sparse_error(next->pos, "No right hand side of '%s'-expression", show_special(op)); \
break; \
} \
top->op = op; \
top->left = left; \
top->right = right; \
left = top; \
} \
} \
out: \
*tree = left; \
return next; \
static struct token *multiplicative_expression(struct token *token, struct expression **tree)
{
LR_BINOP_EXPRESSION(
token, tree, EXPR_BINOP, cast_expression,
(op == '*') || (op == '/') || (op == '%')
);
}
static struct token *additive_expression(struct token *token, struct expression **tree)
{
LR_BINOP_EXPRESSION(
token, tree, EXPR_BINOP, multiplicative_expression,
(op == '+') || (op == '-')
);
}
static struct token *shift_expression(struct token *token, struct expression **tree)
{
LR_BINOP_EXPRESSION(
token, tree, EXPR_BINOP, additive_expression,
(op == SPECIAL_LEFTSHIFT) || (op == SPECIAL_RIGHTSHIFT)
);
}
static struct token *relational_expression(struct token *token, struct expression **tree)
{
LR_BINOP_EXPRESSION(
token, tree, EXPR_COMPARE, shift_expression,
(op == '<') || (op == '>') ||
(op == SPECIAL_LTE) || (op == SPECIAL_GTE)
);
}
static struct token *equality_expression(struct token *token, struct expression **tree)
{
LR_BINOP_EXPRESSION(
token, tree, EXPR_COMPARE, relational_expression,
(op == SPECIAL_EQUAL) || (op == SPECIAL_NOTEQUAL)
);
}
static struct token *bitwise_and_expression(struct token *token, struct expression **tree)
{
LR_BINOP_EXPRESSION(
token, tree, EXPR_BINOP, equality_expression,
(op == '&')
);
}
static struct token *bitwise_xor_expression(struct token *token, struct expression **tree)
{
LR_BINOP_EXPRESSION(
token, tree, EXPR_BINOP, bitwise_and_expression,
(op == '^')
);
}
static struct token *bitwise_or_expression(struct token *token, struct expression **tree)
{
LR_BINOP_EXPRESSION(
token, tree, EXPR_BINOP, bitwise_xor_expression,
(op == '|')
);
}
static struct token *logical_and_expression(struct token *token, struct expression **tree)
{
LR_BINOP_EXPRESSION(
token, tree, EXPR_LOGICAL, bitwise_or_expression,
(op == SPECIAL_LOGICAL_AND)
);
}
static struct token *logical_or_expression(struct token *token, struct expression **tree)
{
LR_BINOP_EXPRESSION(
token, tree, EXPR_LOGICAL, logical_and_expression,
(op == SPECIAL_LOGICAL_OR)
);
}
struct token *conditional_expression(struct token *token, struct expression **tree)
{
token = logical_or_expression(token, tree);
if (*tree && match_op(token, '?')) {
struct expression *expr = alloc_expression(token->pos, EXPR_CONDITIONAL);
expr->op = token->special;
expr->conditional = *tree;
*tree = expr;
token = parse_expression(token->next, &expr->cond_true);
token = expect(token, ':', "in conditional expression");
token = conditional_expression(token, &expr->cond_false);
}
return token;
}
struct token *assignment_expression(struct token *token, struct expression **tree)
{
token = conditional_expression(token, tree);
if (*tree && token_type(token) == TOKEN_SPECIAL) {
static const int assignments[] = {
'=',
SPECIAL_ADD_ASSIGN, SPECIAL_SUB_ASSIGN,
SPECIAL_MUL_ASSIGN, SPECIAL_DIV_ASSIGN,
SPECIAL_MOD_ASSIGN, SPECIAL_SHL_ASSIGN,
SPECIAL_SHR_ASSIGN, SPECIAL_AND_ASSIGN,
SPECIAL_OR_ASSIGN, SPECIAL_XOR_ASSIGN };
int i, op = token->special;
for (i = 0; i < ARRAY_SIZE(assignments); i++)
if (assignments[i] == op) {
struct expression * expr = alloc_expression(token->pos, EXPR_ASSIGNMENT);
struct token *next = token->next;
expr->left = *tree;
expr->op = op;
*tree = expr;
token = assignment_expression(next, &expr->right);
if (token == next)
expression_error(expr, "expression expected before '%s'", show_token(token));
return token;
}
}
return token;
}
static struct token *comma_expression(struct token *token, struct expression **tree)
{
LR_BINOP_EXPRESSION(
token, tree, EXPR_COMMA, assignment_expression,
(op == ',')
);
}
struct token *parse_expression(struct token *token, struct expression **tree)
{
return comma_expression(token,tree);
}
sparse-0.6.4/expression.h 0000664 0000000 0000000 00000021617 14115310122 0015363 0 ustar 00root root 0000000 0000000 #ifndef EXPRESSION_H
#define EXPRESSION_H
/*
* sparse/expression.h
*
* Copyright (C) 2003 Transmeta Corp.
* 2003 Linus Torvalds
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* Declarations and helper functions for expression parsing.
*/
#include "allocate.h"
#include "lib.h"
#include "symbol.h"
struct expression_list;
enum expression_type {
EXPR_VALUE = 1,
EXPR_STRING,
EXPR_SYMBOL,
EXPR_TYPE,
EXPR_BINOP,
EXPR_ASSIGNMENT,
EXPR_LOGICAL,
EXPR_DEREF,
EXPR_PREOP,
EXPR_POSTOP,
EXPR_CAST,
EXPR_FORCE_CAST,
EXPR_IMPLIED_CAST,
EXPR_SIZEOF,
EXPR_ALIGNOF,
EXPR_PTRSIZEOF,
EXPR_CONDITIONAL,
EXPR_SELECT, // a "safe" conditional expression
EXPR_STATEMENT,
EXPR_CALL,
EXPR_COMMA,
EXPR_COMPARE,
EXPR_LABEL,
EXPR_INITIALIZER, // initializer list
EXPR_IDENTIFIER, // identifier in initializer
EXPR_INDEX, // index in initializer
EXPR_POS, // position in initializer
EXPR_FVALUE,
EXPR_SLICE,
EXPR_OFFSETOF,
EXPR_GENERIC,
};
/*
* Flags for tracking the promotion of constness related attributes
* from subexpressions to their parents.
*
* The flags are not independent as one might imply another.
* The implications are as follows:
* - CEF_INT, CEF_ENUM and
* CEF_CHAR imply CEF_ICE.
*
* Use the CEF_*_SET_MASK and CEF_*_CLEAR_MASK
* helper macros defined below to set or clear one of these flags.
*/
enum constexpr_flag {
CEF_NONE = 0,
/*
* A constant in the sense of [6.4.4]:
* - Integer constant [6.4.4.1]
* - Floating point constant [6.4.4.2]
* - Enumeration constant [6.4.4.3]
* - Character constant [6.4.4.4]
*/
CEF_INT = (1 << 0),
CEF_FLOAT = (1 << 1),
CEF_ENUM = (1 << 2),
CEF_CHAR = (1 << 3),
/*
* A constant expression in the sense of [6.6]:
* - integer constant expression [6.6(6)]
* - arithmetic constant expression [6.6(8)]
* - address constant [6.6(9)]
*/
CEF_ICE = (1 << 4),
CEF_ACE = (1 << 5),
CEF_ADDR = (1 << 6),
/* integer constant expression => arithmetic constant expression */
CEF_SET_ICE = (CEF_ICE | CEF_ACE),
/* integer constant => integer constant expression */
CEF_SET_INT = (CEF_INT | CEF_SET_ICE),
/* floating point constant => arithmetic constant expression */
CEF_SET_FLOAT = (CEF_FLOAT | CEF_ACE),
/* enumeration constant => integer constant expression */
CEF_SET_ENUM = (CEF_ENUM | CEF_SET_ICE),
/* character constant => integer constant expression */
CEF_SET_CHAR = (CEF_CHAR | CEF_SET_ICE),
/*
* Remove any "Constant" [6.4.4] flag, but retain the "constant
* expression" [6.6] flags.
*/
CEF_CONST_MASK = (CEF_INT | CEF_FLOAT | CEF_CHAR),
/*
* not an integer constant expression => neither of integer,
* enumeration and character constant
*/
CEF_CLR_ICE = (CEF_ICE | CEF_INT | CEF_ENUM | CEF_CHAR),
};
enum {
Taint_comma = 1,
}; /* for expr->taint */
struct asm_operand {
struct ident *name;
struct expression *constraint;
struct expression *expr;
unsigned int is_assign:1;
unsigned int is_modify:1;
unsigned int is_earlyclobber:1;
unsigned int is_commutative:1;
unsigned int is_register:1;
unsigned int is_memory:1;
};
struct type_expression {
struct symbol *type;
struct expression *expr;
struct type_expression *next;
};
DECLARE_ALLOCATOR(type_expression);
struct expression {
enum expression_type type:8;
unsigned flags:8;
unsigned zero_init:1;
int op;
struct position pos;
struct symbol *ctype;
union {
// EXPR_VALUE
struct {
unsigned long long value;
unsigned taint;
};
// EXPR_FVALUE
long double fvalue;
// EXPR_STRING
struct {
int wide;
struct string *string;
};
// EXPR_UNOP, EXPR_PREOP and EXPR_POSTOP
struct /* unop */ {
struct expression *unop;
unsigned long op_value;
};
// EXPR_SYMBOL, EXPR_TYPE
struct /* symbol_arg */ {
struct symbol *symbol;
struct ident *symbol_name;
};
// EXPR_STATEMENT
struct statement *statement;
// EXPR_BINOP, EXPR_COMMA, EXPR_COMPARE, EXPR_LOGICAL and EXPR_ASSIGNMENT
struct /* binop_arg */ {
struct expression *left, *right;
};
// EXPR_DEREF
struct /* deref_arg */ {
struct expression *deref;
struct ident *member;
};
// EXPR_SLICE
struct /* slice */ {
struct expression *base;
unsigned r_bitpos;
};
// EXPR_CAST, EXPR_FORCE_CAST, EXPR_IMPLIED_CAST,
// EXPR_SIZEOF, EXPR_ALIGNOF and EXPR_PTRSIZEOF
struct /* cast_arg */ {
struct symbol *cast_type;
struct expression *cast_expression;
};
// EXPR_CONDITIONAL
// EXPR_SELECT
struct /* conditional_expr */ {
struct expression *conditional, *cond_true, *cond_false;
};
// EXPR_CALL
struct /* call_expr */ {
struct expression *fn;
struct expression_list *args;
};
// EXPR_LABEL
struct /* label_expr */ {
struct symbol *label_symbol;
};
// EXPR_INITIALIZER
struct expression_list *expr_list;
// EXPR_IDENTIFIER
struct /* ident_expr */ {
int offset;
struct ident *expr_ident;
struct symbol *field;
struct expression *ident_expression;
};
// EXPR_INDEX
struct /* index_expr */ {
unsigned int idx_from, idx_to;
struct expression *idx_expression;
};
// EXPR_POS
struct /* initpos_expr */ {
unsigned int init_offset, init_nr;
struct expression *init_expr;
};
// EXPR_OFFSETOF
struct {
struct symbol *in;
struct expression *down;
union {
struct ident *ident;
struct expression *index;
};
};
// EXPR_GENERIC
struct {
struct expression *control;
struct expression *def;
struct type_expression *map;
};
};
};
///
// Constant expression values
// --------------------------
///
// test if an expression evaluates to the constant ``0``.
// @return: ``1`` if @expr evaluate to ``0``,
// ``0`` otherwise.
int is_zero_constant(struct expression *expr);
///
// test the compile time truth value of an expression
// @return:
// * ``-1`` if @expr is not constant,
// * ``0`` or ``1`` depending on the truth value of @expr.
int expr_truth_value(struct expression *expr);
long long get_expression_value(struct expression *);
long long const_expression_value(struct expression *);
long long get_expression_value_silent(struct expression *expr);
/* Expression parsing */
struct token *parse_expression(struct token *token, struct expression **tree);
struct token *conditional_expression(struct token *token, struct expression **tree);
struct token *primary_expression(struct token *token, struct expression **tree);
struct token *parens_expression(struct token *token, struct expression **expr, const char *where);
struct token *string_expression(struct token *token, struct expression **expr, const char *where);
struct token *assignment_expression(struct token *token, struct expression **tree);
extern int expand_symbol(struct symbol *);
static inline struct expression *alloc_expression(struct position pos, int type)
{
struct expression *expr = __alloc_expression(0);
expr->type = type;
expr->pos = pos;
expr->flags = CEF_NONE;
return expr;
}
static inline struct expression *alloc_const_expression(struct position pos, int value)
{
struct expression *expr = __alloc_expression(0);
expr->type = EXPR_VALUE;
expr->pos = pos;
expr->value = value;
expr->ctype = &int_ctype;
expr->flags = CEF_SET_INT;
return expr;
}
/* Type name parsing */
struct token *typename(struct token *, struct symbol **, int *);
static inline int lookup_type(struct token *token)
{
if (token->pos.type == TOKEN_IDENT) {
struct symbol *sym = lookup_symbol(token->ident, NS_SYMBOL | NS_TYPEDEF);
return sym && (sym->namespace & NS_TYPEDEF);
}
return 0;
}
/* Statement parsing */
struct statement *alloc_statement(struct position pos, int type);
struct token *initializer(struct expression **tree, struct token *token);
struct token *compound_statement(struct token *, struct statement *);
/* The preprocessor calls this 'constant_expression()' */
#define constant_expression(token,tree) conditional_expression(token, tree)
/* Cast folding of constant values.. */
void cast_value(struct expression *expr, struct symbol *newtype,
struct expression *old, struct symbol *oldtype);
#endif
sparse-0.6.4/flow.c 0000664 0000000 0000000 00000056477 14115310122 0014142 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2004 Linus Torvalds
*/
///
// Flow simplification
// -------------------
#include
#include
#include
#include
#include
#include
#include "parse.h"
#include "expression.h"
#include "linearize.h"
#include "simplify.h"
#include "flow.h"
#include "target.h"
unsigned long bb_generation;
///
// remove phi-sources from a removed edge
//
// :note: It's possible to have several edges between the same BBs.
// It's common with switches but it's also possible with branches.
// This function will only remove a single phi-source per edge.
int remove_phisources(struct basic_block *par, struct basic_block *old)
{
struct instruction *insn;
int changed = 0;
FOR_EACH_PTR(old->insns, insn) {
pseudo_t phi;
if (!insn->bb)
continue;
if (insn->opcode != OP_PHI)
return changed;
// found a phi-node in the target BB,
// now look after its phi-sources.
FOR_EACH_PTR(insn->phi_list, phi) {
struct instruction *phisrc = phi->def;
if (phi == VOID)
continue;
assert(phisrc->phi_node == insn);
if (phisrc->bb != par)
continue;
// found a phi-source corresponding to this edge:
// remove it but avoid the recursive killing.
REPLACE_CURRENT_PTR(phi, VOID);
remove_use(&phisrc->src);
phisrc->bb = NULL;
changed |= REPEAT_CSE;
// Only the first one must be removed.
goto next;
} END_FOR_EACH_PTR(phi);
next: ;
} END_FOR_EACH_PTR(insn);
return changed;
}
///
// remove all phisources but the one corresponding to the given target
static int remove_other_phisources(struct basic_block *bb, struct multijmp_list *list, struct basic_block *target)
{
struct multijmp *jmp;
int changed = 0;
FOR_EACH_PTR(list, jmp) {
if (jmp->target == target) {
target = NULL;
continue;
}
changed |= remove_phisources(bb, jmp->target);
} END_FOR_EACH_PTR(jmp);
return changed;
}
/*
* Dammit, if we have a phi-node followed by a conditional
* branch on that phi-node, we should damn well be able to
* do something about the source. Maybe.
*/
static int rewrite_branch(struct basic_block *bb,
struct basic_block **ptr,
struct basic_block *old,
struct basic_block *new)
{
if (*ptr != old || new == old || !bb->ep)
return 0;
/* We might find new if-conversions or non-dominating CSEs */
/* we may also create new dead cycles */
repeat_phase |= REPEAT_CSE | REPEAT_CFG_CLEANUP;
*ptr = new;
replace_bb_in_list(&bb->children, old, new, 1);
remove_bb_from_list(&old->parents, bb, 1);
add_bb(&new->parents, bb);
return 1;
}
/*
* Return the known truth value of a pseudo, or -1 if
* it's not known.
*/
static int pseudo_truth_value(pseudo_t pseudo)
{
switch (pseudo->type) {
case PSEUDO_VAL:
return !!pseudo->value;
case PSEUDO_REG: {
struct instruction *insn = pseudo->def;
/* A symbol address is always considered true.. */
if (insn->opcode == OP_SYMADDR && insn->target == pseudo)
return 1;
}
/* Fall through */
default:
return -1;
}
}
/*
* Does a basic block depend on the pseudos that "src" defines?
*/
static int bb_depends_on(struct basic_block *target, struct basic_block *src)
{
pseudo_t pseudo;
FOR_EACH_PTR(src->defines, pseudo) {
if (pseudo_in_list(target->needs, pseudo))
return 1;
} END_FOR_EACH_PTR(pseudo);
return 0;
}
/*
* This really should be handled by bb_depends_on()
* which efficiently check the dependence using the
* defines - needs liveness info. Problem is that
* there is no liveness done on OP_PHI & OP_PHISRC.
*
* This function add the missing dependency checks.
*/
static int bb_depends_on_phi(struct basic_block *target, struct basic_block *src)
{
struct instruction *insn;
FOR_EACH_PTR(src->insns, insn) {
if (!insn->bb)
continue;
if (insn->opcode != OP_PHI)
continue;
if (pseudo_in_list(target->needs, insn->target))
return 1;
} END_FOR_EACH_PTR(insn);
return 0;
}
///
// does the BB contains ignorable instructions but a final branch?
// :note: something could be done for phi-sources but ... we'll see.
static bool bb_is_forwarder(struct basic_block *bb)
{
struct instruction *insn;
FOR_EACH_PTR(bb->insns, insn) {
if (!insn->bb)
continue;
switch (insn->opcode) {
case OP_NOP:
case OP_INLINED_CALL:
continue;
case OP_CBR:
case OP_BR:
return true;
default:
goto out;
}
} END_FOR_EACH_PTR(insn);
out:
return false;
}
///
// check if the sources of a phi-node match with the parent BBs
static bool phi_check(struct instruction *node)
{
struct basic_block *bb;
pseudo_t phi;
PREPARE_PTR_LIST(node->bb->parents, bb);
FOR_EACH_PTR(node->phi_list, phi) {
if (phi == VOID || !phi->def)
continue;
if (phi->def->bb != bb)
return false;
NEXT_PTR_LIST(bb);
} END_FOR_EACH_PTR(phi);
if (bb)
return false;
FINISH_PTR_LIST(bb);
return true;
}
/*
* When we reach here, we have:
* - a basic block that ends in a conditional branch and
* that has no side effects apart from the pseudos it
* may change.
* - the phi-node that the conditional branch depends on
* - full pseudo liveness information
*
* We need to check if any of the _sources_ of the phi-node
* may be constant, and not actually need this block at all.
*/
static int try_to_simplify_bb(struct basic_block *bb, struct instruction *first, struct instruction *second)
{
int changed = 0;
pseudo_t phi;
int bogus;
/*
* This a due to improper dominance tracking during
* simplify_symbol_usage()/conversion to SSA form.
* No sane simplification can be done when we have this.
*/
bogus = !phi_check(first);
FOR_EACH_PTR(first->phi_list, phi) {
struct instruction *def = phi->def;
struct basic_block *source, *target;
pseudo_t pseudo;
struct instruction *br;
int cond;
if (!def)
continue;
source = def->bb;
pseudo = def->src1;
if (!pseudo || !source)
continue;
br = last_instruction(source->insns);
if (!br)
continue;
if (br->opcode != OP_CBR && br->opcode != OP_BR)
continue;
cond = pseudo_truth_value(pseudo);
if (cond < 0)
continue;
target = cond ? second->bb_true : second->bb_false;
if (bb_depends_on(target, bb))
continue;
if (bb_depends_on_phi(target, bb))
continue;
changed |= rewrite_branch(source, &br->bb_true, bb, target);
changed |= rewrite_branch(source, &br->bb_false, bb, target);
if (changed && !bogus)
kill_use(THIS_ADDRESS(phi));
} END_FOR_EACH_PTR(phi);
return changed;
}
static int bb_has_side_effects(struct basic_block *bb)
{
struct instruction *insn;
FOR_EACH_PTR(bb->insns, insn) {
if (!insn->bb)
continue;
switch (insn->opcode) {
case OP_CALL:
/* FIXME! This should take "const" etc into account */
return 1;
case OP_LOAD:
if (!insn->type)
return 1;
if (insn->is_volatile)
return 1;
continue;
case OP_STORE:
case OP_CONTEXT:
return 1;
case OP_ASM:
/* FIXME! This should take "volatile" etc into account */
return 1;
default:
continue;
}
} END_FOR_EACH_PTR(insn);
return 0;
}
static int simplify_phi_branch(struct basic_block *bb, struct instruction *br)
{
pseudo_t cond = br->cond;
struct instruction *def;
if (cond->type != PSEUDO_REG)
return 0;
def = cond->def;
if (def->bb != bb || def->opcode != OP_PHI)
return 0;
if (bb_has_side_effects(bb))
return 0;
return try_to_simplify_bb(bb, def, br);
}
static int simplify_branch_branch(struct basic_block *bb, struct instruction *br,
struct basic_block **target_p, int bb_true)
{
struct basic_block *target = *target_p, *final;
struct instruction *insn;
int retval;
if (target == bb)
return 0;
insn = last_instruction(target->insns);
if (!insn || insn->opcode != OP_CBR || insn->cond != br->cond)
return 0;
/*
* Ahhah! We've found a branch to a branch on the same conditional!
* Now we just need to see if we can rewrite the branch..
*/
retval = 0;
final = bb_true ? insn->bb_true : insn->bb_false;
if (bb_has_side_effects(target))
goto try_to_rewrite_target;
if (bb_depends_on(final, target))
goto try_to_rewrite_target;
if (bb_depends_on_phi(final, target))
return 0;
return rewrite_branch(bb, target_p, target, final);
try_to_rewrite_target:
/*
* If we're the only parent, at least we can rewrite the
* now-known second branch.
*/
if (bb_list_size(target->parents) != 1)
return retval;
convert_to_jump(insn, final);
return 1;
}
static int simplify_one_branch(struct basic_block *bb, struct instruction *br)
{
if (simplify_phi_branch(bb, br))
return 1;
return simplify_branch_branch(bb, br, &br->bb_true, 1) |
simplify_branch_branch(bb, br, &br->bb_false, 0);
}
static int simplify_branch_nodes(struct entrypoint *ep)
{
int changed = 0;
struct basic_block *bb;
FOR_EACH_PTR(ep->bbs, bb) {
struct instruction *br = last_instruction(bb->insns);
if (!br || br->opcode != OP_CBR)
continue;
changed |= simplify_one_branch(bb, br);
} END_FOR_EACH_PTR(bb);
return changed;
}
/*
* This is called late - when we have intra-bb liveness information..
*/
int simplify_flow(struct entrypoint *ep)
{
return simplify_branch_nodes(ep);
}
static inline void concat_user_list(struct pseudo_user_list *src, struct pseudo_user_list **dst)
{
copy_ptr_list((struct ptr_list **)dst, (struct ptr_list *)src);
}
void convert_instruction_target(struct instruction *insn, pseudo_t src)
{
pseudo_t target;
struct pseudo_user *pu;
/*
* Go through the "insn->users" list and replace them all..
*/
target = insn->target;
if (target == src)
return;
FOR_EACH_PTR(target->users, pu) {
if (*pu->userp != VOID) {
assert(*pu->userp == target);
*pu->userp = src;
}
} END_FOR_EACH_PTR(pu);
if (has_use_list(src))
concat_user_list(target->users, &src->users);
target->users = NULL;
}
static int overlapping_memop(struct instruction *a, struct instruction *b)
{
unsigned int a_start = bytes_to_bits(a->offset);
unsigned int b_start = bytes_to_bits(b->offset);
unsigned int a_size = a->size;
unsigned int b_size = b->size;
if (a_size + a_start <= b_start)
return 0;
if (b_size + b_start <= a_start)
return 0;
return 1;
}
static inline int same_memop(struct instruction *a, struct instruction *b)
{
return a->offset == b->offset && a->size == b->size;
}
static inline int distinct_symbols(pseudo_t a, pseudo_t b)
{
if (a->type != PSEUDO_SYM)
return 0;
if (b->type != PSEUDO_SYM)
return 0;
return a->sym != b->sym;
}
/*
* Return 1 if "dom" dominates the access to "pseudo"
* in "insn".
*
* Return 0 if it doesn't, and -1 if you don't know.
*/
int dominates(struct instruction *insn, struct instruction *dom, int local)
{
switch (dom->opcode) {
case OP_CALL: case OP_ENTRY:
return local ? 0 : -1;
case OP_LOAD: case OP_STORE:
break;
case OP_ASM:
if (dom->clobber_memory)
return -1;
if (dom->output_memory)
return -1;
return 0;
default:
return 0;
}
if (dom->src != insn->src) {
if (local)
return 0;
/* We don't think two explicitly different symbols ever alias */
if (distinct_symbols(insn->src, dom->src))
return 0;
/* We could try to do some alias analysis here */
return -1;
}
if (!same_memop(insn, dom)) {
if (!overlapping_memop(insn, dom))
return 0;
return -1;
}
return 1;
}
/* Kill a pseudo that is dead on exit from the bb */
// The context is:
// * the variable is not global but may have its address used (local/non-local)
// * the stores are only needed by others functions which would do some
// loads via the escaped address
// We start by the terminating BB (normal exit BB + no-return/unreachable)
// We walkup the BB' intruction backward
// * we're only concerned by loads, stores & calls
// * if we reach a call -> we have to stop if var is non-local
// * if we reach a load of our var -> we have to stop
// * if we reach a store of our var -> we can kill it, it's dead
// * we can ignore other stores & loads if the var is local
// * if we reach another store or load done via non-symbol access
// (so done via some address calculation) -> we have to stop
// If we reach the top of the BB we can recurse into the parents BBs.
static void kill_dead_stores_bb(pseudo_t pseudo, unsigned long generation, struct basic_block *bb, int local)
{
struct instruction *insn;
struct basic_block *parent;
if (bb->generation == generation)
return;
bb->generation = generation;
FOR_EACH_PTR_REVERSE(bb->insns, insn) {
if (!insn->bb)
continue;
switch (insn->opcode) {
case OP_LOAD:
if (insn->src == pseudo)
return;
break;
case OP_STORE:
if (insn->src == pseudo) {
kill_instruction_force(insn);
continue;
}
break;
case OP_CALL:
if (!local)
return;
default:
continue;
}
if (!local && insn->src->type != PSEUDO_SYM)
return;
} END_FOR_EACH_PTR_REVERSE(insn);
FOR_EACH_PTR(bb->parents, parent) {
if (bb_list_size(parent->children) > 1)
continue;
kill_dead_stores_bb(pseudo, generation, parent, local);
} END_FOR_EACH_PTR(parent);
}
void check_access(struct instruction *insn)
{
pseudo_t pseudo = insn->src;
if (insn->bb && pseudo->type == PSEUDO_SYM) {
int offset = insn->offset, bit = bytes_to_bits(offset) + insn->size;
struct symbol *sym = pseudo->sym;
if (sym->bit_size > 0 && (offset < 0 || bit > sym->bit_size)) {
if (insn->tainted)
return;
warning(insn->pos, "invalid access %s '%s' (%d %d)",
offset < 0 ? "below" : "past the end of",
show_ident(sym->ident), offset,
bits_to_bytes(sym->bit_size));
insn->tainted = 1;
}
}
}
static struct pseudo_user *first_user(pseudo_t p)
{
struct pseudo_user *pu;
FOR_EACH_PTR(p->users, pu) {
if (!pu)
continue;
return pu;
} END_FOR_EACH_PTR(pu);
return NULL;
}
void kill_dead_stores(struct entrypoint *ep, pseudo_t addr, int local)
{
unsigned long generation;
struct basic_block *bb;
switch (pseudo_user_list_size(addr->users)) {
case 0:
return;
case 1:
if (local) {
struct pseudo_user *pu = first_user(addr);
struct instruction *insn = pu->insn;
if (insn->opcode == OP_STORE) {
kill_instruction_force(insn);
return;
}
}
default:
break;
}
generation = ++bb_generation;
FOR_EACH_PTR(ep->bbs, bb) {
if (bb->children)
continue;
kill_dead_stores_bb(addr, generation, bb, local);
} END_FOR_EACH_PTR(bb);
}
static void mark_bb_reachable(struct basic_block *bb, unsigned long generation)
{
struct basic_block *child;
if (bb->generation == generation)
return;
bb->generation = generation;
FOR_EACH_PTR(bb->children, child) {
mark_bb_reachable(child, generation);
} END_FOR_EACH_PTR(child);
}
static void kill_defs(struct instruction *insn)
{
pseudo_t target = insn->target;
if (!has_use_list(target))
return;
if (target->def != insn)
return;
convert_instruction_target(insn, VOID);
}
void kill_bb(struct basic_block *bb)
{
struct instruction *insn;
struct basic_block *child, *parent;
FOR_EACH_PTR(bb->insns, insn) {
if (!insn->bb)
continue;
kill_instruction_force(insn);
kill_defs(insn);
/*
* We kill unreachable instructions even if they
* otherwise aren't "killable" (e.g. volatile loads)
*/
} END_FOR_EACH_PTR(insn);
bb->insns = NULL;
FOR_EACH_PTR(bb->children, child) {
remove_bb_from_list(&child->parents, bb, 0);
} END_FOR_EACH_PTR(child);
bb->children = NULL;
FOR_EACH_PTR(bb->parents, parent) {
remove_bb_from_list(&parent->children, bb, 0);
} END_FOR_EACH_PTR(parent);
bb->parents = NULL;
}
void kill_unreachable_bbs(struct entrypoint *ep)
{
struct basic_block *bb;
unsigned long generation = ++bb_generation;
mark_bb_reachable(ep->entry->bb, generation);
FOR_EACH_PTR(ep->bbs, bb) {
if (bb->generation == generation)
continue;
/* Mark it as being dead */
kill_bb(bb);
bb->ep = NULL;
DELETE_CURRENT_PTR(bb);
} END_FOR_EACH_PTR(bb);
PACK_PTR_LIST(&ep->bbs);
}
static int rewrite_parent_branch(struct basic_block *bb, struct basic_block *old, struct basic_block *new)
{
int changed = 0;
struct instruction *insn = last_instruction(bb->insns);
if (!insn)
return 0;
/* Infinite loops: let's not "optimize" them.. */
if (old == new)
return 0;
switch (insn->opcode) {
case OP_CBR:
changed |= rewrite_branch(bb, &insn->bb_false, old, new);
/* fall through */
case OP_BR:
changed |= rewrite_branch(bb, &insn->bb_true, old, new);
assert(changed);
return changed;
case OP_SWITCH: {
struct multijmp *jmp;
FOR_EACH_PTR(insn->multijmp_list, jmp) {
changed |= rewrite_branch(bb, &jmp->target, old, new);
} END_FOR_EACH_PTR(jmp);
assert(changed);
return changed;
}
default:
return 0;
}
}
static struct basic_block * rewrite_branch_bb(struct basic_block *bb, struct instruction *br)
{
struct basic_block *parent;
struct basic_block *target = br->bb_true;
if (br->opcode == OP_CBR) {
pseudo_t cond = br->cond;
if (cond->type != PSEUDO_VAL)
return NULL;
target = cond->value ? target : br->bb_false;
}
/*
* We can't do FOR_EACH_PTR() here, because the parent list
* may change when we rewrite the parent.
*/
while ((parent = first_basic_block(bb->parents)) != NULL) {
if (!rewrite_parent_branch(parent, bb, target))
return NULL;
}
return target;
}
static void vrfy_bb_in_list(struct basic_block *bb, struct basic_block_list *list)
{
if (bb) {
struct basic_block *tmp;
int no_bb_in_list = 0;
FOR_EACH_PTR(list, tmp) {
if (bb == tmp)
return;
} END_FOR_EACH_PTR(tmp);
assert(no_bb_in_list);
}
}
static void vrfy_parents(struct basic_block *bb)
{
struct basic_block *tmp;
FOR_EACH_PTR(bb->parents, tmp) {
vrfy_bb_in_list(bb, tmp->children);
} END_FOR_EACH_PTR(tmp);
}
static void vrfy_children(struct basic_block *bb)
{
struct basic_block *tmp;
struct instruction *br = last_instruction(bb->insns);
if (!br) {
assert(!bb->children);
return;
}
switch (br->opcode) {
struct multijmp *jmp;
case OP_CBR:
vrfy_bb_in_list(br->bb_false, bb->children);
/* fall through */
case OP_BR:
vrfy_bb_in_list(br->bb_true, bb->children);
break;
case OP_SWITCH:
case OP_COMPUTEDGOTO:
FOR_EACH_PTR(br->multijmp_list, jmp) {
vrfy_bb_in_list(jmp->target, bb->children);
} END_FOR_EACH_PTR(jmp);
break;
default:
break;
}
FOR_EACH_PTR(bb->children, tmp) {
vrfy_bb_in_list(bb, tmp->parents);
} END_FOR_EACH_PTR(tmp);
}
static void vrfy_bb_flow(struct basic_block *bb)
{
vrfy_children(bb);
vrfy_parents(bb);
}
void vrfy_flow(struct entrypoint *ep)
{
struct basic_block *bb;
struct basic_block *entry = ep->entry->bb;
FOR_EACH_PTR(ep->bbs, bb) {
if (bb == entry)
entry = NULL;
vrfy_bb_flow(bb);
} END_FOR_EACH_PTR(bb);
assert(!entry);
}
///
// change a switch or a conditional branch into a branch
int convert_to_jump(struct instruction *insn, struct basic_block *target)
{
struct basic_block *bb = insn->bb;
struct basic_block *child;
int changed = REPEAT_CSE;
switch (insn->opcode) {
case OP_CBR:
changed |= remove_phisources(insn->bb, insn->bb_true == target ? insn->bb_false : insn->bb_true);
break;
case OP_SWITCH:
changed |= remove_other_phisources(insn->bb, insn->multijmp_list, target);
break;
}
kill_use(&insn->cond);
insn->bb_true = target;
insn->bb_false = NULL;
insn->cond = NULL;
insn->size = 0;
insn->opcode = OP_BR;
FOR_EACH_PTR(bb->children, child) {
if (child == target) {
target = NULL; // leave first occurence
continue;
}
DELETE_CURRENT_PTR(child);
remove_bb_from_list(&child->parents, bb, 1);
changed |= REPEAT_CFG_CLEANUP;
} END_FOR_EACH_PTR(child);
PACK_PTR_LIST(&bb->children);
repeat_phase |= changed;
return changed;
}
static int retarget_parents(struct basic_block *bb, struct basic_block *target)
{
struct basic_block *parent;
/*
* We can't do FOR_EACH_PTR() here, because the parent list
* may change when we rewrite the parent.
*/
while ((parent = first_basic_block(bb->parents))) {
if (!rewrite_parent_branch(parent, bb, target))
return 0;
}
kill_bb(bb);
return REPEAT_CFG_CLEANUP;
}
static void remove_merging_phisrc(struct instruction *insn, struct basic_block *bot)
{
struct instruction *node = insn->phi_node;
pseudo_t phi;
if (!node) {
kill_instruction(insn);
return;
}
FOR_EACH_PTR(node->phi_list, phi) {
struct instruction *phisrc;
if (phi == VOID)
continue;
phisrc = phi->def;
if (phisrc->bb == bot) {
kill_instruction(insn);
return;
}
} END_FOR_EACH_PTR(phi);
}
static void remove_merging_phi(struct basic_block *top, struct instruction *insn)
{
pseudo_t phi;
FOR_EACH_PTR(insn->phi_list, phi) {
struct instruction *def;
if (phi == VOID)
continue;
def = phi->def;
if (def->bb != top)
continue;
convert_instruction_target(insn, def->src);
kill_instruction(def);
kill_instruction(insn);
} END_FOR_EACH_PTR(phi);
}
///
// merge two BBs
// @top: the first BB to be merged
// @bot: the second BB to be merged
static int merge_bb(struct basic_block *top, struct basic_block *bot)
{
struct instruction *insn;
struct basic_block *bb;
if (top == bot)
return 0;
top->children = bot->children;
bot->children = NULL;
bot->parents = NULL;
FOR_EACH_PTR(top->children, bb) {
replace_bb_in_list(&bb->parents, bot, top, 1);
} END_FOR_EACH_PTR(bb);
FOR_EACH_PTR(top->insns, insn) {
if (!insn->bb)
continue;
if (insn->opcode != OP_PHISOURCE)
continue;
remove_merging_phisrc(insn, bot);
} END_FOR_EACH_PTR(insn);
kill_instruction(delete_last_instruction(&top->insns));
FOR_EACH_PTR(bot->insns, insn) {
if (!insn->bb)
continue;
assert(insn->bb == bot);
switch (insn->opcode) {
case OP_PHI:
remove_merging_phi(top, insn);
continue;
}
insn->bb = top;
add_instruction(&top->insns, insn);
} END_FOR_EACH_PTR(insn);
bot->insns = NULL;
bot->ep = NULL;
return REPEAT_CFG_CLEANUP;
}
///
// early simplification of the CFG
// Three things are done here:
// # inactive BB are removed
// # branches to a 'forwarder' BB are redirected to the forwardee.
// # merge single-child/single-parent BBs.
int simplify_cfg_early(struct entrypoint *ep)
{
struct basic_block *bb;
int changed = 0;
FOR_EACH_PTR_REVERSE(ep->bbs, bb) {
struct instruction *insn;
struct basic_block *tgt;
if (!bb->ep) {
DELETE_CURRENT_PTR(bb);
changed = REPEAT_CFG_CLEANUP;
continue;
}
insn = last_instruction(bb->insns);
if (!insn)
continue;
switch (insn->opcode) {
case OP_BR:
tgt = insn->bb_true;
if (bb_is_forwarder(bb))
changed |= retarget_parents(bb, tgt);
else if (bb_list_size(tgt->parents) == 1)
changed |= merge_bb(bb, tgt);
break;
}
} END_FOR_EACH_PTR_REVERSE(bb);
return changed;
}
void pack_basic_blocks(struct entrypoint *ep)
{
struct basic_block *bb;
/* See if we can merge a bb into another one.. */
FOR_EACH_PTR(ep->bbs, bb) {
struct instruction *first;
struct basic_block *parent, *child, *last;
if (!bb_reachable(bb))
continue;
/*
* Just a branch?
*/
FOR_EACH_PTR(bb->insns, first) {
if (!first->bb)
continue;
switch (first->opcode) {
case OP_NOP:
case OP_INLINED_CALL:
continue;
case OP_CBR:
case OP_BR: {
struct basic_block *replace;
replace = rewrite_branch_bb(bb, first);
if (replace) {
kill_bb(bb);
goto no_merge;
}
}
/* fallthrough */
default:
goto out;
}
} END_FOR_EACH_PTR(first);
out:
/*
* See if we only have one parent..
*/
last = NULL;
FOR_EACH_PTR(bb->parents, parent) {
if (last) {
if (last != parent)
goto no_merge;
continue;
}
last = parent;
} END_FOR_EACH_PTR(parent);
parent = last;
if (!parent || parent == bb)
continue;
/*
* Goodie. See if the parent can merge..
*/
FOR_EACH_PTR(parent->children, child) {
if (child != bb)
goto no_merge;
} END_FOR_EACH_PTR(child);
repeat_phase |= merge_bb(parent, bb);
no_merge:
/* nothing to do */;
} END_FOR_EACH_PTR(bb);
}
sparse-0.6.4/flow.h 0000664 0000000 0000000 00000002736 14115310122 0014134 0 ustar 00root root 0000000 0000000 #ifndef FLOW_H
#define FLOW_H
#include "lib.h"
extern unsigned long bb_generation;
#define REPEAT_CSE (1 << 0)
#define REPEAT_CFG_CLEANUP (1 << 2)
struct entrypoint;
struct instruction;
extern int remove_phisources(struct basic_block *par, struct basic_block *old);
extern int simplify_flow(struct entrypoint *ep);
extern void kill_dead_stores(struct entrypoint *ep, pseudo_t addr, int local);
extern void simplify_symbol_usage(struct entrypoint *ep);
extern void simplify_memops(struct entrypoint *ep);
extern void pack_basic_blocks(struct entrypoint *ep);
extern int simplify_cfg_early(struct entrypoint *ep);
extern int convert_to_jump(struct instruction *insn, struct basic_block *target);
extern void convert_instruction_target(struct instruction *insn, pseudo_t src);
extern void remove_dead_insns(struct entrypoint *);
extern void kill_bb(struct basic_block *);
extern void kill_use(pseudo_t *);
extern void remove_use(pseudo_t *);
extern void kill_unreachable_bbs(struct entrypoint *ep);
extern int kill_insn(struct instruction *, int force);
static inline int kill_instruction(struct instruction *insn)
{
return kill_insn(insn, 0);
}
static inline int kill_instruction_force(struct instruction *insn)
{
return kill_insn(insn, 1);
}
void check_access(struct instruction *insn);
int dominates(struct instruction *insn, struct instruction *dom, int local);
extern void vrfy_flow(struct entrypoint *ep);
extern int pseudo_in_list(struct pseudo_list *list, pseudo_t pseudo);
#endif
sparse-0.6.4/flowgraph.c 0000664 0000000 0000000 00000011471 14115310122 0015145 0 ustar 00root root 0000000 0000000 // SPDX-License-Identifier: MIT
//
// Various utilities for flowgraphs.
//
// Copyright (c) 2017 Luc Van Oostenryck.
//
#include "flowgraph.h"
#include "linearize.h"
#include "flow.h" // for bb_generation
#include
#include
#include
struct cfg_info {
struct basic_block_list *list;
unsigned long gen;
unsigned int nr;
};
static void label_postorder(struct basic_block *bb, struct cfg_info *info)
{
struct basic_block *child;
if (bb->generation == info->gen)
return;
bb->generation = info->gen;
FOR_EACH_PTR_REVERSE(bb->children, child) {
label_postorder(child, info);
} END_FOR_EACH_PTR_REVERSE(child);
bb->postorder_nr = info->nr++;
add_bb(&info->list, bb);
}
static void reverse_bbs(struct basic_block_list **dst, struct basic_block_list *src)
{
struct basic_block *bb;
FOR_EACH_PTR_REVERSE(src, bb) {
add_bb(dst, bb);
} END_FOR_EACH_PTR_REVERSE(bb);
}
static void debug_postorder(struct entrypoint *ep)
{
struct basic_block *bb;
printf("%s's reverse postorder:\n", show_ident(ep->name->ident));
FOR_EACH_PTR(ep->bbs, bb) {
printf("\t.L%u: %u\n", bb->nr, bb->postorder_nr);
} END_FOR_EACH_PTR(bb);
}
//
// cfg_postorder - Set the BB's reverse postorder links
//
// Do a postorder DFS walk and set the links
// (which will do the reverse part).
//
int cfg_postorder(struct entrypoint *ep)
{
struct cfg_info info = {
.gen = ++bb_generation,
};
label_postorder(ep->entry->bb, &info);
// OK, now info.list contains the node in postorder
// Reuse ep->bbs for the reverse postorder.
free_ptr_list(&ep->bbs);
ep->bbs = NULL;
reverse_bbs(&ep->bbs, info.list);
free_ptr_list(&info.list);
if (dbg_postorder)
debug_postorder(ep);
return info.nr;
}
//
// Calculate the dominance tree following:
// "A simple, fast dominance algorithm"
// by K. D. Cooper, T. J. Harvey, and K. Kennedy.
// cfr. http://www.cs.rice.edu/∼keith/EMBED/dom.pdf
//
static struct basic_block *intersect_dom(struct basic_block *doms[],
struct basic_block *b1, struct basic_block *b2)
{
int f1 = b1->postorder_nr, f2 = b2->postorder_nr;
while (f1 != f2) {
while (f1 < f2) {
b1 = doms[f1];
f1 = b1->postorder_nr;
}
while (f2 < f1) {
b2 = doms[f2];
f2 = b2->postorder_nr;
}
}
return b1;
}
static void debug_domtree(struct entrypoint *ep)
{
struct basic_block *bb = ep->entry->bb;
printf("%s's idoms:\n", show_ident(ep->name->ident));
FOR_EACH_PTR(ep->bbs, bb) {
if (bb == ep->entry->bb)
continue; // entry node has no idom
printf("\t%s <- %s\n", show_label(bb), show_label(bb->idom));
} END_FOR_EACH_PTR(bb);
}
void domtree_build(struct entrypoint *ep)
{
struct basic_block *entry = ep->entry->bb;
struct basic_block **doms;
struct basic_block *bb;
unsigned int size;
int max_level = 0;
int changed;
// First calculate the (reverse) postorder.
// This will give use us:
// - the links to do a reverse postorder traversal
// - the order number for each block
size = cfg_postorder(ep);
// initialize the dominators array
doms = calloc(size, sizeof(*doms));
assert(entry->postorder_nr == size-1);
doms[size-1] = entry;
do {
struct basic_block *b;
changed = 0;
FOR_EACH_PTR(ep->bbs, b) {
struct basic_block *p;
int bnr = b->postorder_nr;
struct basic_block *new_idom = NULL;
if (b == entry)
continue; // ignore entry node
FOR_EACH_PTR(b->parents, p) {
unsigned int pnr = p->postorder_nr;
if (!doms[pnr])
continue;
if (!new_idom) {
new_idom = p;
continue;
}
new_idom = intersect_dom(doms, p, new_idom);
} END_FOR_EACH_PTR(p);
assert(new_idom);
if (doms[bnr] != new_idom) {
doms[bnr] = new_idom;
changed = 1;
}
} END_FOR_EACH_PTR(b);
} while (changed);
FOR_EACH_PTR(ep->bbs, bb) {
free_ptr_list(&bb->doms);
} END_FOR_EACH_PTR(bb);
// set the idom links
FOR_EACH_PTR(ep->bbs, bb) {
struct basic_block *idom = doms[bb->postorder_nr];
if (bb == entry)
continue; // ignore entry node
bb->idom = idom;
add_bb(&idom->doms, bb);
} END_FOR_EACH_PTR(bb);
entry->idom = NULL;
// set the dominance levels
FOR_EACH_PTR(ep->bbs, bb) {
struct basic_block *idom = bb->idom;
int level = idom ? idom->dom_level + 1 : 0;
bb->dom_level = level;
if (max_level < level)
max_level = level;
} END_FOR_EACH_PTR(bb);
ep->dom_levels = max_level + 1;
free(doms);
if (dbg_domtree)
debug_domtree(ep);
}
// dt_dominates - does BB a dominates BB b?
bool domtree_dominates(struct basic_block *a, struct basic_block *b)
{
if (a == b) // dominance is reflexive
return true;
if (a == b->idom)
return true;
if (b == a->idom)
return false;
// can't dominate if deeper in the DT
if (a->dom_level >= b->dom_level)
return false;
// FIXME: can be faster if we have the DFS in-out numbers
// walk up the dominator tree
for (b = b->idom; b; b = b->idom) {
if (b == a)
return true;
}
return false;
}
sparse-0.6.4/flowgraph.h 0000664 0000000 0000000 00000001512 14115310122 0015145 0 ustar 00root root 0000000 0000000 #ifndef FLOWGRAPH_H
#define FLOWGRAPH_H
///
// Utilities for flowgraphs
// ------------------------
#include
struct entrypoint;
struct basic_block;
///
// Set the BB's reverse postorder links
// Each BB will also have its 'order number' set.
int cfg_postorder(struct entrypoint *ep);
///
// Build the dominance tree.
// Each BB will then have:
// - a link to its immediate dominator (::idom)
// - the list of BB it immediately dominates (::doms)
// - its level in the dominance tree (::dom_level)
void domtree_build(struct entrypoint *ep);
///
// Test the dominance between two basic blocks.
// @a: the basic block expected to dominate
// @b: the basic block expected to be dominated
// @return: ``true`` if @a dominates @b, ``false`` otherwise.
bool domtree_dominates(struct basic_block *a, struct basic_block *b);
#endif
sparse-0.6.4/gcc-attr-list.h 0000664 0000000 0000000 00000010577 14115310122 0015644 0 ustar 00root root 0000000 0000000 GCC_ATTR(BELOW100)
GCC_ATTR(OS_Task)
GCC_ATTR(OS_main)
GCC_ATTR(OS_task)
GCC_ATTR(abi_tag)
GCC_ATTR(absdata)
GCC_ATTR(address)
GCC_ATTR(alias)
GCC_ATTR(alloc_align)
GCC_ATTR(alloc_size)
GCC_ATTR(altivec)
GCC_ATTR(always_inline)
GCC_ATTR(artificial)
GCC_ATTR(assume_aligned)
GCC_ATTR(aux)
GCC_ATTR(bank_switch)
GCC_ATTR(based)
GCC_ATTR(below100)
GCC_ATTR(bnd_instrument)
GCC_ATTR(bnd_legacy)
GCC_ATTR(bnd_variable_size)
GCC_ATTR(break_handler)
GCC_ATTR(brk_interrupt)
GCC_ATTR(callee_pop_aggregate_return)
GCC_ATTR(cb)
GCC_ATTR(cdecl)
GCC_ATTR(cleanup)
GCC_ATTR(cmse_nonsecure_call)
GCC_ATTR(cmse_nonsecure_entry)
GCC_ATTR(cold)
GCC_ATTR(common)
GCC_ATTR(common_object)
GCC_ATTR(constructor)
GCC_ATTR(critical)
GCC_ATTR(deprecated)
GCC_ATTR(destructor)
GCC_ATTR(disinterrupt)
GCC_ATTR(dllexport)
GCC_ATTR(dllimport)
GCC_ATTR(eightbit_data)
GCC_ATTR(either)
GCC_ATTR(error)
GCC_ATTR(exception)
GCC_ATTR(exception_handler)
GCC_ATTR(far)
GCC_ATTR(fast_interrupt)
GCC_ATTR(fastcall)
GCC_ATTR(flatten)
GCC_ATTR(force_align_arg_pointer)
GCC_ATTR(format)
GCC_ATTR(format_arg)
GCC_ATTR(forwarder_section)
GCC_ATTR(function_return)
GCC_ATTR(function_return_mem)
GCC_ATTR(function_return_reg)
GCC_ATTR(function_vector)
GCC_ATTR(gcc_struct)
GCC_ATTR(gnu_inline)
GCC_ATTR(hidden)
GCC_ATTR(hot)
GCC_ATTR(hotpatch)
GCC_ATTR(ifunc)
GCC_ATTR(indirect_branch)
GCC_ATTR(indirect_branch_call)
GCC_ATTR(indirect_branch_jump)
GCC_ATTR(init_priority)
GCC_ATTR(interfacearm)
GCC_ATTR(internal)
GCC_ATTR(interrupt)
GCC_ATTR(interrupt_handler)
GCC_ATTR(interrupt_thread)
GCC_ATTR(io)
GCC_ATTR(io_low)
GCC_ATTR(isr)
GCC_ATTR(jli_always)
GCC_ATTR(jli_fixed)
GCC_ATTR(keep_interrupts_masked)
GCC_ATTR(kernel)
GCC_ATTR(kspisusp)
GCC_ATTR(l1_data)
GCC_ATTR(l1_data_A)
GCC_ATTR(l1_data_B)
GCC_ATTR(l1_text)
GCC_ATTR(l2)
GCC_ATTR(leaf)
GCC_ATTR(long_call)
GCC_ATTR(longcall)
GCC_ATTR(lower)
GCC_ATTR(malloc)
GCC_ATTR(may_alias)
GCC_ATTR(maybe_unused)
GCC_ATTR(medium_call)
GCC_ATTR(micromips)
GCC_ATTR(mips16)
GCC_ATTR(model)
GCC_ATTR(monitor)
GCC_ATTR(ms_abi)
GCC_ATTR(ms_hook_prologue)
GCC_ATTR(ms_struct)
GCC_ATTR(naked)
GCC_ATTR(near)
GCC_ATTR(nested)
GCC_ATTR(nested_ready)
GCC_ATTR(nesting)
GCC_ATTR(nmi)
GCC_ATTR(nmi_handler)
GCC_ATTR(no_address_safety_analysis)
GCC_ATTR(no_caller_saved_registers)
GCC_ATTR(no_gccisr)
GCC_ATTR(no_icf)
GCC_ATTR(no_instrument_function)
GCC_ATTR(no_profile_instrument_function)
GCC_ATTR(no_reorder)
GCC_ATTR(no_sanitize)
GCC_ATTR(no_sanitize_address)
GCC_ATTR(no_sanitize_thread)
GCC_ATTR(no_sanitize_undefined)
GCC_ATTR(no_split_stack)
GCC_ATTR(no_stack_limit)
GCC_ATTR(nocf_check)
GCC_ATTR(noclone)
GCC_ATTR(nocommon)
GCC_ATTR(nocompression)
GCC_ATTR(nodiscard)
GCC_ATTR(noinit)
GCC_ATTR(noinline)
GCC_ATTR(noipa)
GCC_ATTR(nomicromips)
GCC_ATTR(nomips16)
GCC_ATTR(nonnull)
GCC_ATTR(nonstring)
GCC_ATTR(noplt)
GCC_ATTR(nosave_low_regs)
GCC_ATTR(not_nested)
GCC_ATTR(nothrow)
GCC_ATTR(notshared)
GCC_ATTR(optimize)
GCC_ATTR(partial_save)
GCC_ATTR(patchable_function_entry)
GCC_ATTR(pcs)
GCC_ATTR(persistent)
GCC_ATTR(progmem)
GCC_ATTR(protected)
GCC_ATTR(reentrant)
GCC_ATTR(regparm)
GCC_ATTR(renesas)
GCC_ATTR(resbank)
GCC_ATTR(reset)
GCC_ATTR(returns_nonnull)
GCC_ATTR(returns_twice)
GCC_ATTR(s390_vector_bool)
GCC_ATTR(saddr)
GCC_ATTR(save_all)
GCC_ATTR(save_volatiles)
GCC_ATTR(saveall)
GCC_ATTR(scalar_storage_order)
GCC_ATTR(sda)
GCC_ATTR(section)
GCC_ATTR(secure_call)
GCC_ATTR(selectany)
GCC_ATTR(sentinel)
GCC_ATTR(shared)
GCC_ATTR(short_call)
GCC_ATTR(shortcall)
GCC_ATTR(signal)
GCC_ATTR(simd)
GCC_ATTR(sp_switch)
GCC_ATTR(spu_vector)
GCC_ATTR(sseregparm)
GCC_ATTR(stack_protect)
GCC_ATTR(stdcall)
GCC_ATTR(syscall_linkage)
GCC_ATTR(sysv_abi)
GCC_ATTR(target)
GCC_ATTR(target_clones)
GCC_ATTR(tda)
GCC_ATTR(thiscall)
GCC_ATTR(tiny)
GCC_ATTR(tiny_data)
GCC_ATTR(tls_model)
GCC_ATTR(transaction_callable)
GCC_ATTR(transaction_may_cancel_outer)
GCC_ATTR(transaction_pure)
GCC_ATTR(transaction_safe)
GCC_ATTR(transaction_safe_dynamic)
GCC_ATTR(transaction_unsafe)
GCC_ATTR(transaction_wrap)
GCC_ATTR(trap_exit)
GCC_ATTR(trapa_handler)
GCC_ATTR(uncached)
GCC_ATTR(unused)
GCC_ATTR(upper)
GCC_ATTR(use_debug_exception_return)
GCC_ATTR(use_shadow_register_set)
GCC_ATTR(used)
GCC_ATTR(vector)
GCC_ATTR(vector_size)
GCC_ATTR(version_id)
GCC_ATTR(visibility)
GCC_ATTR(vliw)
GCC_ATTR(volatile)
GCC_ATTR(wakeup)
GCC_ATTR(warm)
GCC_ATTR(warn_if_not_aligned)
GCC_ATTR(warn_unused)
GCC_ATTR(warn_unused_result)
GCC_ATTR(warning)
GCC_ATTR(weak)
GCC_ATTR(weakref)
GCC_ATTR(zda)
sparse-0.6.4/gdbhelpers 0000664 0000000 0000000 00000012106 14115310122 0015046 0 ustar 00root root 0000000 0000000
# Don't forget to rebuild sparse with uncommented debug options
# in the Makefile. Also, gcc 3 is known to screw up with the
# cpp macros in the debugging info.
# If a gdb_show_* function is running at a non-zero recursion
# level, only a short summary is shown, preventing further
# recursion. Also note that gdb has only one, global, scope
# for variables, so we need to be careful with recursions.
set $showing_token = 0
set $showing_ident = 0
set $showing_symbol = 0
set $ntabs = 0
define gdb_tabs
set $tmp = $ntabs
while ($tmp--)
printf "\t"
end
end
# Ptr list handling
define ptr_entry
set $ptr = $arg0
set $index = $arg1
set $entry = &($arg2)
set *($entry) = (void *) (~3UL & (unsigned long)$ptr->list[$index])
end
# Ptr list looping skeleton
define gdb_ptr_list_for_each
set $my_head = (struct ptr_list *) $arg0
set $my_list = $my_head
if ($my_head)
while (1)
set $my_nr = 0
while ($my_nr < $my_list->nr)
# Put your iterator code here
set $my_nr++
end
if (($my_list = $my_list->next) == $my_head)
loop_break
end
end
end
end
# Show symbols in a symbol_list. Non-recursive
define gdb_ptr_list_for_each_show_symbol
set $my_head = (struct ptr_list *) $arg0
set $my_list = $my_head
if ($my_head)
while (1)
set $my_nr = 0
while ($my_nr < ($my_list)->nr)
set $my_symbol = (struct symbol *) ((~3UL) & (unsigned long)($my_list)->list[$my_nr])
gdb_show_symbol($my_symbol)
set $my_nr++
end
set $my_list = ($my_list)->next
if ($my_list == $my_head)
loop_break
end
end
end
end
#define gdb_show_statement
# Recursive
define gdb_show_ctype
printf "modifiers: "
if ($arg0->modifiers & MOD_AUTO)
printf "MOD_AUTO "
end
if ($arg0->modifiers & MOD_REGISTER)
printf "MOD_REGISTER "
end
if ($arg0->modifiers & MOD_STATIC)
printf "MOD_STATIC "
end
if ($arg0->modifiers & MOD_EXTERN)
printf "MOD_EXTERN "
end
if ($arg0->modifiers & MOD_CONST)
printf "MOD_CONST "
end
if ($arg0->modifiers & MOD_VOLATILE)
printf "MOD_VOLATILE "
end
if ($arg0->modifiers & MOD_RESTRICT)
printf "MOD_RESTRICT "
end
if ($arg0->modifiers & MOD_ATOMIC)
printf "MOD_ATOMIC "
end
if ($arg0->modifiers & MOD_SIGNED)
printf "MOD_SIGNED "
end
if ($arg0->modifiers & MOD_UNSIGNED)
printf "MOD_UNSIGNED "
end
if ($arg0->modifiers & MOD_INLINE)
printf "MOD_INLINE "
end
if ($arg0->modifiers & MOD_ADDRESSABLE)
printf "MOD_ADDRESSABLE "
end
if ($arg0->modifiers & MOD_NOCAST)
printf "MOD_NOCAST "
end
if ($arg0->modifiers & MOD_NODEREF)
printf "MOD_NODEREF "
end
if ($arg0->modifiers & MOD_TOPLEVEL)
printf "MOD_TOPLEVEL "
end
if ($arg0->modifiers & MOD_ASSIGNED)
printf "MOD_ASSIGNED "
end
if ($arg0->modifiers & MOD_SAFE)
printf "MOD_SAFE "
end
if ($arg0->modifiers & MOD_USERTYPE)
printf "MOD_USERTYPE "
end
if ($arg0->modifiers & MOD_EXPLICITLY_SIGNED)
printf "MOD_EXPLICITLY_SIGNED"
end
if ($arg0->modifiers & MOD_BITWISE)
printf "MOD_BITWISE "
end
if (!$arg0->modifiers)
printf "0"
end
printf ", alignment = %d", $arg0->alignment
if ($arg0->as)
printf ", address_space = %d", $arg0->as
end
printf "\n"
set $ntabs++
if ($arg0->base_type)
gdb_tabs
printf "base_type = "
gdb_show_symbol($arg0->base_type)
end
set $ntabs--
end
define gdb_show_symbol
printf "(%x) type = ", $arg0
output $arg0->type
printf ", namespace = "
output $arg0->namespace
if ($arg0->ident)
printf ", ident = %s\n", show_ident($arg0->ident)
else
printf ", ident = NULL\n"
end
# print "zz"
gdb_tabs
printf "dump:\n"
call show_symbol($arg0)
set $ntabs++
if ($arg0->namespace == NS_SYMBOL)
gdb_tabs
printf "ctype = "
gdb_show_ctype(&($arg0->ctype))
end
set $ntabs--
end
# non-recursive
define gdb_show_symbols_next_id
set $sym = $arg0
printf "{\n"
set $ntabs++
while ($sym)
gdb_tabs
printf "symbol = "
gdb_show_symbol($sym)
set $sym = $sym->next_id
end
set $ntabs--
gdb_tabs
printf "}\n"
end
define gdb_show_ident
if ($arg0)
printf "(%p) '%s'\n", $arg0, show_ident($arg0)
else
printf "NULL\n"
end
if (! $showing_ident)
set $showing_ident = 1
set $ntabs++
set $ident = $arg0
if ($ident->symbols)
gdb_tabs
printf "symbols = "
gdb_show_symbols_next_id($ident->symbols)
end
set $ntabs--
set $showing_ident = 0
end
end
define gdb_show_token
printf "%p: '%s', type = ", $arg0, show_token($arg0)
output (enum token_type) ($arg0)->pos.type
printf "\n"
if (! $showing_token)
set $showing_token = 1
set $ntabs++
set $token = $arg0
if ($token->pos.type == TOKEN_IDENT)
gdb_tabs
printf "ident = "
gdb_show_ident $token.ident
end
if ($token->pos.type == TOKEN_MACRO_ARGUMENT)
gdb_tabs
printf "argnum = %d\n", $token->argnum
end
if ($token->pos.type == TOKEN_SPECIAL)
gdb_tabs
printf "special = \"%s\"\n", show_special($token.special)
end
set $ntabs--
set $showing_token = 0
end
end
# non-recursive
define gdb_show_tokens
set $t = $arg0
printf "{\n"
set $ntabs++
while ($t != &eof_token_entry)
gdb_tabs
printf "token = "
gdb_show_token($t)
set $t = ($t)->next
end
set $ntabs--
gdb_tabs
printf "}\n"
end
sparse-0.6.4/graph.c 0000664 0000000 0000000 00000013242 14115310122 0014253 0 ustar 00root root 0000000 0000000 /* 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"
" file=\"%s\";\n"
" fun=\"%s\";\n"
" ep=bb%p;\n",
ep, sname, fname, sname, fname, ep->entry->bb);
FOR_EACH_PTR(ep->bbs, bb) {
struct basic_block *child;
int ret = 0;
const char * s = ", ls=\"[";
/* Node for the bb */
printf(" bb%p [shape=ellipse,label=%d,line=%d,col=%d",
bb, bb->pos.line, bb->pos.line, bb->pos.pos);
/* List loads and stores */
FOR_EACH_PTR(bb->insns, insn) {
if (!insn->bb)
continue;
switch(insn->opcode) {
case OP_STORE:
if (insn->src->type == PSEUDO_SYM) {
printf("%s store(%s)", s, show_ident(insn->src->sym->ident));
s = ",";
}
break;
case OP_LOAD:
if (insn->src->type == PSEUDO_SYM) {
printf("%s load(%s)", s, show_ident(insn->src->sym->ident));
s = ",";
}
break;
case OP_RET:
ret = 1;
break;
}
} END_FOR_EACH_PTR(insn);
if (s[1] == 0)
printf("]\"");
if (ret)
printf(",op=ret");
printf("];\n");
/* Edges between bbs; lower weight for upward edges */
FOR_EACH_PTR(bb->children, child) {
printf(" bb%p -> bb%p [op=br, %s];\n", bb, child,
(bb->pos.line > child->pos.line) ? "weight=5" : "weight=10");
} END_FOR_EACH_PTR(child);
} END_FOR_EACH_PTR(bb);
printf("}\n");
}
/* Insert edges for intra- or inter-file calls, depending on the value
* of internal. Bold edges are used for calls with destinations;
* dashed for calls to external functions */
static void graph_calls(struct entrypoint *ep, int internal)
{
struct basic_block *bb;
struct instruction *insn;
show_ident(ep->name->ident);
stream_name(ep->entry->bb->pos.stream);
FOR_EACH_PTR(ep->bbs, bb) {
if (!bb)
continue;
if (!bb->parents && !bb->children && !bb->insns && verbose < 2)
continue;
FOR_EACH_PTR(bb->insns, insn) {
if (!insn->bb)
continue;
if (insn->opcode == OP_CALL &&
internal == !(insn->func->sym->ctype.modifiers & MOD_EXTERN)) {
/* Find the symbol for the callee's definition */
struct symbol * sym;
if (insn->func->type == PSEUDO_SYM) {
for (sym = insn->func->sym->ident->symbols;
sym; sym = sym->next_id) {
if (sym->namespace & NS_SYMBOL && sym->ep)
break;
}
if (sym)
printf("bb%p -> bb%p"
"[label=%d,line=%d,col=%d,op=call,style=bold,weight=30];\n",
bb, sym->ep->entry->bb,
insn->pos.line, insn->pos.line, insn->pos.pos);
else
printf("bb%p -> \"%s\" "
"[label=%d,line=%d,col=%d,op=extern,style=dashed];\n",
bb, show_pseudo(insn->func),
insn->pos.line, insn->pos.line, insn->pos.pos);
}
}
} END_FOR_EACH_PTR(insn);
} END_FOR_EACH_PTR(bb);
}
int main(int argc, char **argv)
{
struct string_list *filelist = NULL;
char *file;
struct symbol *sym;
struct symbol_list *fsyms, *all_syms=NULL;
printf("digraph call_graph {\n");
fsyms = sparse_initialize(argc, argv, &filelist);
concat_symbol_list(fsyms, &all_syms);
/* Linearize all symbols, graph internal basic block
* structures and intra-file calls */
FOR_EACH_PTR(filelist, file) {
fsyms = sparse(file);
concat_symbol_list(fsyms, &all_syms);
FOR_EACH_PTR(fsyms, sym) {
expand_symbol(sym);
linearize_symbol(sym);
} END_FOR_EACH_PTR(sym);
FOR_EACH_PTR(fsyms, sym) {
if (sym->ep) {
graph_ep(sym->ep);
graph_calls(sym->ep, 1);
}
} END_FOR_EACH_PTR(sym);
} END_FOR_EACH_PTR(file);
/* Graph inter-file calls */
FOR_EACH_PTR(all_syms, sym) {
if (sym->ep)
graph_calls(sym->ep, 0);
} END_FOR_EACH_PTR(sym);
printf("}\n");
return 0;
}
sparse-0.6.4/gvpr/ 0000775 0000000 0000000 00000000000 14115310122 0013762 5 ustar 00root root 0000000 0000000 sparse-0.6.4/gvpr/return-paths 0000775 0000000 0000000 00000004137 14115310122 0016351 0 ustar 00root root 0000000 0000000 #!/usr/bin/gvpr -f
// Split call sites into call site and return site nodes and add
// return edges
//
// Run with graph ... | return-paths
BEGIN {
// Find the immediate parent subgraph of this object
graph_t find_owner(obj_t o, graph_t g)
{
graph_t g1;
for (g1 = fstsubg(g); g1; g1 = nxtsubg(g1))
if(isIn(g1,o)) return g1;
return NULL;
}
}
BEG_G {
node_t calls[]; // Crude hash table for tracking who calls what
graph_t g,g2;
edge_t e,e2;
string idx;
node_t n, n2;
int i;
$tvtype = TV_en;
}
// Each call edge which hasn't already been seen
E [op == "call" && tail.split != 1] {
int offset=0;
// Clear the label of this call
label = "";
g = find_owner(tail, $G);
// Consider each outgoing call. Split the node accordingly
n = tail;
for (e = fstout(tail); e; e = nxtout(e)) {
if (e.op == "call") {
// Split node
n2 = node(g, sprintf("%s%s%d", tail.name, "x", offset++));
copyA(tail, n2);
n2.line = e.line;
n2.label = e.line;
n2.col = e.col;
n2.split = 1;
n2.op = "target";
// Record this call
g2 = find_owner(e.head, $G);
i = 0;
while(calls[sprintf("%s%d", g2.name, ++i)]) {
}
calls[sprintf("%s%d", g2.name, i)] = n2;
// Connect original to split
e2 = edge(n, n2, "call");
e2.style = "dotted";
e2.weight = 50;
// Replace this outedge
if (n != tail) {
e2 = edge(n, e.head, "transformed-call");
copyA(e,e2);
e2.label = "";
delete($G,e);
}
// Record where we were
n = n2;
}
}
// Consider the outgoing control flow: move down to the bottom of
// the call sequence nodes
for (e = fstout(tail); e; e = nxtout(e)) {
if (e.op == "br") {
// Replace this outedge
e2 = edge(n,e.head,"transformed");
copyA(e,e2);
delete($G,e);
}
}
}
// Each return node: add edges back to the caller
N [op == "ret"] {
for (g = fstsubg($G); g; g = nxtsubg(g)) {
if(isIn(g,$)) {
i = 0;
while(calls[sprintf("%s%d", g.name, ++i)]) {
e = edge($, calls[sprintf("%s%d", g.name, i)], "return");
e.style = "dotted";
e.op = "ret";
e.line = e.tail.line;
e.weight = 5;
}
}
}
}
END_G {
write($);
}
sparse-0.6.4/gvpr/subg-fwd 0000775 0000000 0000000 00000003147 14115310122 0015433 0 ustar 00root root 0000000 0000000 #!/usr/bin/gvpr -f
// Compute the forward partition of the chosen function
//
// Run with graph ... | return-paths | subg-fwd -a functionname
// or graph ... | subg-fwd
BEGIN {
// Find the immediate parent subgraph of this object
graph_t find_owner(obj_t o, graph_t g)
{
graph_t g1;
for (g1 = fstsubg(g); g1; g1 = nxtsubg(g1))
if(isIn(g1,o)) return g1;
return NULL;
}
}
BEG_G {
graph_t sg = subg ($, sprintf("incoming-%s", ARGV[0]));
graph_t returns = graph("return-edges", ""); // Temporary graph to hold return edges
graph_t target, g, g2;
node_t n;
edge_t e;
int i;
$tvtype = TV_fwd;
// find the ep corresponding to ARG[0]
for (g = fstsubg($G); g; g = nxtsubg(g)) {
if(g.fun == ARGV[0]) {
n = node($,g.ep);
$tvroot = n;
n.style = "filled";
target = g;
break;
}
}
if(!target) {
printf(2, "Function %s not found\n", ARGV[0]);
exit(1);
}
}
// Preserve external functions
E [op == "extern"] {
subnode (sg, head);
}
// Move unused return edges into a separate graph so they don't get followed
N [op == "ret"] {
for (e = fstout($); e; e = nxtout(e))
if (e.op == "ret" && !isIn(sg, e.head)) {
clone(returns, e);
delete($G, e);
}
}
// Recover elided return edge for this target node
N [op == "target" && indegree == 1] {
n = copy(returns, $);
e = fstin(n); // each target node can only have one return edge
e = edge(copy(sg, e.tail), $, "recovered"); // clone should work here, but doesn't
copyA(fstin(n), e);
}
// Copy relevant nodes
N {
$tvroot = NULL;
g = find_owner($, $G);
if(g && g != sg)
subnode (copy(sg, g), $);
}
END_G {
induce(sg);
write(sg);
}
sparse-0.6.4/gvpr/subg-rev 0000775 0000000 0000000 00000003627 14115310122 0015452 0 ustar 00root root 0000000 0000000 #!/usr/bin/gvpr -f
// Compute the reverse partition of the chosen function
//
// Run with graph ... | return-paths | subg-rev -a functionname
BEGIN {
// Find the immediate parent subgraph of this object
graph_t find_owner(obj_t o, graph_t g)
{
graph_t g1;
for (g1 = fstsubg(g); g1; g1 = nxtsubg(g1))
if(isIn(g1,o)) return g1;
return NULL;
}
}
BEG_G {
graph_t calls[]; // Crude hash table for tracking who calls what
graph_t sg = subg ($, "reachable");
graph_t target, g, g2;
edge_t e;
int i;
$tvtype = TV_rev;
// find the ep corresponding to ARG[0]
for (g = fstsubg($G); g; g = nxtsubg(g)) {
if(g.fun == ARGV[0]) {
node_t n;
n = node($,g.ep);
$tvroot = n;
n.style = "filled";
target = g;
break;
}
}
if(!target) {
printf(2, "Function %s not found\n", ARGV[0]);
exit(1);
}
// Add the incoming call edges to the allowed call list
i = 0;
for(e = fstin(n); e; e = nxtin(e)) {
if (e.op = "call") {
g2 = find_owner(e.tail, $G);
calls[sprintf("%s%d", g2.name, ++i)] = g;
}
}
}
E [op == "ret"] {
// This is a return edge. Allow the corresponding call
g = find_owner(head, $G);
i = 0;
while(calls[sprintf("%s%d", g.name, ++i)]) {
}
calls[sprintf("%s%d", g.name, i)] = find_owner(tail, $G);
g2 = find_owner(tail, $G);
}
N [split == 1] {
// Ignore returns back to the target function
for (e = fstin($); e; e = nxtin(e))
if (e.op == "ret" && isIn(target,e.tail))
delete($G,e);
}
N {
$tvroot = NULL;
for (e = fstin($); e; e = nxtin(e)) {
if (e.op == "call") {
int found = 0;
g = find_owner(e.tail, $G);
i = 0;
while(calls[sprintf("%s%d", g.name, ++i)]) {
if (isIn(calls[sprintf("%s%d", g.name, i)],e.head))
found = 1;
}
g2 = find_owner(e.head, $G);
if (!found) delete($G, e);
}
}
for (g = fstsubg($G); g; g = nxtsubg(g)) {
if(isIn(g,$) && g != sg) {
subnode (copy(sg, g), $);
}
}
}
END_G {
induce(sg);
write(sg);
}
sparse-0.6.4/ident-list.h 0000664 0000000 0000000 00000004363 14115310122 0015237 0 ustar 00root root 0000000 0000000
#define IDENT(n) __IDENT(n## _ident, #n, 0)
#define IDENT_RESERVED(n) __IDENT(n## _ident, #n, 1)
/* Basic C reserved words.. */
IDENT_RESERVED(sizeof);
IDENT_RESERVED(if);
IDENT_RESERVED(else);
IDENT_RESERVED(return);
IDENT_RESERVED(switch);
IDENT_RESERVED(case);
IDENT_RESERVED(default);
IDENT_RESERVED(break);
IDENT_RESERVED(continue);
IDENT_RESERVED(for);
IDENT_RESERVED(while);
IDENT_RESERVED(do);
IDENT_RESERVED(goto);
/* C typenames. They get marked as reserved when initialized */
IDENT(struct);
IDENT(union);
IDENT(enum);
IDENT(__attribute); IDENT(__attribute__);
IDENT(volatile); IDENT(__volatile); IDENT(__volatile__);
IDENT(double);
/* C storage classes. They get marked as reserved when initialized */
IDENT(static);
/* C99 keywords */
IDENT(restrict); IDENT(__restrict); IDENT(__restrict__);
IDENT(_Bool);
IDENT_RESERVED(_Complex);
IDENT_RESERVED(_Imaginary);
/* C11 keywords */
IDENT(_Alignas);
IDENT_RESERVED(_Alignof);
IDENT(_Atomic);
IDENT_RESERVED(_Generic);
IDENT(_Noreturn);
IDENT_RESERVED(_Static_assert);
IDENT(_Thread_local);
/* Special case for L'\t' */
IDENT(L);
/* Extended gcc identifiers */
IDENT(asm); IDENT_RESERVED(__asm); IDENT_RESERVED(__asm__);
IDENT(alignof); IDENT_RESERVED(__alignof); IDENT_RESERVED(__alignof__);
IDENT_RESERVED(__sizeof_ptr__);
IDENT_RESERVED(__builtin_types_compatible_p);
IDENT_RESERVED(__builtin_offsetof);
IDENT_RESERVED(__label__);
/* Preprocessor idents. Direct use of __IDENT avoids mentioning the keyword
* itself by name, preventing these tokens from expanding when compiling
* sparse. */
IDENT(defined);
IDENT(once);
IDENT(c_alignas);
IDENT(c_alignof);
IDENT(c_generic_selections);
IDENT(c_static_assert);
__IDENT(pragma_ident, "__pragma__", 0);
__IDENT(__VA_ARGS___ident, "__VA_ARGS__", 0);
__IDENT(__func___ident, "__func__", 0);
__IDENT(__FUNCTION___ident, "__FUNCTION__", 0);
__IDENT(__PRETTY_FUNCTION___ident, "__PRETTY_FUNCTION__", 0);
/* Sparse commands */
IDENT_RESERVED(__context__);
IDENT_RESERVED(__range__);
/* Magic function names we recognize */
IDENT(memset); IDENT(memcpy);
IDENT(copy_to_user); IDENT(copy_from_user);
IDENT(main);
/* used by the symbolic checker */
IDENT(__assume);
IDENT(__assert);
IDENT(__assert_eq);
IDENT(__assert_const);
#undef __IDENT
#undef IDENT
#undef IDENT_RESERVED
sparse-0.6.4/inline.c 0000664 0000000 0000000 00000036760 14115310122 0014442 0 ustar 00root root 0000000 0000000 /*
* Sparse - a semantic source parser.
*
* Copyright (C) 2003 Transmeta Corp.
* 2003-2004 Linus Torvalds
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include
#include
#include "lib.h"
#include "allocate.h"
#include "token.h"
#include "parse.h"
#include "symbol.h"
#include "expression.h"
#include "evaluate.h"
static void copy_statement(struct statement *src, struct statement *dst);
static struct expression * dup_expression(struct expression *expr)
{
struct expression *dup = alloc_expression(expr->pos, expr->type);
*dup = *expr;
return dup;
}
static struct statement * dup_statement(struct statement *stmt)
{
struct statement *dup = alloc_statement(stmt->pos, stmt->type);
*dup = *stmt;
return dup;
}
static struct symbol *copy_symbol(struct position pos, struct symbol *sym)
{
if (!sym)
return sym;
if (sym->ctype.modifiers & (MOD_STATIC | MOD_EXTERN | MOD_TOPLEVEL | MOD_INLINE))
return sym;
if (!sym->replace) {
warning(pos, "unreplaced symbol '%s'", show_ident(sym->ident));
return sym;
}
return sym->replace;
}
static struct symbol_list *copy_symbol_list(struct symbol_list *src)
{
struct symbol_list *dst = NULL;
struct symbol *sym;
FOR_EACH_PTR(src, sym) {
struct symbol *newsym = copy_symbol(sym->pos, sym);
add_symbol(&dst, newsym);
} END_FOR_EACH_PTR(sym);
return dst;
}
static struct expression * copy_expression(struct expression *expr)
{
if (!expr)
return NULL;
switch (expr->type) {
/*
* EXPR_SYMBOL is the interesting case, we may need to replace the
* symbol to the new copy.
*/
case EXPR_SYMBOL: {
struct symbol *sym = copy_symbol(expr->pos, expr->symbol);
if (sym == expr->symbol)
break;
expr = dup_expression(expr);
expr->symbol = sym;
break;
}
/* Atomics, never change, just return the expression directly */
case EXPR_VALUE:
case EXPR_STRING:
case EXPR_FVALUE:
case EXPR_TYPE:
break;
/* Unops: check if the subexpression is unique */
case EXPR_PREOP:
case EXPR_POSTOP: {
struct expression *unop = copy_expression(expr->unop);
if (expr->unop == unop)
break;
expr = dup_expression(expr);
expr->unop = unop;
break;
}
case EXPR_SLICE: {
struct expression *base = copy_expression(expr->base);
expr = dup_expression(expr);
expr->base = base;
break;
}
/* Binops: copy left/right expressions */
case EXPR_BINOP:
case EXPR_COMMA:
case EXPR_COMPARE:
case EXPR_LOGICAL: {
struct expression *left = copy_expression(expr->left);
struct expression *right = copy_expression(expr->right);
if (left == expr->left && right == expr->right)
break;
expr = dup_expression(expr);
expr->left = left;
expr->right = right;
break;
}
case EXPR_ASSIGNMENT: {
struct expression *left = copy_expression(expr->left);
struct expression *right = copy_expression(expr->right);
if (expr->op == '=' && left == expr->left && right == expr->right)
break;
expr = dup_expression(expr);
expr->left = left;
expr->right = right;
break;
}
/* Dereference */
case EXPR_DEREF: {
struct expression *deref = copy_expression(expr->deref);
expr = dup_expression(expr);
expr->deref = deref;
break;
}
/* Cast/sizeof/__alignof__ */
case EXPR_CAST:
if (expr->cast_expression->type == EXPR_INITIALIZER) {
struct expression *cast = expr->cast_expression;
struct symbol *sym = expr->cast_type;
expr = dup_expression(expr);
expr->cast_expression = copy_expression(cast);
expr->cast_type = alloc_symbol(sym->pos, sym->type);
*expr->cast_type = *sym;
break;
}
case EXPR_FORCE_CAST:
case EXPR_IMPLIED_CAST:
case EXPR_SIZEOF:
case EXPR_PTRSIZEOF:
case EXPR_ALIGNOF: {
struct expression *cast = copy_expression(expr->cast_expression);
if (cast == expr->cast_expression)
break;
expr = dup_expression(expr);
expr->cast_expression = cast;
break;
}
/* Conditional expression */
case EXPR_SELECT:
case EXPR_CONDITIONAL: {
struct expression *cond = copy_expression(expr->conditional);
struct expression *valt = copy_expression(expr->cond_true);
struct expression *valf = copy_expression(expr->cond_false);
if (cond == expr->conditional && valt == expr->cond_true && valf == expr->cond_false)
break;
expr = dup_expression(expr);
expr->conditional = cond;
expr->cond_true = valt;
expr->cond_false = valf;
break;
}
/* Statement expression */
case EXPR_STATEMENT: {
struct statement *stmt = alloc_statement(expr->pos, STMT_COMPOUND);
copy_statement(expr->statement, stmt);
expr = dup_expression(expr);
expr->statement = stmt;
break;
}
/* Call expression */
case EXPR_CALL: {
struct expression *fn = copy_expression(expr->fn);
struct expression_list *list = expr->args;
struct expression *arg;
expr = dup_expression(expr);
expr->fn = fn;
expr->args = NULL;
FOR_EACH_PTR(list, arg) {
add_expression(&expr->args, copy_expression(arg));
} END_FOR_EACH_PTR(arg);
break;
}
/* Initializer list statement */
case EXPR_INITIALIZER: {
struct expression_list *list = expr->expr_list;
struct expression *entry;
expr = dup_expression(expr);
expr->expr_list = NULL;
FOR_EACH_PTR(list, entry) {
add_expression(&expr->expr_list, copy_expression(entry));
} END_FOR_EACH_PTR(entry);
break;
}
/* Label in inline function - hmm. */
case EXPR_LABEL: {
struct symbol *label_symbol = copy_symbol(expr->pos, expr->label_symbol);
expr = dup_expression(expr);
expr->label_symbol = label_symbol;
break;
}
case EXPR_INDEX: {
struct expression *sub_expr = copy_expression(expr->idx_expression);
expr = dup_expression(expr);
expr->idx_expression = sub_expr;
break;
}
case EXPR_IDENTIFIER: {
struct expression *sub_expr = copy_expression(expr->ident_expression);
expr = dup_expression(expr);
expr->ident_expression = sub_expr;
break;
}
/* Position in initializer.. */
case EXPR_POS: {
struct expression *val = copy_expression(expr->init_expr);
expr = dup_expression(expr);
expr->init_expr = val;
break;
}
case EXPR_OFFSETOF: {
struct expression *val = copy_expression(expr->down);
if (expr->op == '.') {
if (expr->down != val) {
expr = dup_expression(expr);
expr->down = val;
}
} else {
struct expression *idx = copy_expression(expr->index);
if (expr->down != val || expr->index != idx) {
expr = dup_expression(expr);
expr->down = val;
expr->index = idx;
}
}
break;
}
case EXPR_GENERIC:
expr = dup_expression(expr);
expr->control = copy_expression(expr->control);
if (!evaluate_expression(expr))
return NULL;
expr = copy_expression(expr);
break;
default:
warning(expr->pos, "trying to copy expression type %d", expr->type);
}
return expr;
}
static struct asm_operand_list *copy_asm_operands(struct asm_operand_list *in)
{
struct asm_operand_list *out = NULL;
struct asm_operand *old;
FOR_EACH_PTR(in, old) {
struct asm_operand *new = __alloc_asm_operand(0);
new->name = old->name;
new->constraint = copy_expression(old->constraint);
new->expr = copy_expression(old->expr);
add_ptr_list(&out, new);
} END_FOR_EACH_PTR(old);
return out;
}
static void set_replace(struct symbol *old, struct symbol *new)
{
new->replace = old;
old->replace = new;
}
static void unset_replace(struct symbol *sym)
{
struct symbol *r = sym->replace;
if (!r) {
warning(sym->pos, "symbol '%s' not replaced?", show_ident(sym->ident));
return;
}
r->replace = NULL;
sym->replace = NULL;
}
static void unset_replace_list(struct symbol_list *list)
{
struct symbol *sym;
FOR_EACH_PTR(list, sym) {
unset_replace(sym);
} END_FOR_EACH_PTR(sym);
}
static struct statement *copy_one_statement(struct statement *stmt)
{
if (!stmt)
return NULL;
switch(stmt->type) {
case STMT_NONE:
break;
case STMT_DECLARATION: {
struct symbol *sym;
struct statement *newstmt = dup_statement(stmt);
newstmt->declaration = NULL;
FOR_EACH_PTR(stmt->declaration, sym) {
struct symbol *newsym = copy_symbol(stmt->pos, sym);
if (newsym != sym)
newsym->initializer = copy_expression(sym->initializer);
add_symbol(&newstmt->declaration, newsym);
} END_FOR_EACH_PTR(sym);
stmt = newstmt;
break;
}
case STMT_CONTEXT:
case STMT_EXPRESSION: {
struct expression *expr = copy_expression(stmt->expression);
if (expr == stmt->expression)
break;
stmt = dup_statement(stmt);
stmt->expression = expr;
break;
}
case STMT_RANGE: {
struct expression *expr = copy_expression(stmt->range_expression);
if (expr == stmt->expression)
break;
stmt = dup_statement(stmt);
stmt->range_expression = expr;
break;
}
case STMT_COMPOUND: {
struct statement *new = alloc_statement(stmt->pos, STMT_COMPOUND);
copy_statement(stmt, new);
stmt = new;
break;
}
case STMT_IF: {
struct expression *cond = stmt->if_conditional;
struct statement *valt = stmt->if_true;
struct statement *valf = stmt->if_false;
cond = copy_expression(cond);
valt = copy_one_statement(valt);
valf = copy_one_statement(valf);
if (stmt->if_conditional == cond &&
stmt->if_true == valt &&
stmt->if_false == valf)
break;
stmt = dup_statement(stmt);
stmt->if_conditional = cond;
stmt->if_true = valt;
stmt->if_false = valf;
break;
}
case STMT_RETURN: {
struct expression *retval = copy_expression(stmt->ret_value);
struct symbol *sym = copy_symbol(stmt->pos, stmt->ret_target);
stmt = dup_statement(stmt);
stmt->ret_value = retval;
stmt->ret_target = sym;
break;
}
case STMT_CASE: {
stmt = dup_statement(stmt);
stmt->case_label = copy_symbol(stmt->pos, stmt->case_label);
stmt->case_label->stmt = stmt;
stmt->case_expression = copy_expression(stmt->case_expression);
stmt->case_to = copy_expression(stmt->case_to);
stmt->case_statement = copy_one_statement(stmt->case_statement);
break;
}
case STMT_SWITCH: {
struct symbol *switch_break = copy_symbol(stmt->pos, stmt->switch_break);
struct symbol *switch_case = copy_symbol(stmt->pos, stmt->switch_case);
struct expression *expr = copy_expression(stmt->switch_expression);
struct statement *switch_stmt = copy_one_statement(stmt->switch_statement);
stmt = dup_statement(stmt);
switch_case->symbol_list = copy_symbol_list(switch_case->symbol_list);
stmt->switch_break = switch_break;
stmt->switch_case = switch_case;
stmt->switch_expression = expr;
stmt->switch_statement = switch_stmt;
break;
}
case STMT_ITERATOR: {
stmt = dup_statement(stmt);
stmt->iterator_break = copy_symbol(stmt->pos, stmt->iterator_break);
stmt->iterator_continue = copy_symbol(stmt->pos, stmt->iterator_continue);
stmt->iterator_syms = copy_symbol_list(stmt->iterator_syms);
stmt->iterator_pre_statement = copy_one_statement(stmt->iterator_pre_statement);
stmt->iterator_pre_condition = copy_expression(stmt->iterator_pre_condition);
stmt->iterator_statement = copy_one_statement(stmt->iterator_statement);
stmt->iterator_post_statement = copy_one_statement(stmt->iterator_post_statement);
stmt->iterator_post_condition = copy_expression(stmt->iterator_post_condition);
break;
}
case STMT_LABEL: {
stmt = dup_statement(stmt);
stmt->label_identifier = copy_symbol(stmt->pos, stmt->label_identifier);
stmt->label_statement = copy_one_statement(stmt->label_statement);
break;
}
case STMT_GOTO: {
stmt = dup_statement(stmt);
stmt->goto_label = copy_symbol(stmt->pos, stmt->goto_label);
stmt->goto_expression = copy_expression(stmt->goto_expression);
stmt->target_list = copy_symbol_list(stmt->target_list);
break;
}
case STMT_ASM: {
stmt = dup_statement(stmt);
stmt->asm_inputs = copy_asm_operands(stmt->asm_inputs);
stmt->asm_outputs = copy_asm_operands(stmt->asm_outputs);
/* no need to dup "clobbers", since they are all constant strings */
break;
}
default:
warning(stmt->pos, "trying to copy statement type %d", stmt->type);
break;
}
return stmt;
}
/*
* Copy a statement tree from 'src' to 'dst', where both
* source and destination are of type STMT_COMPOUND.
*
* We do this for the tree-level inliner.
*
* This doesn't do the symbol replacement right: it's not
* re-entrant.
*/
static void copy_statement(struct statement *src, struct statement *dst)
{
struct statement *stmt;
FOR_EACH_PTR(src->stmts, stmt) {
add_statement(&dst->stmts, copy_one_statement(stmt));
} END_FOR_EACH_PTR(stmt);
dst->args = copy_one_statement(src->args);
dst->ret = copy_symbol(src->pos, src->ret);
dst->inline_fn = src->inline_fn;
}
static struct symbol *create_copy_symbol(struct symbol *orig)
{
struct symbol *sym = orig;
if (orig) {
sym = alloc_symbol(orig->pos, orig->type);
*sym = *orig;
sym->bb_target = NULL;
sym->pseudo = NULL;
set_replace(orig, sym);
orig = sym;
}
return orig;
}
static struct symbol_list *create_symbol_list(struct symbol_list *src)
{
struct symbol_list *dst = NULL;
struct symbol *sym;
FOR_EACH_PTR(src, sym) {
struct symbol *newsym = create_copy_symbol(sym);
add_symbol(&dst, newsym);
} END_FOR_EACH_PTR(sym);
return dst;
}
int inline_function(struct expression *expr, struct symbol *sym)
{
struct symbol_list * fn_symbol_list;
struct symbol *fn = sym->ctype.base_type;
struct expression_list *arg_list = expr->args;
struct statement *stmt = alloc_statement(expr->pos, STMT_COMPOUND);
struct symbol_list *name_list, *arg_decl;
struct symbol *name;
struct expression *arg;
if (!fn->inline_stmt) {
sparse_error(fn->pos, "marked inline, but without a definition");
return 0;
}
if (fn->expanding)
return 0;
name_list = fn->arguments;
expr->type = EXPR_STATEMENT;
expr->statement = stmt;
expr->ctype = fn->ctype.base_type;
fn_symbol_list = create_symbol_list(sym->inline_symbol_list);
arg_decl = NULL;
PREPARE_PTR_LIST(name_list, name);
FOR_EACH_PTR(arg_list, arg) {
struct symbol *a = alloc_symbol(arg->pos, SYM_NODE);
a->ctype.base_type = arg->ctype;
if (name) {
*a = *name;
set_replace(name, a);
add_symbol(&fn_symbol_list, a);
}
a->initializer = arg;
add_symbol(&arg_decl, a);
NEXT_PTR_LIST(name);
} END_FOR_EACH_PTR(arg);
FINISH_PTR_LIST(name);
copy_statement(fn->inline_stmt, stmt);
if (arg_decl) {
struct statement *decl = alloc_statement(expr->pos, STMT_DECLARATION);
decl->declaration = arg_decl;
stmt->args = decl;
}
stmt->inline_fn = sym;
unset_replace_list(fn_symbol_list);
return 1;
}
void uninline(struct symbol *sym)
{
struct symbol *fn = sym->ctype.base_type;
struct symbol_list *arg_list = fn->arguments;
struct symbol *p;
sym->symbol_list = create_symbol_list(sym->inline_symbol_list);
FOR_EACH_PTR(arg_list, p) {
p->replace = p;
} END_FOR_EACH_PTR(p);
fn->stmt = alloc_statement(fn->pos, STMT_COMPOUND);
copy_statement(fn->inline_stmt, fn->stmt);
unset_replace_list(sym->symbol_list);
unset_replace_list(arg_list);
}
sparse-0.6.4/ir.c 0000664 0000000 0000000 00000007764 14115310122 0013600 0 ustar 00root root 0000000 0000000 // SPDX-License-Identifier: MIT
#include "ir.h"
#include "linearize.h"
#include
#include
static int nbr_phi_operands(struct instruction *insn)
{
pseudo_t p;
int nbr = 0;
if (!insn->phi_list)
return 0;
FOR_EACH_PTR(insn->phi_list, p) {
if (p == VOID)
continue;
nbr++;
} END_FOR_EACH_PTR(p);
return nbr;
}
static int check_phi_node(struct instruction *insn)
{
struct basic_block *par;
pseudo_t phi;
int err = 0;
if (!has_users(insn->target))
return err;
if (bb_list_size(insn->bb->parents) != nbr_phi_operands(insn)) {
sparse_error(insn->pos, "bad number of phi operands in:\n\t%s",
show_instruction(insn));
info(insn->pos, "parents: %d", bb_list_size(insn->bb->parents));
info(insn->pos, "phisrcs: %d", nbr_phi_operands(insn));
return 1;
}
PREPARE_PTR_LIST(insn->bb->parents, par);
FOR_EACH_PTR(insn->phi_list, phi) {
struct instruction *src;
if (phi == VOID)
continue;
assert(phi->type == PSEUDO_PHI);
src = phi->def;
if (src->bb != par) {
sparse_error(src->pos, "wrong BB for %s:", show_instruction(src));
info(src->pos, "expected: %s", show_label(par));
info(src->pos, " got: %s", show_label(src->bb));
err++;
}
NEXT_PTR_LIST(par);
} END_FOR_EACH_PTR(phi);
FINISH_PTR_LIST(par);
return err;
}
static int check_user(struct instruction *insn, pseudo_t pseudo)
{
struct instruction *def;
if (!pseudo) {
show_entry(insn->bb->ep);
sparse_error(insn->pos, "null pseudo in %s", show_instruction(insn));
return 1;
}
switch (pseudo->type) {
case PSEUDO_PHI:
case PSEUDO_REG:
def = pseudo->def;
if (def && def->bb)
break;
show_entry(insn->bb->ep);
sparse_error(insn->pos, "wrong usage for %s in %s", show_pseudo(pseudo),
show_instruction(insn));
return 1;
default:
break;
}
return 0;
}
static int check_branch(struct entrypoint *ep, struct instruction *insn, struct basic_block *bb)
{
if (bb->ep && lookup_bb(ep->bbs, bb))
return 0;
sparse_error(insn->pos, "branch to dead BB: %s", show_instruction(insn));
return 1;
}
static int check_switch(struct entrypoint *ep, struct instruction *insn)
{
struct multijmp *jmp;
int err = 0;
FOR_EACH_PTR(insn->multijmp_list, jmp) {
err = check_branch(ep, insn, jmp->target);
if (err)
return err;
} END_FOR_EACH_PTR(jmp);
return err;
}
static int check_return(struct instruction *insn)
{
struct symbol *ctype = insn->type;
if (ctype && ctype->bit_size > 0 && insn->src == VOID) {
sparse_error(insn->pos, "return without value");
return 1;
}
return 0;
}
static int validate_insn(struct entrypoint *ep, struct instruction *insn)
{
int err = 0;
switch (insn->opcode) {
case OP_SEL:
case OP_RANGE:
err += check_user(insn, insn->src3);
/* fall through */
case OP_BINARY ... OP_BINCMP_END:
err += check_user(insn, insn->src2);
/* fall through */
case OP_UNOP ... OP_UNOP_END:
case OP_SLICE:
case OP_SYMADDR:
case OP_PHISOURCE:
err += check_user(insn, insn->src1);
break;
case OP_CBR:
err += check_branch(ep, insn, insn->bb_true);
err += check_branch(ep, insn, insn->bb_false);
/* fall through */
case OP_COMPUTEDGOTO:
err += check_user(insn, insn->cond);
break;
case OP_PHI:
err += check_phi_node(insn);
break;
case OP_CALL:
// FIXME: ignore for now
break;
case OP_STORE:
err += check_user(insn, insn->target);
/* fall through */
case OP_LOAD:
err += check_user(insn, insn->src);
break;
case OP_RET:
err += check_return(insn);
break;
case OP_BR:
err += check_branch(ep, insn, insn->bb_true);
break;
case OP_SWITCH:
err += check_switch(ep, insn);
break;
case OP_ENTRY:
case OP_LABEL:
case OP_SETVAL:
default:
break;
}
return err;
}
int ir_validate(struct entrypoint *ep)
{
struct basic_block *bb;
int err = 0;
if (!dbg_ir || has_error)
return 0;
FOR_EACH_PTR(ep->bbs, bb) {
struct instruction *insn;
FOR_EACH_PTR(bb->insns, insn) {
if (!insn->bb)
continue;
err += validate_insn(ep, insn);
} END_FOR_EACH_PTR(insn);
} END_FOR_EACH_PTR(bb);
if (err)
abort();
return err;
}
sparse-0.6.4/ir.h 0000664 0000000 0000000 00000000145 14115310122 0013567 0 ustar 00root root 0000000 0000000 #ifndef _IR_H
#define _IR_H
#include "linearize.h"
int ir_validate(struct entrypoint *ep);
#endif
sparse-0.6.4/lib.c 0000664 0000000 0000000 00000026711 14115310122 0013725 0 ustar 00root root 0000000 0000000 /*
* 'sparse' library helper routines.
*
* Copyright (C) 2003 Transmeta Corp.
* 2003-2004 Linus Torvalds
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "lib.h"
#include "allocate.h"
#include "token.h"
#include "parse.h"
#include "symbol.h"
#include "expression.h"
#include "evaluate.h"
#include "scope.h"
#include "linearize.h"
#include "target.h"
#include "machine.h"
#include "bits.h"
static int prettify(const char **fnamep)
{
const char *name = *fnamep;
int len = strlen(name);
if (len > 2 && !memcmp(name, "./", 2)) {
name += 2;
len -= 2;
}
*fnamep = name;
return len;
}
static const char *show_include_chain(int stream, const char *base)
{
static char buffer[200];
int len = 0;
while ((stream = stream_prev(stream)) >= 0) {
const char *p = stream_name(stream);
int pretty_len;
if (p == base)
break;
pretty_len = prettify(&p);
if (pretty_len <= 0)
break;
/*
* At worst, we'll need " (through %s, ...)" in addition to the
* new filename
*/
if (pretty_len + len + 20 > sizeof(buffer)) {
if (!len)
return "";
memcpy(buffer+len, ", ...", 5);
len += 5;
break;
}
if (!len) {
memcpy(buffer, " (through ", 10);
len = 10;
} else {
buffer[len++] = ',';
buffer[len++] = ' ';
}
memcpy(buffer+len, p, pretty_len);
len += pretty_len;
}
if (!len)
return "";
buffer[len] = ')';
buffer[len+1] = 0;
return buffer;
}
static const char *show_stream_name(struct position pos)
{
const char *name = stream_name(pos.stream);
static const char *last;
if (name == base_filename)
return name;
if (name == last)
return name;
last = name;
fprintf(stderr, "%s: note: in included file%s:\n",
base_filename,
show_include_chain(pos.stream, base_filename));
return name;
}
static void do_warn(const char *type, struct position pos, const char * fmt, va_list args)
{
static char buffer[512];
/* Shut up warnings if position is bad_token.pos */
if (pos.type == TOKEN_BAD)
return;
vsprintf(buffer, fmt, args);
fflush(stdout);
fprintf(stderr, "%s:%d:%d: %s%s%s\n",
show_stream_name(pos), pos.line, pos.pos,
diag_prefix, type, buffer);
}
static int show_info = 1;
void info(struct position pos, const char * fmt, ...)
{
va_list args;
if (!show_info)
return;
va_start(args, fmt);
do_warn("", pos, fmt, args);
va_end(args);
}
static void do_error(struct position pos, const char * fmt, va_list args)
{
static int errors = 0;
die_if_error = 1;
show_info = 1;
/* Shut up warnings if position is bad_token.pos */
if (pos.type == TOKEN_BAD)
return;
/* Shut up warnings after an error */
has_error |= ERROR_CURR_PHASE;
if (errors > fmax_errors) {
static int once = 0;
show_info = 0;
if (once)
return;
fmt = "too many errors";
once = 1;
}
do_warn("error: ", pos, fmt, args);
errors++;
}
void warning(struct position pos, const char * fmt, ...)
{
va_list args;
if (Wsparse_error) {
va_start(args, fmt);
do_error(pos, fmt, args);
va_end(args);
return;
}
if (!fmax_warnings || has_error) {
show_info = 0;
return;
}
if (!--fmax_warnings) {
show_info = 0;
fmt = "too many warnings";
}
va_start(args, fmt);
do_warn("warning: ", pos, fmt, args);
va_end(args);
}
void sparse_error(struct position pos, const char * fmt, ...)
{
va_list args;
va_start(args, fmt);
do_error(pos, fmt, args);
va_end(args);
}
void expression_error(struct expression *expr, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
do_error(expr->pos, fmt, args);
va_end(args);
expr->ctype = &bad_ctype;
}
NORETURN_ATTR
void error_die(struct position pos, const char * fmt, ...)
{
va_list args;
va_start(args, fmt);
do_warn("error: ", pos, fmt, args);
va_end(args);
exit(1);
}
NORETURN_ATTR
void die(const char *fmt, ...)
{
va_list args;
static char buffer[512];
va_start(args, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args);
fprintf(stderr, "%s%s\n", diag_prefix, buffer);
exit(1);
}
////////////////////////////////////////////////////////////////////////////////
static struct token *pre_buffer_begin = NULL;
static struct token **pre_buffer_next = &pre_buffer_begin;
void add_pre_buffer(const char *fmt, ...)
{
va_list args;
unsigned int size;
struct token *begin, *end;
char buffer[4096];
va_start(args, fmt);
size = vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args);
begin = tokenize_buffer(buffer, size, &end);
*pre_buffer_next = begin;
pre_buffer_next = &end->next;
}
static void create_builtin_stream(void)
{
// Temporary hack
add_pre_buffer("#define _Pragma(x)\n");
/* add the multiarch include directories, if any */
if (multiarch_dir && *multiarch_dir) {
add_pre_buffer("#add_system \"/usr/include/%s\"\n", multiarch_dir);
add_pre_buffer("#add_system \"/usr/local/include/%s\"\n", multiarch_dir);
}
/* We add compiler headers path here because we have to parse
* the arguments to get it, falling back to default. */
add_pre_buffer("#add_system \"%s/include\"\n", gcc_base_dir);
add_pre_buffer("#add_system \"%s/include-fixed\"\n", gcc_base_dir);
add_pre_buffer("#define __builtin_stdarg_start(a,b) ((a) = (__builtin_va_list)(&(b)))\n");
add_pre_buffer("#define __builtin_va_start(a,b) ((a) = (__builtin_va_list)(&(b)))\n");
add_pre_buffer("#define __builtin_ms_va_start(a,b) ((a) = (__builtin_ms_va_list)(&(b)))\n");
add_pre_buffer("#define __builtin_va_arg(arg,type) ({ type __va_arg_ret = *(type *)(arg); arg += sizeof(type); __va_arg_ret; })\n");
add_pre_buffer("#define __builtin_va_alist (*(void *)0)\n");
add_pre_buffer("#define __builtin_va_arg_incr(x) ((x) + 1)\n");
add_pre_buffer("#define __builtin_va_copy(dest, src) ({ dest = src; (void)0; })\n");
add_pre_buffer("#define __builtin_ms_va_copy(dest, src) ({ dest = src; (void)0; })\n");
add_pre_buffer("#define __builtin_va_end(arg)\n");
add_pre_buffer("#define __builtin_ms_va_end(arg)\n");
add_pre_buffer("#define __builtin_va_arg_pack()\n");
}
static struct symbol_list *sparse_tokenstream(struct token *token)
{
int builtin = token && !token->pos.stream;
// Preprocess the stream
token = preprocess(token);
if (dump_macro_defs || dump_macros_only) {
if (!builtin)
dump_macro_definitions();
if (dump_macros_only)
return NULL;
}
if (preprocess_only) {
while (!eof_token(token)) {
int prec = 1;
struct token *next = token->next;
const char *separator = "";
if (next->pos.whitespace)
separator = " ";
if (next->pos.newline) {
separator = "\n\t\t\t\t\t";
prec = next->pos.pos;
if (prec > 4)
prec = 4;
}
printf("%s%.*s", show_token(token), prec, separator);
token = next;
}
putchar('\n');
return NULL;
}
// Parse the resulting C code
while (!eof_token(token))
token = external_declaration(token, &translation_unit_used_list, NULL);
return translation_unit_used_list;
}
static struct symbol_list *sparse_file(const char *filename)
{
int fd;
struct token *token;
if (strcmp(filename, "-") == 0) {
fd = 0;
} else {
fd = open(filename, O_RDONLY);
if (fd < 0)
die("No such file: %s", filename);
}
base_filename = filename;
// Tokenize the input stream
token = tokenize(NULL, filename, fd, NULL, includepath);
close(fd);
return sparse_tokenstream(token);
}
/*
* This handles the "-include" directive etc: we're in global
* scope, and all types/macros etc will affect all the following
* files.
*
* NOTE NOTE NOTE! "#undef" of anything in this stage will
* affect all subsequent files too, i.e. we can have non-local
* behaviour between files!
*/
static struct symbol_list *sparse_initial(void)
{
int i;
// Prepend any "include" file to the stream.
// We're in global scope, it will affect all files!
for (i = 0; i < cmdline_include_nr; i++)
add_pre_buffer("#argv_include \"%s\"\n", cmdline_include[i]);
return sparse_tokenstream(pre_buffer_begin);
}
struct symbol_list *sparse_initialize(int argc, char **argv, struct string_list **filelist)
{
char **args;
struct symbol_list *list;
base_filename = "command-line";
// Initialize symbol stream first, so that we can add defines etc
init_symbols();
// initialize the default target to the native 'machine'
target_config(MACH_NATIVE);
args = argv;
for (;;) {
char *arg = *++args;
if (!arg)
break;
if (arg[0] == '-' && arg[1]) {
args = handle_switch(arg+1, args);
continue;
}
add_ptr_list(filelist, arg);
}
handle_switch_finalize();
// Redirect stdout if needed
if (dump_macro_defs || preprocess_only)
do_output = 1;
if (do_output && outfile && strcmp(outfile, "-")) {
if (!freopen(outfile, "w", stdout))
die("error: cannot open %s: %s", outfile, strerror(errno));
}
if (fdump_ir == 0)
fdump_ir = PASS_FINAL;
list = NULL;
if (filelist) {
// Initialize type system
target_init();
init_ctype();
predefined_macros();
create_builtin_stream();
init_builtins(0);
list = sparse_initial();
/*
* Protect the initial token allocations, since
* they need to survive all the others
*/
protect_token_alloc();
}
/*
* Evaluate the complete symbol list
* Note: This is not needed for normal cases.
* These symbols should only be predefined defines and
* declaratons which will be evaluated later, when needed.
* This is also the case when a file is directly included via
* '-include ' on the command line *AND* the file only
* contains defines, declarations and inline definitions.
* However, in the rare cases where the given file should
* contain some definitions, these will never be evaluated
* and thus won't be able to be linearized correctly.
* Hence the evaluate_symbol_list() here under.
*/
evaluate_symbol_list(list);
return list;
}
struct symbol_list * sparse_keep_tokens(char *filename)
{
struct symbol_list *res;
/* Clear previous symbol list */
translation_unit_used_list = NULL;
new_file_scope();
res = sparse_file(filename);
/* And return it */
return res;
}
struct symbol_list * __sparse(char *filename)
{
struct symbol_list *res;
res = sparse_keep_tokens(filename);
/* Drop the tokens for this file after parsing */
clear_token_alloc();
/* And return it */
return res;
}
struct symbol_list * sparse(char *filename)
{
struct symbol_list *res = __sparse(filename);
if (has_error & ERROR_CURR_PHASE)
has_error = ERROR_PREV_PHASE;
/* Evaluate the complete symbol list */
evaluate_symbol_list(res);
return res;
}
sparse-0.6.4/lib.h 0000664 0000000 0000000 00000016050 14115310122 0013725 0 ustar 00root root 0000000 0000000 #ifndef LIB_H
#define LIB_H
#include
#include
#include
/*
* Basic helper routine descriptions for 'sparse'.
*
* Copyright (C) 2003 Transmeta Corp.
* 2003 Linus Torvalds
* 2004 Christopher Li
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "compat.h"
#include "ptrlist.h"
#include "utils.h"
#include "bits.h"
#include "options.h"
#define DO_STRINGIFY(x) #x
#define STRINGIFY(x) DO_STRINGIFY(x)
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
#endif
#ifdef __gnu_hurd__
#define PATH_MAX 4096 // Hurd doesn't define this
#endif
extern const char *sparse_version;
struct position {
unsigned int type:6,
stream:14,
newline:1,
whitespace:1,
pos:10;
unsigned int line:31,
noexpand:1;
};
struct ident;
struct token;
struct symbol;
struct statement;
struct asm_operand;
struct expression;
struct basic_block;
struct entrypoint;
struct instruction;
struct multijmp;
struct pseudo;
DECLARE_PTR_LIST(symbol_list, struct symbol);
DECLARE_PTR_LIST(statement_list, struct statement);
DECLARE_PTR_LIST(asm_operand_list, struct asm_operand);
DECLARE_PTR_LIST(expression_list, struct expression);
DECLARE_PTR_LIST(basic_block_list, struct basic_block);
DECLARE_PTR_LIST(instruction_list, struct instruction);
DECLARE_PTR_LIST(multijmp_list, struct multijmp);
DECLARE_PTR_LIST(pseudo_list, struct pseudo);
DECLARE_PTR_LIST(ident_list, struct ident);
DECLARE_PTR_LIST(string_list, char);
typedef struct pseudo *pseudo_t;
#ifdef __GNUC__
#define FORMAT_ATTR(pos) __attribute__ ((__format__ (__printf__, pos, pos+1)))
#define NORETURN_ATTR __attribute__ ((__noreturn__))
#define SENTINEL_ATTR __attribute__ ((__sentinel__))
#else
#define FORMAT_ATTR(pos)
#define NORETURN_ATTR
#define SENTINEL_ATTR
#endif
FORMAT_ATTR(1) NORETURN_ATTR
extern void die(const char *, ...);
FORMAT_ATTR(2) NORETURN_ATTR
extern void error_die(struct position, const char *, ...);
extern void info(struct position, const char *, ...) FORMAT_ATTR(2);
extern void warning(struct position, const char *, ...) FORMAT_ATTR(2);
extern void sparse_error(struct position, const char *, ...) FORMAT_ATTR(2);
extern void expression_error(struct expression *, const char *, ...) FORMAT_ATTR(2);
#define ERROR_CURR_PHASE (1 << 0)
#define ERROR_PREV_PHASE (1 << 1)
extern int has_error;
enum phase {
PASS__PARSE,
PASS__LINEARIZE,
PASS__MEM2REG,
PASS__OPTIM,
PASS__FINAL,
};
#define PASS_PARSE (1UL << PASS__PARSE)
#define PASS_LINEARIZE (1UL << PASS__LINEARIZE)
#define PASS_MEM2REG (1UL << PASS__MEM2REG)
#define PASS_OPTIM (1UL << PASS__OPTIM)
#define PASS_FINAL (1UL << PASS__FINAL)
extern void add_pre_buffer(const char *fmt, ...) FORMAT_ATTR(1);
extern void predefine(const char *name, int weak, const char *fmt, ...) FORMAT_ATTR(3);
extern void predefine_strong(const char *name, ...) FORMAT_ATTR(1);
extern void predefine_weak(const char *name, ...) FORMAT_ATTR(1);
extern void predefine_nostd(const char *name);
extern void predefined_macros(void);
extern void dump_macro_definitions(void);
extern struct symbol_list *sparse_initialize(int argc, char **argv, struct string_list **files);
extern struct symbol_list *__sparse(char *filename);
extern struct symbol_list *sparse_keep_tokens(char *filename);
extern struct symbol_list *sparse(char *filename);
extern void report_stats(void);
static inline int symbol_list_size(struct symbol_list *list)
{
return ptr_list_size((struct ptr_list *)(list));
}
static inline int statement_list_size(struct statement_list *list)
{
return ptr_list_size((struct ptr_list *)(list));
}
static inline int expression_list_size(struct expression_list *list)
{
return ptr_list_size((struct ptr_list *)(list));
}
static inline int instruction_list_size(struct instruction_list *list)
{
return ptr_list_size((struct ptr_list *)(list));
}
static inline int pseudo_list_size(struct pseudo_list *list)
{
return ptr_list_size((struct ptr_list *)(list));
}
static inline int bb_list_size(struct basic_block_list *list)
{
return ptr_list_size((struct ptr_list *)(list));
}
static inline void free_instruction_list(struct instruction_list **head)
{
free_ptr_list(head);
}
static inline struct instruction * delete_last_instruction(struct instruction_list **head)
{
return undo_ptr_list_last((struct ptr_list **)head);
}
static inline struct basic_block *first_basic_block(struct basic_block_list *head)
{
return first_ptr_list((struct ptr_list *)head);
}
static inline struct instruction *last_instruction(struct instruction_list *head)
{
return last_ptr_list((struct ptr_list *)head);
}
static inline struct instruction *first_instruction(struct instruction_list *head)
{
return first_ptr_list((struct ptr_list *)head);
}
static inline struct expression *first_expression(struct expression_list *head)
{
return first_ptr_list((struct ptr_list *)head);
}
static inline pseudo_t first_pseudo(struct pseudo_list *head)
{
return first_ptr_list((struct ptr_list *)head);
}
static inline struct symbol *first_symbol(struct symbol_list *head)
{
return first_ptr_list((struct ptr_list *)head);
}
static inline void concat_symbol_list(struct symbol_list *from, struct symbol_list **to)
{
concat_ptr_list((struct ptr_list *)from, (struct ptr_list **)to);
}
static inline void concat_basic_block_list(struct basic_block_list *from, struct basic_block_list **to)
{
concat_ptr_list((struct ptr_list *)from, (struct ptr_list **)to);
}
static inline void concat_instruction_list(struct instruction_list *from, struct instruction_list **to)
{
concat_ptr_list((struct ptr_list *)from, (struct ptr_list **)to);
}
static inline void add_symbol(struct symbol_list **list, struct symbol *sym)
{
add_ptr_list(list, sym);
}
static inline void add_statement(struct statement_list **list, struct statement *stmt)
{
add_ptr_list(list, stmt);
}
static inline void add_expression(struct expression_list **list, struct expression *expr)
{
add_ptr_list(list, expr);
}
static inline void add_ident(struct ident_list **list, struct ident *ident)
{
add_ptr_list(list, ident);
}
#define hashval(x) ((unsigned long)(x))
#endif
sparse-0.6.4/linearize.c 0000664 0000000 0000000 00000205462 14115310122 0015143 0 ustar 00root root 0000000 0000000 /*
* Linearize - walk the statement tree (but _not_ the expressions)
* to generate a linear version of it and the basic blocks.
*
* NOTE! We're not interested in the actual sub-expressions yet,
* even though they can generate conditional branches and
* subroutine calls. That's all "local" behaviour.
*
* Copyright (C) 2004 Linus Torvalds
* Copyright (C) 2004 Christopher Li
*/
#include
#include
#include
#include
#include
#include "parse.h"
#include "expression.h"
#include "linearize.h"
#include "optimize.h"
#include "flow.h"
#include "target.h"
static pseudo_t linearize_statement(struct entrypoint *ep, struct statement *stmt);
static pseudo_t linearize_expression(struct entrypoint *ep, struct expression *expr);
static pseudo_t add_cast(struct entrypoint *ep, struct symbol *to, struct symbol *from, int op, pseudo_t src);
static pseudo_t add_binary_op(struct entrypoint *ep, struct symbol *ctype, int op, pseudo_t left, pseudo_t right);
static pseudo_t linearize_one_symbol(struct entrypoint *ep, struct symbol *sym);
static pseudo_t cast_pseudo(struct entrypoint *ep, pseudo_t src, struct symbol *from, struct symbol *to);
struct pseudo void_pseudo = {};
static struct position current_pos;
ALLOCATOR(pseudo_user, "pseudo_user");
static struct instruction *alloc_instruction(int opcode, int size)
{
struct instruction * insn = __alloc_instruction(0);
insn->opcode = opcode;
insn->size = size;
insn->pos = current_pos;
return insn;
}
static inline int type_size(struct symbol *type)
{
return type ? type->bit_size > 0 ? type->bit_size : 0 : 0;
}
static struct instruction *alloc_typed_instruction(int opcode, struct symbol *type)
{
struct instruction *insn = alloc_instruction(opcode, type_size(type));
insn->type = type;
return insn;
}
static struct entrypoint *alloc_entrypoint(void)
{
return __alloc_entrypoint(0);
}
static struct basic_block *alloc_basic_block(struct entrypoint *ep, struct position pos)
{
static int nr;
struct basic_block *bb = __alloc_basic_block(0);
bb->pos = pos;
bb->ep = ep;
bb->nr = nr++;
return bb;
}
static struct multijmp *alloc_multijmp(struct basic_block *target, long long begin, long long end)
{
struct multijmp *multijmp = __alloc_multijmp(0);
multijmp->target = target;
multijmp->begin = begin;
multijmp->end = end;
return multijmp;
}
const char *show_label(struct basic_block *bb)
{
static int n;
static char buffer[4][16];
char *buf = buffer[3 & ++n];
if (!bb)
return ".L???";
snprintf(buf, 64, ".L%u", bb->nr);
return buf;
}
const char *show_pseudo(pseudo_t pseudo)
{
static int n;
static char buffer[4][64];
char *buf;
int i;
if (!pseudo)
return "no pseudo";
if (pseudo == VOID)
return "VOID";
buf = buffer[3 & ++n];
switch(pseudo->type) {
case PSEUDO_SYM: {
struct symbol *sym = pseudo->sym;
struct expression *expr;
if (!sym) {
snprintf(buf, 64, "");
break;
}
if (sym->bb_target) {
snprintf(buf, 64, "%s", show_label(sym->bb_target));
break;
}
if (sym->ident) {
snprintf(buf, 64, "%s", show_ident(sym->ident));
break;
}
expr = sym->initializer;
snprintf(buf, 64, "", verbose ? sym : NULL);
if (expr) {
switch (expr->type) {
case EXPR_VALUE:
snprintf(buf, 64, "", expr->value);
break;
case EXPR_STRING:
return show_string(expr->string);
default:
break;
}
}
break;
}
case PSEUDO_REG:
i = snprintf(buf, 64, "%%r%d", pseudo->nr);
if (pseudo->ident)
sprintf(buf+i, "(%s)", show_ident(pseudo->ident));
break;
case PSEUDO_VAL: {
long long value = pseudo->value;
if (value > 1000 || value < -1000)
snprintf(buf, 64, "$%#llx", value);
else
snprintf(buf, 64, "$%lld", value);
break;
}
case PSEUDO_ARG:
snprintf(buf, 64, "%%arg%d", pseudo->nr);
break;
case PSEUDO_PHI:
i = snprintf(buf, 64, "%%phi%d", pseudo->nr);
if (pseudo->ident)
sprintf(buf+i, "(%s)", show_ident(pseudo->ident));
break;
case PSEUDO_UNDEF:
return "UNDEF";
default:
snprintf(buf, 64, "", pseudo->type);
}
return buf;
}
static const char *opcodes[] = {
[OP_BADOP] = "bad_op",
/* Fn entrypoint */
[OP_ENTRY] = "",
/* Terminator */
[OP_RET] = "ret",
[OP_BR] = "br",
[OP_CBR] = "cbr",
[OP_SWITCH] = "switch",
[OP_UNREACH] = "unreachable",
[OP_COMPUTEDGOTO] = "jmp *",
/* Binary */
[OP_ADD] = "add",
[OP_SUB] = "sub",
[OP_MUL] = "mul",
[OP_DIVU] = "divu",
[OP_DIVS] = "divs",
[OP_MODU] = "modu",
[OP_MODS] = "mods",
[OP_SHL] = "shl",
[OP_LSR] = "lsr",
[OP_ASR] = "asr",
/* Floating-point Binary */
[OP_FADD] = "fadd",
[OP_FSUB] = "fsub",
[OP_FMUL] = "fmul",
[OP_FDIV] = "fdiv",
/* Logical */
[OP_AND] = "and",
[OP_OR] = "or",
[OP_XOR] = "xor",
/* Binary comparison */
[OP_SET_EQ] = "seteq",
[OP_SET_NE] = "setne",
[OP_SET_LE] = "setle",
[OP_SET_GE] = "setge",
[OP_SET_LT] = "setlt",
[OP_SET_GT] = "setgt",
[OP_SET_B] = "setb",
[OP_SET_A] = "seta",
[OP_SET_BE] = "setbe",
[OP_SET_AE] = "setae",
/* floating-point comparison */
[OP_FCMP_ORD] = "fcmpord",
[OP_FCMP_OEQ] = "fcmpoeq",
[OP_FCMP_ONE] = "fcmpone",
[OP_FCMP_OLE] = "fcmpole",
[OP_FCMP_OGE] = "fcmpoge",
[OP_FCMP_OLT] = "fcmpolt",
[OP_FCMP_OGT] = "fcmpogt",
[OP_FCMP_UEQ] = "fcmpueq",
[OP_FCMP_UNE] = "fcmpune",
[OP_FCMP_ULE] = "fcmpule",
[OP_FCMP_UGE] = "fcmpuge",
[OP_FCMP_ULT] = "fcmpult",
[OP_FCMP_UGT] = "fcmpugt",
[OP_FCMP_UNO] = "fcmpuno",
/* Uni */
[OP_NOT] = "not",
[OP_NEG] = "neg",
[OP_FNEG] = "fneg",
/* Special three-input */
[OP_SEL] = "select",
[OP_FMADD] = "fmadd",
/* Memory */
[OP_LOAD] = "load",
[OP_STORE] = "store",
[OP_LABEL] = "label",
[OP_SETVAL] = "set",
[OP_SETFVAL] = "setfval",
[OP_SYMADDR] = "symaddr",
/* Other */
[OP_PHI] = "phi",
[OP_PHISOURCE] = "phisrc",
[OP_SEXT] = "sext",
[OP_ZEXT] = "zext",
[OP_TRUNC] = "trunc",
[OP_FCVTU] = "fcvtu",
[OP_FCVTS] = "fcvts",
[OP_UCVTF] = "ucvtf",
[OP_SCVTF] = "scvtf",
[OP_FCVTF] = "fcvtf",
[OP_UTPTR] = "utptr",
[OP_PTRTU] = "ptrtu",
[OP_PTRCAST] = "ptrcast",
[OP_INLINED_CALL] = "# call",
[OP_CALL] = "call",
[OP_SLICE] = "slice",
[OP_NOP] = "nop",
[OP_DEATHNOTE] = "dead",
[OP_ASM] = "asm",
/* Sparse tagging (line numbers, context, whatever) */
[OP_CONTEXT] = "context",
[OP_RANGE] = "range-check",
[OP_COPY] = "copy",
};
static char *show_asm_constraints(char *buf, const char *sep, struct asm_constraint_list *list)
{
struct asm_constraint *entry;
FOR_EACH_PTR(list, entry) {
buf += sprintf(buf, "%s\"%s\"", sep, entry->constraint);
if (entry->pseudo)
buf += sprintf(buf, " (%s)", show_pseudo(entry->pseudo));
if (entry->ident)
buf += sprintf(buf, " [%s]", show_ident(entry->ident));
sep = ", ";
} END_FOR_EACH_PTR(entry);
return buf;
}
static char *show_asm(char *buf, struct instruction *insn)
{
struct asm_rules *rules = insn->asm_rules;
buf += sprintf(buf, "\"%s\"", insn->string);
buf = show_asm_constraints(buf, "\n\t\tout: ", rules->outputs);
buf = show_asm_constraints(buf, "\n\t\tin: ", rules->inputs);
buf = show_asm_constraints(buf, "\n\t\tclobber: ", rules->clobbers);
return buf;
}
const char *show_instruction(struct instruction *insn)
{
int opcode = insn->opcode;
static char buffer[4096];
char *buf;
buf = buffer;
if (!insn->bb)
buf += sprintf(buf, "# ");
if (opcode < ARRAY_SIZE(opcodes)) {
const char *op = opcodes[opcode];
if (!op)
buf += sprintf(buf, "opcode:%d", opcode);
else
buf += sprintf(buf, "%s", op);
if (insn->size)
buf += sprintf(buf, ".%d", insn->size);
memset(buf, ' ', 20);
buf++;
}
if (buf < buffer + 12)
buf = buffer + 12;
switch (opcode) {
case OP_RET:
if (insn->src && insn->src != VOID)
buf += sprintf(buf, "%s", show_pseudo(insn->src));
break;
case OP_CBR:
buf += sprintf(buf, "%s, %s, %s", show_pseudo(insn->cond), show_label(insn->bb_true), show_label(insn->bb_false));
break;
case OP_BR:
buf += sprintf(buf, "%s", show_label(insn->bb_true));
break;
case OP_LABEL:
buf += sprintf(buf, "%s <- ", show_pseudo(insn->target));
buf += sprintf(buf, "%s", show_label(insn->bb_true));
break;
case OP_SETVAL: {
struct expression *expr = insn->val;
buf += sprintf(buf, "%s <- ", show_pseudo(insn->target));
if (!expr) {
buf += sprintf(buf, "%s", "");
break;
}
switch (expr->type) {
case EXPR_VALUE:
buf += sprintf(buf, "%lld", expr->value);
break;
case EXPR_FVALUE:
buf += sprintf(buf, "%Le", expr->fvalue);
break;
case EXPR_STRING:
buf += sprintf(buf, "%.40s", show_string(expr->string));
break;
case EXPR_SYMBOL:
buf += sprintf(buf, "%s", show_ident(expr->symbol->ident));
break;
case EXPR_LABEL:
buf += sprintf(buf, "%s", show_label(expr->symbol->bb_target));
break;
default:
buf += sprintf(buf, "SETVAL EXPR TYPE %d", expr->type);
}
break;
}
case OP_SETFVAL:
buf += sprintf(buf, "%s <- ", show_pseudo(insn->target));
buf += sprintf(buf, "%Le", insn->fvalue);
break;
case OP_SWITCH: {
struct multijmp *jmp;
buf += sprintf(buf, "%s", show_pseudo(insn->cond));
FOR_EACH_PTR(insn->multijmp_list, jmp) {
if (jmp->begin == jmp->end)
buf += sprintf(buf, ", %lld -> %s", jmp->begin, show_label(jmp->target));
else if (jmp->begin < jmp->end)
buf += sprintf(buf, ", %lld ... %lld -> %s", jmp->begin, jmp->end, show_label(jmp->target));
else
buf += sprintf(buf, ", default -> %s", show_label(jmp->target));
} END_FOR_EACH_PTR(jmp);
break;
}
case OP_COMPUTEDGOTO: {
struct multijmp *jmp;
buf += sprintf(buf, "%s", show_pseudo(insn->src));
FOR_EACH_PTR(insn->multijmp_list, jmp) {
buf += sprintf(buf, ", %s", show_label(jmp->target));
} END_FOR_EACH_PTR(jmp);
break;
}
case OP_UNREACH:
break;
case OP_PHISOURCE: {
buf += sprintf(buf, "%s <- %s ", show_pseudo(insn->target), show_pseudo(insn->phi_src));
break;
}
case OP_PHI: {
pseudo_t phi;
const char *s = " <-";
buf += sprintf(buf, "%s", show_pseudo(insn->target));
FOR_EACH_PTR(insn->phi_list, phi) {
if (phi == VOID && !verbose)
continue;
buf += sprintf(buf, "%s %s", s, show_pseudo(phi));
s = ",";
} END_FOR_EACH_PTR(phi);
break;
}
case OP_LOAD:
buf += sprintf(buf, "%s <- %lld[%s]", show_pseudo(insn->target), insn->offset, show_pseudo(insn->src));
break;
case OP_STORE:
buf += sprintf(buf, "%s -> %lld[%s]", show_pseudo(insn->target), insn->offset, show_pseudo(insn->src));
break;
case OP_INLINED_CALL:
case OP_CALL: {
struct pseudo *arg;
if (insn->target && insn->target != VOID)
buf += sprintf(buf, "%s <- ", show_pseudo(insn->target));
buf += sprintf(buf, "%s", show_pseudo(insn->func));
FOR_EACH_PTR(insn->arguments, arg) {
buf += sprintf(buf, ", %s", show_pseudo(arg));
} END_FOR_EACH_PTR(arg);
break;
}
case OP_SEXT: case OP_ZEXT:
case OP_TRUNC:
case OP_FCVTU: case OP_FCVTS:
case OP_UCVTF: case OP_SCVTF:
case OP_FCVTF:
case OP_UTPTR:
case OP_PTRTU:
case OP_PTRCAST:
buf += sprintf(buf, "%s <- (%d) %s",
show_pseudo(insn->target),
type_size(insn->orig_type),
show_pseudo(insn->src));
break;
case OP_BINARY ... OP_BINARY_END:
case OP_FPCMP ... OP_FPCMP_END:
case OP_BINCMP ... OP_BINCMP_END:
buf += sprintf(buf, "%s <- %s, %s", show_pseudo(insn->target), show_pseudo(insn->src1), show_pseudo(insn->src2));
break;
case OP_SEL:
case OP_FMADD:
buf += sprintf(buf, "%s <- %s, %s, %s", show_pseudo(insn->target),
show_pseudo(insn->src1), show_pseudo(insn->src2), show_pseudo(insn->src3));
break;
case OP_SLICE:
buf += sprintf(buf, "%s <- (%d) %s, %d", show_pseudo(insn->target), type_size(insn->orig_type), show_pseudo(insn->src), insn->from);
break;
case OP_NOT: case OP_NEG:
case OP_FNEG:
case OP_SYMADDR:
buf += sprintf(buf, "%s <- %s", show_pseudo(insn->target), show_pseudo(insn->src1));
break;
case OP_CONTEXT:
buf += sprintf(buf, "%s%d", insn->check ? "check: " : "", insn->increment);
break;
case OP_RANGE:
buf += sprintf(buf, "%s between %s..%s", show_pseudo(insn->src1), show_pseudo(insn->src2), show_pseudo(insn->src3));
break;
case OP_NOP:
buf += sprintf(buf, "%s <- %s", show_pseudo(insn->target), show_pseudo(insn->src1));
break;
case OP_DEATHNOTE:
buf += sprintf(buf, "%s", show_pseudo(insn->target));
break;
case OP_ASM:
buf = show_asm(buf, insn);
break;
case OP_COPY:
buf += sprintf(buf, "%s <- %s", show_pseudo(insn->target), show_pseudo(insn->src));
break;
default:
break;
}
if (buf >= buffer + sizeof(buffer))
die("instruction buffer overflowed %td\n", buf - buffer);
do { --buf; } while (*buf == ' ');
*++buf = 0;
return buffer;
}
void show_bb(struct basic_block *bb)
{
struct instruction *insn;
printf("%s:\n", show_label(bb));
if (verbose) {
pseudo_t needs, defines;
printf("%s:%d\n", stream_name(bb->pos.stream), bb->pos.line);
FOR_EACH_PTR(bb->needs, needs) {
struct instruction *def = needs->def;
if (def->opcode != OP_PHI) {
printf(" **uses %s (from %s)**\n", show_pseudo(needs), show_label(def->bb));
} else {
pseudo_t phi;
const char *sep = " ";
printf(" **uses %s (from", show_pseudo(needs));
FOR_EACH_PTR(def->phi_list, phi) {
if (phi == VOID)
continue;
printf("%s(%s:%s)", sep, show_pseudo(phi), show_label(phi->def->bb));
sep = ", ";
} END_FOR_EACH_PTR(phi);
printf(")**\n");
}
} END_FOR_EACH_PTR(needs);
FOR_EACH_PTR(bb->defines, defines) {
printf(" **defines %s **\n", show_pseudo(defines));
} END_FOR_EACH_PTR(defines);
if (bb->parents) {
struct basic_block *from;
FOR_EACH_PTR(bb->parents, from) {
printf(" **from %s (%s:%d:%d)**\n", show_label(from),
stream_name(from->pos.stream), from->pos.line, from->pos.pos);
} END_FOR_EACH_PTR(from);
}
if (bb->children) {
struct basic_block *to;
FOR_EACH_PTR(bb->children, to) {
printf(" **to %s (%s:%d:%d)**\n", show_label(to),
stream_name(to->pos.stream), to->pos.line, to->pos.pos);
} END_FOR_EACH_PTR(to);
}
}
FOR_EACH_PTR(bb->insns, insn) {
if (!insn->bb && verbose < 2)
continue;
printf("\t%s\n", show_instruction(insn));
} END_FOR_EACH_PTR(insn);
if (!bb_terminated(bb))
printf("\tEND\n");
}
///
// show BB of non-removed instruction
void show_insn_bb(struct instruction *insn)
{
if (!insn || !insn->bb)
return;
show_bb(insn->bb);
}
static void show_symbol_usage(pseudo_t pseudo)
{
struct pseudo_user *pu;
if (pseudo) {
FOR_EACH_PTR(pseudo->users, pu) {
printf("\t%s\n", show_instruction(pu->insn));
} END_FOR_EACH_PTR(pu);
}
}
void show_entry(struct entrypoint *ep)
{
struct symbol *sym;
struct basic_block *bb;
printf("%s:\n", show_ident(ep->name->ident));
if (verbose) {
printf("ep %p: %s\n", ep, show_ident(ep->name->ident));
FOR_EACH_PTR(ep->syms, sym) {
if (!sym->pseudo)
continue;
if (!sym->pseudo->users)
continue;
printf(" sym: %p %s\n", sym, show_ident(sym->ident));
if (sym->ctype.modifiers & (MOD_EXTERN | MOD_STATIC | MOD_ADDRESSABLE))
printf("\texternal visibility\n");
show_symbol_usage(sym->pseudo);
} END_FOR_EACH_PTR(sym);
printf("\n");
}
FOR_EACH_PTR(ep->bbs, bb) {
if (!bb)
continue;
if (!bb->parents && !bb->children && !bb->insns && verbose < 2)
continue;
show_bb(bb);
printf("\n");
} END_FOR_EACH_PTR(bb);
printf("\n");
}
///
// show the function containing the instruction but only if not already removed.
void show_insn_entry(struct instruction *insn)
{
if (!insn || !insn->bb || !insn->bb->ep)
return;
show_entry(insn->bb->ep);
}
static void bind_label(struct symbol *label, struct basic_block *bb, struct position pos)
{
if (label->bb_target)
warning(pos, "label '%s' already bound", show_ident(label->ident));
label->bb_target = bb;
}
static struct basic_block * get_bound_block(struct entrypoint *ep, struct symbol *label)
{
struct basic_block *bb = label->bb_target;
if (!bb) {
bb = alloc_basic_block(ep, label->pos);
label->bb_target = bb;
}
return bb;
}
static void finish_block(struct entrypoint *ep)
{
struct basic_block *src = ep->active;
if (bb_reachable(src))
ep->active = NULL;
}
static void add_goto(struct entrypoint *ep, struct basic_block *dst)
{
struct basic_block *src = ep->active;
if (bb_reachable(src)) {
struct instruction *br = alloc_instruction(OP_BR, 0);
br->bb_true = dst;
add_bb(&dst->parents, src);
add_bb(&src->children, dst);
br->bb = src;
add_instruction(&src->insns, br);
ep->active = NULL;
}
}
static void add_one_insn(struct entrypoint *ep, struct instruction *insn)
{
struct basic_block *bb = ep->active;
if (bb_reachable(bb)) {
insn->bb = bb;
add_instruction(&bb->insns, insn);
}
}
static void add_unreachable(struct entrypoint *ep)
{
struct instruction *insn = alloc_instruction(OP_UNREACH, 0);
add_one_insn(ep, insn);
ep->active = NULL;
}
static void set_activeblock(struct entrypoint *ep, struct basic_block *bb)
{
if (!bb_terminated(ep->active))
add_goto(ep, bb);
ep->active = bb;
if (bb_reachable(bb))
add_bb(&ep->bbs, bb);
}
void insert_select(struct basic_block *bb, struct instruction *br, struct instruction *phi_node, pseudo_t if_true, pseudo_t if_false)
{
pseudo_t target;
struct instruction *select;
select = alloc_typed_instruction(OP_SEL, phi_node->type);
assert(br->cond);
use_pseudo(select, br->cond, &select->src1);
target = phi_node->target;
assert(target->def == phi_node);
select->target = target;
target->def = select;
use_pseudo(select, if_true, &select->src2);
use_pseudo(select, if_false, &select->src3);
insert_last_instruction(bb, select);
}
static inline int bb_empty(struct basic_block *bb)
{
return !bb->insns;
}
/* Add a label to the currently active block, return new active block */
static struct basic_block * add_label(struct entrypoint *ep, struct symbol *label)
{
struct basic_block *bb = label->bb_target;
if (bb) {
set_activeblock(ep, bb);
return bb;
}
bb = ep->active;
if (!bb_reachable(bb) || !bb_empty(bb)) {
bb = alloc_basic_block(ep, label->pos);
set_activeblock(ep, bb);
}
label->bb_target = bb;
return bb;
}
static void add_branch(struct entrypoint *ep, pseudo_t cond, struct basic_block *bb_true, struct basic_block *bb_false)
{
struct basic_block *bb = ep->active;
struct instruction *br;
if (bb_reachable(bb)) {
br = alloc_instruction(OP_CBR, 0);
use_pseudo(br, cond, &br->cond);
br->bb_true = bb_true;
br->bb_false = bb_false;
add_bb(&bb_true->parents, bb);
add_bb(&bb_false->parents, bb);
add_bb(&bb->children, bb_true);
add_bb(&bb->children, bb_false);
add_one_insn(ep, br);
}
}
pseudo_t alloc_pseudo(struct instruction *def)
{
static int nr = 0;
struct pseudo * pseudo = __alloc_pseudo(0);
pseudo->type = PSEUDO_REG;
pseudo->nr = ++nr;
pseudo->def = def;
return pseudo;
}
static pseudo_t symbol_pseudo(struct entrypoint *ep, struct symbol *sym)
{
pseudo_t pseudo;
if (!sym)
return VOID;
pseudo = sym->pseudo;
if (!pseudo) {
pseudo = __alloc_pseudo(0);
pseudo->nr = -1;
pseudo->type = PSEUDO_SYM;
pseudo->sym = sym;
pseudo->ident = sym->ident;
sym->pseudo = pseudo;
add_pseudo(&ep->accesses, pseudo);
}
/* Symbol pseudos have neither nr nor def */
return pseudo;
}
pseudo_t value_pseudo(long long val)
{
#define MAX_VAL_HASH 64
static struct pseudo_list *prev[MAX_VAL_HASH];
int hash = val & (MAX_VAL_HASH-1);
struct pseudo_list **list = prev + hash;
pseudo_t pseudo;
FOR_EACH_PTR(*list, pseudo) {
if (pseudo->value == val)
return pseudo;
} END_FOR_EACH_PTR(pseudo);
pseudo = __alloc_pseudo(0);
pseudo->type = PSEUDO_VAL;
pseudo->value = val;
add_pseudo(list, pseudo);
/* Value pseudos have neither nr, usage nor def */
return pseudo;
}
pseudo_t undef_pseudo(void)
{
pseudo_t pseudo = __alloc_pseudo(0);
pseudo->type = PSEUDO_UNDEF;
return pseudo;
}
static pseudo_t argument_pseudo(struct entrypoint *ep, int nr)
{
pseudo_t pseudo = __alloc_pseudo(0);
struct instruction *entry = ep->entry;
pseudo->type = PSEUDO_ARG;
pseudo->nr = nr;
pseudo->def = entry;
add_pseudo(&entry->arg_list, pseudo);
/* Argument pseudos have neither usage nor def */
return pseudo;
}
struct instruction *alloc_phisrc(pseudo_t pseudo, struct symbol *type)
{
struct instruction *insn = alloc_typed_instruction(OP_PHISOURCE, type);
pseudo_t phi = __alloc_pseudo(0);
static int nr = 0;
phi->type = PSEUDO_PHI;
phi->nr = ++nr;
phi->def = insn;
use_pseudo(insn, pseudo, &insn->phi_src);
insn->target = phi;
return insn;
}
pseudo_t alloc_phi(struct basic_block *source, pseudo_t pseudo, struct symbol *type)
{
struct instruction *insn;
if (!source)
return VOID;
insn = alloc_phisrc(pseudo, type);
insn->bb = source;
add_instruction(&source->insns, insn);
return insn->target;
}
struct instruction *alloc_phi_node(struct basic_block *bb, struct symbol *type, struct ident *ident)
{
struct instruction *phi_node = alloc_typed_instruction(OP_PHI, type);
pseudo_t phi;
phi = alloc_pseudo(phi_node);
phi->ident = ident;
phi->def = phi_node;
phi_node->target = phi;
phi_node->bb = bb;
return phi_node;
}
void add_phi_node(struct basic_block *bb, struct instruction *phi_node)
{
struct instruction *insn;
FOR_EACH_PTR(bb->insns, insn) {
enum opcode op = insn->opcode;
if (op == OP_PHI)
continue;
INSERT_CURRENT(phi_node, insn);
return;
} END_FOR_EACH_PTR(insn);
// FIXME
add_instruction(&bb->insns, phi_node);
}
struct instruction *insert_phi_node(struct basic_block *bb, struct symbol *var)
{
struct instruction *phi_node = alloc_phi_node(bb, var, var->ident);
add_phi_node(bb, phi_node);
return phi_node;
}
/*
* We carry the "access_data" structure around for any accesses,
* which simplifies things a lot. It contains all the access
* information in one place.
*/
struct access_data {
struct symbol *type; // ctype
struct symbol *btype; // base type of bitfields
pseudo_t address; // pseudo containing address ..
long long offset; // byte offset
};
static int linearize_simple_address(struct entrypoint *ep,
struct expression *addr,
struct access_data *ad)
{
if (addr->type == EXPR_SYMBOL) {
linearize_one_symbol(ep, addr->symbol);
ad->address = symbol_pseudo(ep, addr->symbol);
return 1;
}
if (addr->type == EXPR_BINOP) {
if (addr->right->type == EXPR_VALUE) {
if (addr->op == '+') {
ad->offset += get_expression_value(addr->right);
return linearize_simple_address(ep, addr->left, ad);
}
}
}
ad->address = linearize_expression(ep, addr);
return 1;
}
static struct symbol *bitfield_base_type(struct symbol *sym)
{
struct symbol *base = sym;
if (sym) {
if (sym->type == SYM_NODE)
base = base->ctype.base_type;
if (base->type == SYM_BITFIELD) {
base = base->ctype.base_type;
if (sym->packed) {
int size = bits_to_bytes(sym->bit_offset + sym->bit_size);
sym = __alloc_symbol(0);
*sym = *base;
sym->bit_size = bytes_to_bits(size);
return sym;
}
return base;
}
}
return sym;
}
static int linearize_address_gen(struct entrypoint *ep,
struct expression *expr,
struct access_data *ad)
{
struct symbol *ctype = expr->ctype;
if (!ctype)
return 0;
ad->type = ctype;
if (expr->type == EXPR_PREOP && expr->op == '*')
return linearize_simple_address(ep, expr->unop, ad);
warning(expr->pos, "generating address of non-lvalue (%d)", expr->type);
return 0;
}
static pseudo_t add_load(struct entrypoint *ep, struct access_data *ad)
{
struct instruction *insn;
pseudo_t new;
if (!ep->active)
return VOID;
insn = alloc_typed_instruction(OP_LOAD, ad->btype);
new = alloc_pseudo(insn);
insn->target = new;
insn->offset = ad->offset;
insn->is_volatile = ad->type && (ad->type->ctype.modifiers & MOD_VOLATILE);
use_pseudo(insn, ad->address, &insn->src);
add_one_insn(ep, insn);
return new;
}
static void add_store(struct entrypoint *ep, struct access_data *ad, pseudo_t value)
{
struct basic_block *bb = ep->active;
struct instruction *store;
if (!bb)
return;
store = alloc_typed_instruction(OP_STORE, ad->btype);
store->offset = ad->offset;
store->is_volatile = ad->type && (ad->type->ctype.modifiers & MOD_VOLATILE);
use_pseudo(store, value, &store->target);
use_pseudo(store, ad->address, &store->src);
add_one_insn(ep, store);
}
static pseudo_t linearize_bitfield_insert(struct entrypoint *ep,
pseudo_t ori, pseudo_t val, struct symbol *ctype, struct symbol *btype)
{
unsigned int shift = ctype->bit_offset;
unsigned int size = ctype->bit_size;
unsigned long long mask = ((1ULL << size) - 1);
unsigned long long smask= bits_mask(btype->bit_size);
val = add_cast(ep, btype, ctype, OP_ZEXT, val);
if (shift) {
val = add_binary_op(ep, btype, OP_SHL, val, value_pseudo(shift));
mask <<= shift;
}
ori = add_binary_op(ep, btype, OP_AND, ori, value_pseudo(~mask & smask));
val = add_binary_op(ep, btype, OP_OR, ori, val);
return val;
}
static pseudo_t linearize_store_gen(struct entrypoint *ep,
pseudo_t value,
struct access_data *ad)
{
struct symbol *ctype = ad->type;
struct symbol *btype;
pseudo_t store = value;
if (!ep->active)
return VOID;
btype = ad->btype = bitfield_base_type(ctype);
if (type_size(btype) != type_size(ctype)) {
pseudo_t orig = add_load(ep, ad);
store = linearize_bitfield_insert(ep, orig, value, ctype, btype);
}
add_store(ep, ad, store);
return value;
}
static void taint_undefined_behaviour(struct instruction *insn)
{
pseudo_t src2;
switch (insn->opcode) {
case OP_LSR:
case OP_ASR:
case OP_SHL:
src2 = insn->src2;
if (src2->type != PSEUDO_VAL)
break;
if ((unsigned long long)src2->value >= insn->size)
insn->tainted = 1;
break;
}
}
static pseudo_t add_binary_op(struct entrypoint *ep, struct symbol *ctype, int op, pseudo_t left, pseudo_t right)
{
struct instruction *insn = alloc_typed_instruction(op, ctype);
pseudo_t target = alloc_pseudo(insn);
insn->target = target;
use_pseudo(insn, left, &insn->src1);
use_pseudo(insn, right, &insn->src2);
add_one_insn(ep, insn);
return target;
}
static pseudo_t add_cmp_op(struct entrypoint *ep, struct symbol *ctype, int op, struct symbol *itype, pseudo_t left, pseudo_t right)
{
pseudo_t target = add_binary_op(ep, ctype, op, left, right);
target->def->itype = itype;
return target;
}
static pseudo_t add_setval(struct entrypoint *ep, struct symbol *ctype, struct expression *val)
{
struct instruction *insn = alloc_typed_instruction(OP_SETVAL, ctype);
pseudo_t target = alloc_pseudo(insn);
insn->target = target;
insn->val = val;
add_one_insn(ep, insn);
return target;
}
static pseudo_t add_setfval(struct entrypoint *ep, struct symbol *ctype, long double fval)
{
struct instruction *insn = alloc_typed_instruction(OP_SETFVAL, ctype);
pseudo_t target = alloc_pseudo(insn);
insn->target = target;
insn->fvalue = fval;
add_one_insn(ep, insn);
return target;
}
static pseudo_t add_symbol_address(struct entrypoint *ep, struct expression *expr)
{
struct instruction *insn = alloc_typed_instruction(OP_SYMADDR, expr->ctype);
pseudo_t target = alloc_pseudo(insn);
insn->target = target;
use_pseudo(insn, symbol_pseudo(ep, expr->symbol), &insn->src);
add_one_insn(ep, insn);
return target;
}
static pseudo_t linearize_bitfield_extract(struct entrypoint *ep,
pseudo_t val, struct symbol *ctype, struct symbol *btype)
{
unsigned int off = ctype->bit_offset;
if (off) {
pseudo_t shift = value_pseudo(off);
val = add_binary_op(ep, btype, OP_LSR, val, shift);
}
val = cast_pseudo(ep, val, btype, ctype);
return val;
}
static pseudo_t linearize_load_gen(struct entrypoint *ep, struct access_data *ad)
{
struct symbol *ctype = ad->type;
struct symbol *btype;
pseudo_t new;
if (!ep->active)
return VOID;
btype = ad->btype = bitfield_base_type(ctype);
new = add_load(ep, ad);
if (ctype->bit_size != type_size(btype))
new = linearize_bitfield_extract(ep, new, ctype, btype);
return new;
}
static pseudo_t linearize_access(struct entrypoint *ep, struct expression *expr)
{
struct access_data ad = { NULL, };
pseudo_t value;
if (!linearize_address_gen(ep, expr, &ad))
return VOID;
value = linearize_load_gen(ep, &ad);
return value;
}
static pseudo_t linearize_inc_dec(struct entrypoint *ep, struct expression *expr, int postop)
{
struct access_data ad = { NULL, };
pseudo_t old, new, one;
int op = expr->op == SPECIAL_INCREMENT ? OP_ADD : OP_SUB;
if (!linearize_address_gen(ep, expr->unop, &ad))
return VOID;
old = linearize_load_gen(ep, &ad);
op = opcode_float(op, expr->ctype);
if (is_float_type(expr->ctype))
one = add_setfval(ep, expr->ctype, expr->op_value);
else
one = value_pseudo(expr->op_value);
if (ad.btype != ad.type)
old = cast_pseudo(ep, old, ad.type, ad.btype);
new = add_binary_op(ep, ad.btype, op, old, one);
if (ad.btype != ad.type)
new = cast_pseudo(ep, new, ad.btype, ad.type);
linearize_store_gen(ep, new, &ad);
return postop ? old : new;
}
static pseudo_t add_unop(struct entrypoint *ep, struct symbol *ctype, int op, pseudo_t src)
{
struct instruction *insn = alloc_typed_instruction(op, ctype);
pseudo_t new = alloc_pseudo(insn);
insn->target = new;
use_pseudo(insn, src, &insn->src1);
add_one_insn(ep, insn);
return new;
}
static pseudo_t add_cast(struct entrypoint *ep, struct symbol *to,
struct symbol *from, int op, pseudo_t src)
{
pseudo_t new = add_unop(ep, to, op, src);
new->def->orig_type = from;
return new;
}
static pseudo_t linearize_slice(struct entrypoint *ep, struct expression *expr)
{
pseudo_t pre = linearize_expression(ep, expr->base);
struct instruction *insn = alloc_typed_instruction(OP_SLICE, expr->ctype);
pseudo_t new = alloc_pseudo(insn);
insn->target = new;
insn->from = expr->r_bitpos;
insn->orig_type = expr->base->ctype;
use_pseudo(insn, pre, &insn->src);
add_one_insn(ep, insn);
return new;
}
static pseudo_t linearize_regular_preop(struct entrypoint *ep, struct expression *expr)
{
pseudo_t pre = linearize_expression(ep, expr->unop);
struct symbol *ctype = expr->ctype;
switch (expr->op) {
case '+':
return pre;
case '!': {
pseudo_t zero = value_pseudo(0);
return add_cmp_op(ep, ctype, OP_SET_EQ, expr->unop->ctype, pre, zero);
}
case '~':
return add_unop(ep, ctype, OP_NOT, pre);
case '-':
return add_unop(ep, ctype, opcode_float(OP_NEG, ctype), pre);
}
return VOID;
}
static pseudo_t linearize_preop(struct entrypoint *ep, struct expression *expr)
{
/*
* '*' is an lvalue access, and is fundamentally different
* from an arithmetic operation. Maybe it should have an
* expression type of its own..
*/
if (expr->op == '*')
return linearize_access(ep, expr);
if (expr->op == SPECIAL_INCREMENT || expr->op == SPECIAL_DECREMENT)
return linearize_inc_dec(ep, expr, 0);
return linearize_regular_preop(ep, expr);
}
static pseudo_t linearize_postop(struct entrypoint *ep, struct expression *expr)
{
return linearize_inc_dec(ep, expr, 1);
}
/*
* Casts to pointers are "less safe" than other casts, since
* they imply type-unsafe accesses. "void *" is a special
* case, since you can't access through it anyway without another
* cast.
*/
enum mtype {
MTYPE_UINT,
MTYPE_SINT,
MTYPE_PTR,
MTYPE_VPTR, // TODO: must be removed ?
MTYPE_FLOAT,
MTYPE_BAD,
};
static enum mtype get_mtype(struct symbol *s)
{
int sign = (s->ctype.modifiers & MOD_SIGNED) ? 1 : 0;
retry: switch (s->type) {
case SYM_NODE:
s = s->ctype.base_type;
goto retry;
case SYM_PTR:
if (s->ctype.base_type == &void_ctype)
return MTYPE_VPTR;
return MTYPE_PTR;
case SYM_BITFIELD:
case SYM_RESTRICT:
case SYM_FOULED:
case SYM_ENUM:
s = s->ctype.base_type;
/* fall-through */
case_int:
return sign ? MTYPE_SINT : MTYPE_UINT;
case SYM_BASETYPE:
if (s->ctype.base_type == &fp_type)
return MTYPE_FLOAT;
if (s->ctype.base_type == &int_type)
goto case_int;
/* fall-through */
default:
return MTYPE_BAD;
}
}
static int get_cast_opcode(struct symbol *dst, struct symbol *src)
{
enum mtype stype = get_mtype(src);
enum mtype dtype = get_mtype(dst);
switch (dtype) {
case MTYPE_FLOAT:
switch (stype) {
case MTYPE_FLOAT:
if (dst->bit_size == src->bit_size)
return OP_NOP;
return OP_FCVTF;
case MTYPE_UINT:
return OP_UCVTF;
case MTYPE_SINT:
return OP_SCVTF;
default:
return OP_BADOP;
}
case MTYPE_PTR:
switch (stype) {
case MTYPE_UINT:
case MTYPE_SINT:
return OP_UTPTR;
case MTYPE_PTR:
case MTYPE_VPTR:
return OP_PTRCAST;
default:
return OP_BADOP;
}
case MTYPE_VPTR:
switch (stype) {
case MTYPE_PTR:
case MTYPE_VPTR:
case MTYPE_UINT:
stype = MTYPE_UINT;
/* fall through */
case MTYPE_SINT:
break;
default:
return OP_BADOP;
}
/* fall through */
case MTYPE_UINT:
case MTYPE_SINT:
switch (stype) {
case MTYPE_FLOAT:
return dtype == MTYPE_UINT ? OP_FCVTU : OP_FCVTS;
case MTYPE_PTR:
return OP_PTRTU;
case MTYPE_VPTR:
case MTYPE_UINT:
case MTYPE_SINT:
if (dst->bit_size ==src->bit_size)
return OP_NOP;
if (dst->bit_size < src->bit_size)
return OP_TRUNC;
return stype == MTYPE_SINT ? OP_SEXT : OP_ZEXT;
default:
return OP_BADOP;
}
/* fall through */
default:
if (src->type == SYM_NODE)
src = src->ctype.base_type;
if (dst->type == SYM_NODE)
dst = dst->ctype.base_type;
if (src == dst)
return OP_NOP;
return OP_BADOP;
}
}
static pseudo_t cast_pseudo(struct entrypoint *ep, pseudo_t src, struct symbol *from, struct symbol *to)
{
const struct position pos = current_pos;
pseudo_t result;
struct instruction *insn;
int opcode;
if (src == VOID)
return VOID;
if (!from || !to)
return VOID;
if (from->bit_size < 0 || to->bit_size < 0)
return VOID;
opcode = get_cast_opcode(to, from);
switch (opcode) {
case OP_NOP:
return src;
case OP_UTPTR:
if (from->bit_size == to->bit_size)
break;
if (src == value_pseudo(0))
break;
if (Wint_to_pointer_cast)
warning(pos, "non size-preserving integer to pointer cast");
src = cast_pseudo(ep, src, from, size_t_ctype);
from = size_t_ctype;
break;
case OP_PTRTU:
if (from->bit_size == to->bit_size)
break;
if (Wpointer_to_int_cast)
warning(pos, "non size-preserving pointer to integer cast");
src = cast_pseudo(ep, src, from, size_t_ctype);
return cast_pseudo(ep, src, size_t_ctype, to);
case OP_BADOP:
return VOID;
default:
break;
}
insn = alloc_typed_instruction(opcode, to);
result = alloc_pseudo(insn);
insn->target = result;
insn->orig_type = from;
use_pseudo(insn, src, &insn->src);
add_one_insn(ep, insn);
return result;
}
static int map_opcode(int opcode, struct symbol *ctype)
{
if (ctype && is_float_type(ctype))
return opcode_table[opcode].to_float;
if (ctype && (ctype->ctype.modifiers & MOD_SIGNED)) {
switch(opcode) {
case OP_DIVU: case OP_MODU: case OP_LSR:
opcode++;
}
}
return opcode;
}
static inline pseudo_t add_convert_to_bool(struct entrypoint *ep, pseudo_t src, struct symbol *type)
{
pseudo_t zero;
int op;
if (!type || src == VOID)
return VOID;
if (is_bool_type(type))
return src;
if (src->type == PSEUDO_VAL && (src->value == 0 || src->value == 1))
return src;
if (is_float_type(type)) {
zero = add_setfval(ep, type, 0.0);
op = map_opcode(OP_SET_NE, type);
} else {
zero = value_pseudo(0);
op = OP_SET_NE;
}
return add_cmp_op(ep, &bool_ctype, op, type, src, zero);
}
static pseudo_t linearize_expression_to_bool(struct entrypoint *ep, struct expression *expr)
{
pseudo_t dst;
dst = linearize_expression(ep, expr);
dst = add_convert_to_bool(ep, dst, expr->ctype);
return dst;
}
static pseudo_t linearize_assignment(struct entrypoint *ep, struct expression *expr)
{
struct access_data ad = { NULL, };
struct expression *target = expr->left;
struct expression *src = expr->right;
struct symbol *ctype;
pseudo_t value;
value = linearize_expression(ep, src);
if (!target || !linearize_address_gen(ep, target, &ad))
return value;
if (expr->op != '=') {
pseudo_t oldvalue = linearize_load_gen(ep, &ad);
pseudo_t dst;
static const int op_trans[] = {
[SPECIAL_ADD_ASSIGN - SPECIAL_BASE] = OP_ADD,
[SPECIAL_SUB_ASSIGN - SPECIAL_BASE] = OP_SUB,
[SPECIAL_MUL_ASSIGN - SPECIAL_BASE] = OP_MUL,
[SPECIAL_DIV_ASSIGN - SPECIAL_BASE] = OP_DIVU,
[SPECIAL_MOD_ASSIGN - SPECIAL_BASE] = OP_MODU,
[SPECIAL_SHL_ASSIGN - SPECIAL_BASE] = OP_SHL,
[SPECIAL_SHR_ASSIGN - SPECIAL_BASE] = OP_LSR,
[SPECIAL_AND_ASSIGN - SPECIAL_BASE] = OP_AND,
[SPECIAL_OR_ASSIGN - SPECIAL_BASE] = OP_OR,
[SPECIAL_XOR_ASSIGN - SPECIAL_BASE] = OP_XOR
};
int opcode;
if (!src)
return VOID;
ctype = src->ctype;
oldvalue = cast_pseudo(ep, oldvalue, target->ctype, ctype);
opcode = map_opcode(op_trans[expr->op - SPECIAL_BASE], ctype);
dst = add_binary_op(ep, ctype, opcode, oldvalue, value);
taint_undefined_behaviour(dst->def);
value = cast_pseudo(ep, dst, ctype, expr->ctype);
}
value = linearize_store_gen(ep, value, &ad);
return value;
}
static pseudo_t linearize_call_expression(struct entrypoint *ep, struct expression *expr)
{
struct expression *arg, *fn;
struct instruction *insn;
pseudo_t retval, call;
struct ctype *ctype = NULL;
struct symbol *fntype;
struct context *context;
if (!expr->ctype)
return VOID;
fn = expr->fn;
fntype = fn->ctype;
// handle builtins
if (fntype->op && fntype->op->linearize) {
retval = fntype->op->linearize(ep, expr);
if (retval)
return retval;
}
ctype = &fntype->ctype;
insn = alloc_typed_instruction(OP_CALL, expr->ctype);
add_symbol(&insn->fntypes, fntype);
FOR_EACH_PTR(expr->args, arg) {
pseudo_t new = linearize_expression(ep, arg);
use_pseudo(insn, new, add_pseudo(&insn->arguments, new));
add_symbol(&insn->fntypes, arg->ctype);
} END_FOR_EACH_PTR(arg);
if (fn->type == EXPR_PREOP && fn->op == '*' && is_func_type(fn->ctype))
fn = fn->unop;
if (fn->type == EXPR_SYMBOL) {
call = symbol_pseudo(ep, fn->symbol);
} else {
call = linearize_expression(ep, fn);
}
use_pseudo(insn, call, &insn->func);
retval = VOID;
if (expr->ctype != &void_ctype)
retval = alloc_pseudo(insn);
insn->target = retval;
add_one_insn(ep, insn);
if (ctype) {
FOR_EACH_PTR(ctype->contexts, context) {
int in = context->in;
int out = context->out;
int check = 0;
int context_diff;
if (in < 0) {
check = 1;
in = 0;
}
if (out < 0) {
check = 0;
out = 0;
}
context_diff = out - in;
if (check || context_diff) {
insn = alloc_instruction(OP_CONTEXT, 0);
insn->increment = context_diff;
insn->check = check;
insn->context_expr = context->context;
add_one_insn(ep, insn);
}
} END_FOR_EACH_PTR(context);
if (ctype->modifiers & MOD_NORETURN)
add_unreachable(ep);
}
return retval;
}
static pseudo_t linearize_binop_bool(struct entrypoint *ep, struct expression *expr)
{
pseudo_t src1, src2, dst;
int op = (expr->op == SPECIAL_LOGICAL_OR) ? OP_OR : OP_AND;
src1 = linearize_expression_to_bool(ep, expr->left);
src2 = linearize_expression_to_bool(ep, expr->right);
dst = add_binary_op(ep, &bool_ctype, op, src1, src2);
if (expr->ctype != &bool_ctype)
dst = cast_pseudo(ep, dst, &bool_ctype, expr->ctype);
return dst;
}
static pseudo_t linearize_binop(struct entrypoint *ep, struct expression *expr)
{
pseudo_t src1, src2, dst;
static const int opcode[] = {
['+'] = OP_ADD, ['-'] = OP_SUB,
['*'] = OP_MUL, ['/'] = OP_DIVU,
['%'] = OP_MODU, ['&'] = OP_AND,
['|'] = OP_OR, ['^'] = OP_XOR,
[SPECIAL_LEFTSHIFT] = OP_SHL,
[SPECIAL_RIGHTSHIFT] = OP_LSR,
};
int op;
src1 = linearize_expression(ep, expr->left);
src2 = linearize_expression(ep, expr->right);
op = map_opcode(opcode[expr->op], expr->ctype);
dst = add_binary_op(ep, expr->ctype, op, src1, src2);
taint_undefined_behaviour(dst->def);
return dst;
}
static pseudo_t linearize_logical_branch(struct entrypoint *ep, struct expression *expr, struct basic_block *bb_true, struct basic_block *bb_false);
static pseudo_t linearize_cond_branch(struct entrypoint *ep, struct expression *expr, struct basic_block *bb_true, struct basic_block *bb_false);
static pseudo_t linearize_select(struct entrypoint *ep, struct expression *expr)
{
pseudo_t cond, valt, valf, res;
struct instruction *insn;
valt = linearize_expression(ep, expr->cond_true);
valf = linearize_expression(ep, expr->cond_false);
cond = linearize_expression(ep, expr->conditional);
insn = alloc_typed_instruction(OP_SEL, expr->ctype);
if (!expr->cond_true)
valt = cond;
use_pseudo(insn, cond, &insn->src1);
use_pseudo(insn, valt, &insn->src2);
use_pseudo(insn, valf, &insn->src3);
res = alloc_pseudo(insn);
insn->target = res;
add_one_insn(ep, insn);
return res;
}
static pseudo_t add_join_conditional(struct entrypoint *ep, struct expression *expr,
pseudo_t phi1, pseudo_t phi2)
{
pseudo_t target;
struct instruction *phi_node;
if (phi1 == VOID)
return (phi2 == VOID) ? phi2 : phi2->def->src;
if (phi2 == VOID)
return (phi1 == VOID) ? phi1 : phi1->def->src;
phi_node = alloc_typed_instruction(OP_PHI, expr->ctype);
link_phi(phi_node, phi1);
link_phi(phi_node, phi2);
phi_node->target = target = alloc_pseudo(phi_node);
add_one_insn(ep, phi_node);
return target;
}
static pseudo_t linearize_short_conditional(struct entrypoint *ep, struct expression *expr,
struct expression *cond,
struct expression *expr_false)
{
pseudo_t src1, src2;
struct basic_block *bb_false;
struct basic_block *merge;
pseudo_t phi1, phi2;
if (!expr_false || !ep->active)
return VOID;
bb_false = alloc_basic_block(ep, expr_false->pos);
merge = alloc_basic_block(ep, expr->pos);
src1 = linearize_expression(ep, cond);
phi1 = alloc_phi(ep->active, src1, expr->ctype);
add_branch(ep, src1, merge, bb_false);
set_activeblock(ep, bb_false);
src2 = linearize_expression(ep, expr_false);
phi2 = alloc_phi(ep->active, src2, expr->ctype);
set_activeblock(ep, merge);
return add_join_conditional(ep, expr, phi1, phi2);
}
static pseudo_t linearize_conditional(struct entrypoint *ep, struct expression *expr,
struct expression *cond,
struct expression *expr_true,
struct expression *expr_false)
{
pseudo_t src1, src2;
pseudo_t phi1, phi2;
struct basic_block *bb_true, *bb_false, *merge;
if (!cond || !expr_true || !expr_false || !ep->active)
return VOID;
bb_true = alloc_basic_block(ep, expr_true->pos);
bb_false = alloc_basic_block(ep, expr_false->pos);
merge = alloc_basic_block(ep, expr->pos);
linearize_cond_branch(ep, cond, bb_true, bb_false);
set_activeblock(ep, bb_true);
src1 = linearize_expression(ep, expr_true);
phi1 = alloc_phi(ep->active, src1, expr->ctype);
add_goto(ep, merge);
set_activeblock(ep, bb_false);
src2 = linearize_expression(ep, expr_false);
phi2 = alloc_phi(ep->active, src2, expr->ctype);
set_activeblock(ep, merge);
return add_join_conditional(ep, expr, phi1, phi2);
}
static void insert_phis(struct basic_block *bb, pseudo_t src, struct symbol *ctype,
struct instruction *node)
{
struct basic_block *parent;
FOR_EACH_PTR(bb->parents, parent) {
struct instruction *phisrc = alloc_phisrc(src, ctype);
insert_last_instruction(parent, phisrc);
link_phi(node, phisrc->target);
} END_FOR_EACH_PTR(parent);
}
static pseudo_t linearize_logical(struct entrypoint *ep, struct expression *expr)
{
struct symbol *ctype = expr->ctype;
struct basic_block *other, *merge;
struct instruction *node;
pseudo_t src1, src2, phi2;
if (!ep->active || !expr->left || !expr->right)
return VOID;
other = alloc_basic_block(ep, expr->right->pos);
merge = alloc_basic_block(ep, expr->pos);
node = alloc_phi_node(merge, ctype, NULL);
// LHS and its shortcut
if (expr->op == SPECIAL_LOGICAL_OR) {
linearize_cond_branch(ep, expr->left, merge, other);
src1 = value_pseudo(1);
} else {
linearize_cond_branch(ep, expr->left, other, merge);
src1 = value_pseudo(0);
}
insert_phis(merge, src1, ctype, node);
// RHS
set_activeblock(ep, other);
src2 = linearize_expression_to_bool(ep, expr->right);
src2 = cast_pseudo(ep, src2, &bool_ctype, ctype);
phi2 = alloc_phi(ep->active, src2, ctype);
link_phi(node, phi2);
// join
set_activeblock(ep, merge);
add_instruction(&merge->insns, node);
return node->target;
}
static pseudo_t linearize_compare(struct entrypoint *ep, struct expression *expr)
{
static const int cmpop[] = {
['>'] = OP_SET_GT, ['<'] = OP_SET_LT,
[SPECIAL_EQUAL] = OP_SET_EQ,
[SPECIAL_NOTEQUAL] = OP_SET_NE,
[SPECIAL_GTE] = OP_SET_GE,
[SPECIAL_LTE] = OP_SET_LE,
[SPECIAL_UNSIGNED_LT] = OP_SET_B,
[SPECIAL_UNSIGNED_GT] = OP_SET_A,
[SPECIAL_UNSIGNED_LTE] = OP_SET_BE,
[SPECIAL_UNSIGNED_GTE] = OP_SET_AE,
};
struct symbol *itype = expr->right->ctype;
int op = opcode_float(cmpop[expr->op], itype);
pseudo_t src1 = linearize_expression(ep, expr->left);
pseudo_t src2 = linearize_expression(ep, expr->right);
pseudo_t dst = add_cmp_op(ep, expr->ctype, op, itype, src1, src2);
return dst;
}
static pseudo_t linearize_cond_branch(struct entrypoint *ep, struct expression *expr, struct basic_block *bb_true, struct basic_block *bb_false)
{
pseudo_t cond;
if (!expr || !valid_type(expr->ctype) || !bb_reachable(ep->active))
return VOID;
switch (expr->type) {
case EXPR_STRING:
case EXPR_VALUE:
add_goto(ep, expr->value ? bb_true : bb_false);
break;
case EXPR_FVALUE:
add_goto(ep, expr->fvalue ? bb_true : bb_false);
break;
case EXPR_LOGICAL:
linearize_logical_branch(ep, expr, bb_true, bb_false);
break;
case EXPR_COMPARE:
cond = linearize_compare(ep, expr);
add_branch(ep, cond, bb_true, bb_false);
break;
case EXPR_PREOP:
if (expr->op == '!')
return linearize_cond_branch(ep, expr->unop, bb_false, bb_true);
/* fall through */
default:
cond = linearize_expression_to_bool(ep, expr);
add_branch(ep, cond, bb_true, bb_false);
break;
}
return VOID;
}
static pseudo_t linearize_logical_branch(struct entrypoint *ep, struct expression *expr, struct basic_block *bb_true, struct basic_block *bb_false)
{
struct basic_block *next = alloc_basic_block(ep, expr->pos);
if (expr->op == SPECIAL_LOGICAL_OR)
linearize_cond_branch(ep, expr->left, bb_true, next);
else
linearize_cond_branch(ep, expr->left, next, bb_false);
set_activeblock(ep, next);
linearize_cond_branch(ep, expr->right, bb_true, bb_false);
return VOID;
}
static pseudo_t linearize_cast(struct entrypoint *ep, struct expression *expr)
{
pseudo_t src;
struct expression *orig = expr->cast_expression;
if (!orig)
return VOID;
src = linearize_expression(ep, orig);
return cast_pseudo(ep, src, orig->ctype, expr->ctype);
}
static pseudo_t linearize_initializer(struct entrypoint *ep, struct expression *initializer, struct access_data *ad)
{
switch (initializer->type) {
case EXPR_INITIALIZER: {
struct expression *expr;
FOR_EACH_PTR(initializer->expr_list, expr) {
linearize_initializer(ep, expr, ad);
} END_FOR_EACH_PTR(expr);
break;
}
case EXPR_POS:
ad->offset = initializer->init_offset;
linearize_initializer(ep, initializer->init_expr, ad);
break;
default: {
pseudo_t value = linearize_expression(ep, initializer);
ad->type = initializer->ctype;
linearize_store_gen(ep, value, ad);
return value;
}
}
return VOID;
}
static void linearize_argument(struct entrypoint *ep, struct symbol *arg, int nr)
{
struct access_data ad = { NULL, };
ad.type = arg;
ad.address = symbol_pseudo(ep, arg);
linearize_store_gen(ep, argument_pseudo(ep, nr), &ad);
}
static pseudo_t linearize_expression(struct entrypoint *ep, struct expression *expr)
{
if (!expr || !valid_type(expr->ctype))
return VOID;
current_pos = expr->pos;
switch (expr->type) {
case EXPR_SYMBOL:
linearize_one_symbol(ep, expr->symbol);
return add_symbol_address(ep, expr);
case EXPR_VALUE:
return value_pseudo(expr->value);
case EXPR_STRING:
case EXPR_LABEL:
return add_setval(ep, expr->ctype, expr);
case EXPR_FVALUE:
return add_setfval(ep, expr->ctype, expr->fvalue);
case EXPR_STATEMENT:
return linearize_statement(ep, expr->statement);
case EXPR_CALL:
return linearize_call_expression(ep, expr);
case EXPR_BINOP:
if (expr->op == SPECIAL_LOGICAL_AND || expr->op == SPECIAL_LOGICAL_OR)
return linearize_binop_bool(ep, expr);
return linearize_binop(ep, expr);
case EXPR_LOGICAL:
return linearize_logical(ep, expr);
case EXPR_COMPARE:
return linearize_compare(ep, expr);
case EXPR_SELECT:
return linearize_select(ep, expr);
case EXPR_CONDITIONAL:
if (!expr->cond_true)
return linearize_short_conditional(ep, expr, expr->conditional, expr->cond_false);
return linearize_conditional(ep, expr, expr->conditional,
expr->cond_true, expr->cond_false);
case EXPR_COMMA:
linearize_expression(ep, expr->left);
return linearize_expression(ep, expr->right);
case EXPR_ASSIGNMENT:
return linearize_assignment(ep, expr);
case EXPR_PREOP:
return linearize_preop(ep, expr);
case EXPR_POSTOP:
return linearize_postop(ep, expr);
case EXPR_CAST:
case EXPR_FORCE_CAST:
case EXPR_IMPLIED_CAST:
return linearize_cast(ep, expr);
case EXPR_SLICE:
return linearize_slice(ep, expr);
case EXPR_INITIALIZER:
case EXPR_POS:
warning(expr->pos, "unexpected initializer expression (%d %d)", expr->type, expr->op);
return VOID;
default:
warning(expr->pos, "unknown expression (%d %d)", expr->type, expr->op);
return VOID;
}
return VOID;
}
static pseudo_t linearize_one_symbol(struct entrypoint *ep, struct symbol *sym)
{
struct access_data ad = { NULL, };
pseudo_t value;
if (!sym || !sym->initializer || sym->initialized)
return VOID;
/* We need to output these puppies some day too.. */
if (sym->ctype.modifiers & (MOD_STATIC | MOD_TOPLEVEL))
return VOID;
sym->initialized = 1;
ad.address = symbol_pseudo(ep, sym);
if (sym->initializer && !is_scalar_type(sym)) {
// default zero initialization [6.7.9.21]
// FIXME: this init the whole aggregate while
// only the existing fields need to be initialized.
// FIXME: this init the whole aggregate even if
// all fields arelater explicitely initialized.
ad.type = sym;
ad.address = symbol_pseudo(ep, sym);
linearize_store_gen(ep, value_pseudo(0), &ad);
}
value = linearize_initializer(ep, sym->initializer, &ad);
return value;
}
static pseudo_t linearize_compound_statement(struct entrypoint *ep, struct statement *stmt)
{
pseudo_t pseudo;
struct statement *s;
pseudo = VOID;
FOR_EACH_PTR(stmt->stmts, s) {
pseudo = linearize_statement(ep, s);
} END_FOR_EACH_PTR(s);
return pseudo;
}
static void add_return(struct entrypoint *ep, struct basic_block *bb, struct symbol *ctype, pseudo_t src)
{
struct instruction *phi_node = first_instruction(bb->insns);
pseudo_t phi;
if (!phi_node) {
phi_node = alloc_typed_instruction(OP_PHI, ctype);
phi_node->target = alloc_pseudo(phi_node);
phi_node->bb = bb;
add_instruction(&bb->insns, phi_node);
}
phi = alloc_phi(ep->active, src, ctype);
phi->ident = &return_ident;
link_phi(phi_node, phi);
}
static pseudo_t linearize_fn_statement(struct entrypoint *ep, struct statement *stmt)
{
struct instruction *phi_node;
struct basic_block *bb;
pseudo_t pseudo;
pseudo = linearize_compound_statement(ep, stmt);
if (!is_void_type(stmt->ret)) { // non-void function
struct basic_block *active = ep->active;
if (active && !bb_terminated(active)) { // missing return
struct basic_block *bb_ret;
bb_ret = get_bound_block(ep, stmt->ret);
add_return(ep, bb_ret, stmt->ret, undef_pseudo());
}
}
bb = add_label(ep, stmt->ret);
phi_node = first_instruction(bb->insns);
if (phi_node)
pseudo = phi_node->target;
return pseudo;
}
static pseudo_t linearize_inlined_call(struct entrypoint *ep, struct statement *stmt)
{
struct instruction *insn = alloc_instruction(OP_INLINED_CALL, 0);
struct statement *args = stmt->args;
struct basic_block *bb;
pseudo_t pseudo;
if (args) {
struct symbol *sym;
concat_symbol_list(args->declaration, &ep->syms);
FOR_EACH_PTR(args->declaration, sym) {
pseudo_t value = linearize_one_symbol(ep, sym);
add_pseudo(&insn->arguments, value);
} END_FOR_EACH_PTR(sym);
}
pseudo = linearize_fn_statement(ep, stmt);
insn->target = pseudo;
insn->func = symbol_pseudo(ep, stmt->inline_fn);
bb = ep->active;
if (!bb->insns)
bb->pos = stmt->pos;
add_one_insn(ep, insn);
return pseudo;
}
static pseudo_t linearize_context(struct entrypoint *ep, struct statement *stmt)
{
struct instruction *insn = alloc_instruction(OP_CONTEXT, 0);
struct expression *expr = stmt->expression;
insn->increment = get_expression_value(expr);
insn->context_expr = stmt->context;
add_one_insn(ep, insn);
return VOID;
}
static pseudo_t linearize_range(struct entrypoint *ep, struct statement *stmt)
{
struct instruction *insn = alloc_instruction(OP_RANGE, 0);
use_pseudo(insn, linearize_expression(ep, stmt->range_expression), &insn->src1);
use_pseudo(insn, linearize_expression(ep, stmt->range_low), &insn->src2);
use_pseudo(insn, linearize_expression(ep, stmt->range_high), &insn->src3);
add_one_insn(ep, insn);
return VOID;
}
ALLOCATOR(asm_rules, "asm rules");
ALLOCATOR(asm_constraint, "asm constraints");
static void add_asm_rule(struct instruction *insn, struct asm_constraint_list **list, struct asm_operand *op, pseudo_t pseudo)
{
struct asm_constraint *rule = __alloc_asm_constraint(0);
rule->is_memory = op->is_memory;
rule->ident = op->name;
rule->constraint = op->constraint ? op->constraint->string->data : "";
use_pseudo(insn, pseudo, &rule->pseudo);
add_ptr_list(list, rule);
}
static void add_asm_input(struct entrypoint *ep, struct instruction *insn, struct asm_operand *op)
{
pseudo_t pseudo = linearize_expression(ep, op->expr);
add_asm_rule(insn, &insn->asm_rules->inputs, op, pseudo);
}
static void add_asm_output_address(struct entrypoint *ep, struct instruction *insn, struct asm_operand *op)
{
pseudo_t pseudo;
if (!op->is_memory)
return;
pseudo = linearize_expression(ep, op->expr);
add_asm_rule(insn, &insn->asm_rules->outputs, op, pseudo);
insn->output_memory = 1;
}
static void add_asm_output(struct entrypoint *ep, struct instruction *insn, struct asm_operand *op)
{
struct access_data ad = { NULL, };
pseudo_t pseudo;
if (op->is_memory)
return;
if (!linearize_address_gen(ep, op->expr, &ad))
return;
pseudo = alloc_pseudo(insn);
linearize_store_gen(ep, pseudo, &ad);
add_asm_rule(insn, &insn->asm_rules->outputs, op, pseudo);
}
static pseudo_t linearize_asm_statement(struct entrypoint *ep, struct statement *stmt)
{
struct instruction *insn;
struct expression *expr, *clob;
struct asm_rules *rules;
struct asm_operand *op;
insn = alloc_instruction(OP_ASM, 0);
expr = stmt->asm_string;
if (!expr || expr->type != EXPR_STRING) {
warning(stmt->pos, "expected string in inline asm");
return VOID;
}
insn->string = expr->string->data;
rules = __alloc_asm_rules(0);
insn->asm_rules = rules;
/* Gather the inputs.. */
FOR_EACH_PTR(stmt->asm_inputs, op) {
add_asm_input(ep, insn, op);
} END_FOR_EACH_PTR(op);
/* ... and the addresses for memory outputs */
FOR_EACH_PTR(stmt->asm_outputs, op) {
add_asm_output_address(ep, insn, op);
} END_FOR_EACH_PTR(op);
add_one_insn(ep, insn);
/* Assign the outputs */
FOR_EACH_PTR(stmt->asm_outputs, op) {
add_asm_output(ep, insn, op);
} END_FOR_EACH_PTR(op);
/* and finally, look if it clobbers memory */
FOR_EACH_PTR(stmt->asm_clobbers, clob) {
if (!strcmp(clob->string->data, "memory"))
insn->clobber_memory = 1;
} END_FOR_EACH_PTR(clob);
return VOID;
}
static int multijmp_cmp(const void *_a, const void *_b)
{
const struct multijmp *a = _a;
const struct multijmp *b = _b;
// "default" case?
if (a->begin > a->end) {
if (b->begin > b->end)
return 0;
return 1;
}
if (b->begin > b->end)
return -1;
if (a->begin == b->begin) {
if (a->end == b->end)
return 0;
return (a->end < b->end) ? -1 : 1;
}
return a->begin < b->begin ? -1 : 1;
}
static void sort_switch_cases(struct instruction *insn)
{
sort_list((struct ptr_list **)&insn->multijmp_list, multijmp_cmp);
}
static pseudo_t linearize_declaration(struct entrypoint *ep, struct statement *stmt)
{
struct symbol *sym;
concat_symbol_list(stmt->declaration, &ep->syms);
FOR_EACH_PTR(stmt->declaration, sym) {
linearize_one_symbol(ep, sym);
} END_FOR_EACH_PTR(sym);
return VOID;
}
static pseudo_t linearize_return(struct entrypoint *ep, struct statement *stmt)
{
struct expression *expr = stmt->expression;
struct symbol *ret = stmt->ret_target;
struct basic_block *bb_return = get_bound_block(ep, ret);
struct basic_block *active;
pseudo_t src = linearize_expression(ep, expr);
active = ep->active;
if (active && !is_void_type(ret)) {
add_return(ep, bb_return, ret, src);
}
add_goto(ep, bb_return);
return VOID;
}
static pseudo_t linearize_switch(struct entrypoint *ep, struct statement *stmt)
{
struct symbol *sym;
struct instruction *switch_ins;
struct basic_block *switch_end = alloc_basic_block(ep, stmt->pos);
struct basic_block *active, *default_case;
struct expression *expr = stmt->switch_expression;
struct multijmp *jmp;
pseudo_t pseudo;
if (!expr || !expr->ctype)
return VOID;
pseudo = linearize_expression(ep, expr);
active = ep->active;
if (!active) {
active = alloc_basic_block(ep, stmt->pos);
set_activeblock(ep, active);
}
switch_ins = alloc_typed_instruction(OP_SWITCH, expr->ctype);
use_pseudo(switch_ins, pseudo, &switch_ins->cond);
add_one_insn(ep, switch_ins);
finish_block(ep);
default_case = NULL;
FOR_EACH_PTR(stmt->switch_case->symbol_list, sym) {
struct statement *case_stmt = sym->stmt;
struct basic_block *bb_case = get_bound_block(ep, sym);
if (!case_stmt->case_expression) {
default_case = bb_case;
continue;
} else if (case_stmt->case_expression->type != EXPR_VALUE) {
continue;
} else {
struct expression *case_to = case_stmt->case_to;
long long begin, end;
begin = end = case_stmt->case_expression->value;
if (case_to && case_to->type == EXPR_VALUE)
end = case_to->value;
if (begin > end)
jmp = alloc_multijmp(bb_case, end, begin);
else
jmp = alloc_multijmp(bb_case, begin, end);
}
add_multijmp(&switch_ins->multijmp_list, jmp);
add_bb(&bb_case->parents, active);
add_bb(&active->children, bb_case);
} END_FOR_EACH_PTR(sym);
bind_label(stmt->switch_break, switch_end, stmt->pos);
/* And linearize the actual statement */
linearize_statement(ep, stmt->switch_statement);
set_activeblock(ep, switch_end);
if (!default_case)
default_case = switch_end;
jmp = alloc_multijmp(default_case, 1, 0);
add_multijmp(&switch_ins->multijmp_list, jmp);
add_bb(&default_case->parents, active);
add_bb(&active->children, default_case);
sort_switch_cases(switch_ins);
return VOID;
}
static pseudo_t linearize_iterator(struct entrypoint *ep, struct statement *stmt)
{
struct statement *pre_statement = stmt->iterator_pre_statement;
struct expression *pre_condition = stmt->iterator_pre_condition;
struct statement *statement = stmt->iterator_statement;
struct statement *post_statement = stmt->iterator_post_statement;
struct expression *post_condition = stmt->iterator_post_condition;
struct basic_block *loop_top, *loop_body, *loop_continue, *loop_end;
struct symbol *sym;
FOR_EACH_PTR(stmt->iterator_syms, sym) {
linearize_one_symbol(ep, sym);
} END_FOR_EACH_PTR(sym);
concat_symbol_list(stmt->iterator_syms, &ep->syms);
linearize_statement(ep, pre_statement);
loop_body = loop_top = alloc_basic_block(ep, stmt->pos);
loop_continue = alloc_basic_block(ep, stmt->pos);
loop_end = alloc_basic_block(ep, stmt->pos);
/* An empty post-condition means that it's the same as the pre-condition */
if (!post_condition) {
loop_top = alloc_basic_block(ep, stmt->pos);
set_activeblock(ep, loop_top);
}
if (pre_condition)
linearize_cond_branch(ep, pre_condition, loop_body, loop_end);
bind_label(stmt->iterator_continue, loop_continue, stmt->pos);
bind_label(stmt->iterator_break, loop_end, stmt->pos);
set_activeblock(ep, loop_body);
linearize_statement(ep, statement);
add_goto(ep, loop_continue);
set_activeblock(ep, loop_continue);
linearize_statement(ep, post_statement);
if (!post_condition)
add_goto(ep, loop_top);
else
linearize_cond_branch(ep, post_condition, loop_top, loop_end);
set_activeblock(ep, loop_end);
return VOID;
}
static pseudo_t linearize_statement(struct entrypoint *ep, struct statement *stmt)
{
struct basic_block *bb;
if (!stmt)
return VOID;
bb = ep->active;
if (bb && !bb->insns)
bb->pos = stmt->pos;
current_pos = stmt->pos;
switch (stmt->type) {
case STMT_NONE:
break;
case STMT_DECLARATION:
return linearize_declaration(ep, stmt);
case STMT_CONTEXT:
return linearize_context(ep, stmt);
case STMT_RANGE:
return linearize_range(ep, stmt);
case STMT_EXPRESSION:
return linearize_expression(ep, stmt->expression);
case STMT_ASM:
return linearize_asm_statement(ep, stmt);
case STMT_RETURN:
return linearize_return(ep, stmt);
case STMT_CASE: {
add_label(ep, stmt->case_label);
linearize_statement(ep, stmt->case_statement);
break;
}
case STMT_LABEL: {
struct symbol *label = stmt->label_identifier;
if (label->used) {
add_label(ep, label);
}
return linearize_statement(ep, stmt->label_statement);
}
case STMT_GOTO: {
struct symbol *sym;
struct expression *expr;
struct instruction *goto_ins;
struct basic_block *active;
pseudo_t pseudo;
active = ep->active;
if (!bb_reachable(active))
break;
if (stmt->goto_label) {
add_goto(ep, get_bound_block(ep, stmt->goto_label));
break;
}
expr = stmt->goto_expression;
if (!expr)
break;
/* This can happen as part of simplification */
if (expr->type == EXPR_LABEL) {
add_goto(ep, get_bound_block(ep, expr->label_symbol));
break;
}
pseudo = linearize_expression(ep, expr);
goto_ins = alloc_instruction(OP_COMPUTEDGOTO, 0);
use_pseudo(goto_ins, pseudo, &goto_ins->src);
add_one_insn(ep, goto_ins);
FOR_EACH_PTR(stmt->target_list, sym) {
struct basic_block *bb_computed = get_bound_block(ep, sym);
struct multijmp *jmp = alloc_multijmp(bb_computed, 1, 0);
add_multijmp(&goto_ins->multijmp_list, jmp);
add_bb(&bb_computed->parents, ep->active);
add_bb(&active->children, bb_computed);
} END_FOR_EACH_PTR(sym);
finish_block(ep);
break;
}
case STMT_COMPOUND:
if (stmt->inline_fn)
return linearize_inlined_call(ep, stmt);
return linearize_compound_statement(ep, stmt);
/*
* This could take 'likely/unlikely' into account, and
* switch the arms around appropriately..
*/
case STMT_IF: {
struct basic_block *bb_true, *bb_false, *endif;
struct expression *cond = stmt->if_conditional;
bb_true = alloc_basic_block(ep, stmt->pos);
bb_false = endif = alloc_basic_block(ep, stmt->pos);
// If the condition is invalid, the following
// statement(s) are not evaluated.
if (!cond || !valid_type(cond->ctype))
return VOID;
linearize_cond_branch(ep, cond, bb_true, bb_false);
set_activeblock(ep, bb_true);
linearize_statement(ep, stmt->if_true);
if (stmt->if_false) {
endif = alloc_basic_block(ep, stmt->pos);
add_goto(ep, endif);
set_activeblock(ep, bb_false);
linearize_statement(ep, stmt->if_false);
}
set_activeblock(ep, endif);
break;
}
case STMT_SWITCH:
return linearize_switch(ep, stmt);
case STMT_ITERATOR:
return linearize_iterator(ep, stmt);
default:
break;
}
return VOID;
}
static void check_tainted_insn(struct instruction *insn)
{
unsigned long long uval;
long long sval;
pseudo_t src2;
switch (insn->opcode) {
case OP_DIVU: case OP_DIVS:
case OP_MODU: case OP_MODS:
if (insn->src2 == value_pseudo(0))
warning(insn->pos, "divide by zero");
break;
case OP_SHL: case OP_LSR: case OP_ASR:
src2 = insn->src2;
if (src2->type != PSEUDO_VAL)
break;
uval = src2->value;
if (uval < insn->size)
break;
sval = sign_extend(uval, insn->size);
if (Wshift_count_negative && sval < 0)
warning(insn->pos, "shift count is negative (%lld)", sval);
else if (Wshift_count_overflow)
warning(insn->pos, "shift too big (%llu) for type %s", uval, show_typename(insn->type));
}
}
///
// issue warnings after all possible DCE
static void late_warnings(struct entrypoint *ep)
{
struct basic_block *bb;
FOR_EACH_PTR(ep->bbs, bb) {
struct instruction *insn;
FOR_EACH_PTR(bb->insns, insn) {
if (!insn->bb)
continue;
if (insn->tainted)
check_tainted_insn(insn);
switch (insn->opcode) {
case OP_LOAD:
// Check for illegal offsets.
check_access(insn);
break;
}
} END_FOR_EACH_PTR(insn);
} END_FOR_EACH_PTR(bb);
}
static struct entrypoint *linearize_fn(struct symbol *sym, struct symbol *base_type)
{
struct statement *stmt = base_type->stmt;
struct entrypoint *ep;
struct basic_block *bb;
struct symbol *ret_type;
struct symbol *arg;
struct instruction *entry;
struct instruction *ret;
pseudo_t result;
int i;
if (!stmt || sym->bogus_linear)
return NULL;
ep = alloc_entrypoint();
ep->name = sym;
sym->ep = ep;
bb = alloc_basic_block(ep, sym->pos);
set_activeblock(ep, bb);
if (stmt->type == STMT_ASM) { // top-level asm
linearize_asm_statement(ep, stmt);
return ep;
}
entry = alloc_instruction(OP_ENTRY, 0);
add_one_insn(ep, entry);
ep->entry = entry;
concat_symbol_list(base_type->arguments, &ep->syms);
/* FIXME!! We should do something else about varargs.. */
i = 0;
FOR_EACH_PTR(base_type->arguments, arg) {
linearize_argument(ep, arg, ++i);
} END_FOR_EACH_PTR(arg);
result = linearize_fn_statement(ep, stmt);
ret_type = base_type->ctype.base_type;
ret = alloc_typed_instruction(OP_RET, ret_type);
if (type_size(ret_type) > 0)
use_pseudo(ret, result, &ret->src);
add_one_insn(ep, ret);
optimize(ep);
late_warnings(ep);
return ep;
}
struct entrypoint *linearize_symbol(struct symbol *sym)
{
struct symbol *base_type;
if (!sym)
return NULL;
current_pos = sym->pos;
base_type = sym->ctype.base_type;
if (!base_type)
return NULL;
if (base_type->type == SYM_FN)
return linearize_fn(sym, base_type);
return NULL;
}
/*
* Builtin functions
*/
static pseudo_t linearize_fma(struct entrypoint *ep, struct expression *expr)
{
struct instruction *insn = alloc_typed_instruction(OP_FMADD, expr->ctype);
struct expression *arg;
PREPARE_PTR_LIST(expr->args, arg);
use_pseudo(insn, linearize_expression(ep, arg), &insn->src1);
NEXT_PTR_LIST(arg)
use_pseudo(insn, linearize_expression(ep, arg), &insn->src2);
NEXT_PTR_LIST(arg)
use_pseudo(insn, linearize_expression(ep, arg), &insn->src3);
FINISH_PTR_LIST(arg);
add_one_insn(ep, insn);
return insn->target = alloc_pseudo(insn);
}
static pseudo_t linearize_isdigit(struct entrypoint *ep, struct expression *expr)
{
struct instruction *insn;
pseudo_t src;
insn = alloc_typed_instruction(OP_SUB, &int_ctype);
src = linearize_expression(ep, first_expression(expr->args));
use_pseudo(insn, src, &insn->src1);
insn->src2 = value_pseudo('0');
src = insn->target = alloc_pseudo(insn);
add_one_insn(ep, insn);
insn = alloc_typed_instruction(OP_SET_BE, &int_ctype);
use_pseudo(insn, src, &insn->src1);
insn->src2 = value_pseudo(9);
insn->target = alloc_pseudo(insn);
insn->itype = &int_ctype;
add_one_insn(ep, insn);
return insn->target;
}
static pseudo_t linearize_unreachable(struct entrypoint *ep, struct expression *exp)
{
add_unreachable(ep);
return VOID;
}
static struct sym_init {
const char *name;
pseudo_t (*linearize)(struct entrypoint *, struct expression*);
struct symbol_op op;
} builtins_table[] = {
// must be declared in builtin.c:declare_builtins[]
{ "__builtin_fma", linearize_fma },
{ "__builtin_fmaf", linearize_fma },
{ "__builtin_fmal", linearize_fma },
{ "__builtin_isdigit", linearize_isdigit },
{ "__builtin_unreachable", linearize_unreachable },
{ }
};
void init_linearized_builtins(int stream)
{
struct sym_init *ptr;
for (ptr = builtins_table; ptr->name; ptr++) {
struct symbol *sym;
sym = create_symbol(stream, ptr->name, SYM_NODE, NS_SYMBOL);
if (!sym->op)
sym->op = &ptr->op;
sym->op->type |= KW_BUILTIN;
sym->op->linearize = ptr->linearize;
}
}
sparse-0.6.4/linearize.h 0000664 0000000 0000000 00000021305 14115310122 0015140 0 ustar 00root root 0000000 0000000 #ifndef LINEARIZE_H
#define LINEARIZE_H
#include "lib.h"
#include "allocate.h"
#include "token.h"
#include "opcode.h"
#include "parse.h"
#include "symbol.h"
#include "ptrmap.h"
struct instruction;
struct pseudo_user {
struct instruction *insn;
pseudo_t *userp;
};
DECLARE_ALLOCATOR(pseudo_user);
DECLARE_PTR_LIST(pseudo_user_list, struct pseudo_user);
DECLARE_PTRMAP(phi_map, struct symbol *, struct instruction *);
enum pseudo_type {
PSEUDO_VOID,
PSEUDO_UNDEF,
PSEUDO_PHI,
PSEUDO_REG,
PSEUDO_ARG,
PSEUDO_SYM,
PSEUDO_VAL,
};
struct pseudo {
int nr;
enum pseudo_type type;
struct pseudo_user_list *users;
struct ident *ident;
union {
struct symbol *sym;
struct instruction *def;
long long value;
};
void *priv;
};
extern struct pseudo void_pseudo;
#define VOID (&void_pseudo)
static inline bool is_zero(pseudo_t pseudo)
{
return pseudo->type == PSEUDO_VAL && pseudo->value == 0;
}
static inline bool is_nonzero(pseudo_t pseudo)
{
return pseudo->type == PSEUDO_VAL && pseudo->value != 0;
}
static inline bool is_positive(pseudo_t pseudo, unsigned size)
{
return pseudo->type == PSEUDO_VAL && !(pseudo->value & sign_bit(size));
}
struct multijmp {
struct basic_block *target;
long long begin, end;
};
struct asm_constraint {
pseudo_t pseudo;
const char *constraint;
const struct ident *ident;
unsigned int is_memory:1;
};
DECLARE_ALLOCATOR(asm_constraint);
DECLARE_PTR_LIST(asm_constraint_list, struct asm_constraint);
struct asm_rules {
struct asm_constraint_list *inputs;
struct asm_constraint_list *outputs;
struct asm_constraint_list *clobbers;
};
DECLARE_ALLOCATOR(asm_rules);
struct instruction {
unsigned opcode:7,
tainted:1,
size:24;
struct basic_block *bb;
struct position pos;
struct symbol *type;
pseudo_t target;
union {
struct /* entrypoint */ {
struct pseudo_list *arg_list;
};
struct /* branch */ {
pseudo_t cond;
struct basic_block *bb_true, *bb_false;
};
struct /* switch */ {
pseudo_t _cond;
struct multijmp_list *multijmp_list;
};
struct /* phi_node */ {
pseudo_t phi_var; // used for SSA conversion
struct pseudo_list *phi_list;
unsigned int used:1;
};
struct /* phi source */ {
pseudo_t phi_src;
struct instruction *phi_node;
};
struct /* unops */ {
pseudo_t src;
unsigned from; /* slice */
struct symbol *orig_type; /* casts */
};
struct /* memops */ {
pseudo_t addr; /* alias .src */
long long offset;
unsigned int is_volatile:1;
};
struct /* binops and sel */ {
pseudo_t src1, src2, src3;
};
struct /* compare */ {
pseudo_t _src1, _src2; // alias .src[12]
struct symbol *itype; // input operands' type
};
struct /* setval */ {
struct expression *val;
};
struct /* setfval */ {
long double fvalue;
};
struct /* call */ {
pseudo_t func;
struct pseudo_list *arguments;
struct symbol_list *fntypes;
};
struct /* context */ {
int increment;
int check;
struct expression *context_expr;
};
struct /* asm */ {
const char *string;
struct asm_rules *asm_rules;
unsigned int clobber_memory:1;
unsigned int output_memory:1;
};
};
};
struct basic_block_list;
struct instruction_list;
struct basic_block {
struct position pos;
unsigned long generation;
struct entrypoint *ep;
struct basic_block_list *parents; /* sources */
struct basic_block_list *children; /* destinations */
struct instruction_list *insns; /* Linear list of instructions */
struct basic_block *idom; /* link to the immediate dominator */
unsigned int nr; /* unique id for label's names */
int dom_level; /* level in the dominance tree */
struct basic_block_list *doms; /* list of BB idominated by this one */
struct pseudo_list *needs, *defines;
union {
struct phi_map *phi_map;/* needed during SSA conversion */
int postorder_nr; /* postorder number */
int context; /* needed during context checking */
void *priv;
};
};
//
// return the opcode of the instruction defining ``SRC`` if existing
// and OP_BADOP if not. It also assigns the defining instruction
// to ``DEF``.
#define DEF_OPCODE(DEF, SRC) \
(((SRC)->type == PSEUDO_REG && (DEF = (SRC)->def)) ? DEF->opcode : OP_BADOP)
static inline void add_bb(struct basic_block_list **list, struct basic_block *bb)
{
add_ptr_list(list, bb);
}
static inline void add_instruction(struct instruction_list **list, struct instruction *insn)
{
add_ptr_list(list, insn);
}
static inline void insert_last_instruction(struct basic_block *bb, struct instruction *insn)
{
struct instruction *last = delete_last_instruction(&bb->insns);
add_instruction(&bb->insns, insn);
add_instruction(&bb->insns, last);
insn->bb = bb;
}
static inline void add_multijmp(struct multijmp_list **list, struct multijmp *multijmp)
{
add_ptr_list(list, multijmp);
}
static inline pseudo_t *add_pseudo(struct pseudo_list **list, pseudo_t pseudo)
{
return add_ptr_list(list, pseudo);
}
static inline int remove_pseudo(struct pseudo_list **list, pseudo_t pseudo)
{
return delete_ptr_list_entry((struct ptr_list **)list, pseudo, 0) != 0;
}
static inline int pseudo_in_list(struct pseudo_list *list, pseudo_t pseudo)
{
return lookup_ptr_list_entry((struct ptr_list *)list, pseudo);
}
static inline int bb_terminated(struct basic_block *bb)
{
struct instruction *insn;
if (!bb)
return 0;
insn = last_instruction(bb->insns);
return insn && insn->opcode >= OP_TERMINATOR
&& insn->opcode <= OP_TERMINATOR_END;
}
static inline int bb_reachable(struct basic_block *bb)
{
return bb != NULL;
}
static inline int lookup_bb(struct basic_block_list *list, struct basic_block *bb)
{
return lookup_ptr_list_entry((struct ptr_list *)list, bb);
}
static inline void add_pseudo_user_ptr(struct pseudo_user *user, struct pseudo_user_list **list)
{
add_ptr_list(list, user);
}
static inline int has_use_list(pseudo_t p)
{
return (p && p->type != PSEUDO_VOID && p->type != PSEUDO_UNDEF && p->type != PSEUDO_VAL);
}
static inline bool has_definition(pseudo_t p)
{
return p->type == PSEUDO_REG || p->type == PSEUDO_PHI;
}
static inline int pseudo_user_list_size(struct pseudo_user_list *list)
{
return ptr_list_size((struct ptr_list *)list);
}
static inline bool pseudo_user_list_empty(struct pseudo_user_list *list)
{
return ptr_list_empty((struct ptr_list *)list);
}
static inline int has_users(pseudo_t p)
{
return !pseudo_user_list_empty(p->users);
}
static inline bool one_use(pseudo_t p)
{
return !ptr_list_multiple((struct ptr_list *)(p->users));
}
static inline int nbr_users(pseudo_t p)
{
return pseudo_user_list_size(p->users);
}
static inline struct pseudo_user *alloc_pseudo_user(struct instruction *insn, pseudo_t *pp)
{
struct pseudo_user *user = __alloc_pseudo_user(0);
user->userp = pp;
user->insn = insn;
return user;
}
static inline void use_pseudo(struct instruction *insn, pseudo_t p, pseudo_t *pp)
{
*pp = p;
if (has_use_list(p))
add_pseudo_user_ptr(alloc_pseudo_user(insn, pp), &p->users);
}
static inline void link_phi(struct instruction *node, pseudo_t phi)
{
use_pseudo(node, phi, add_pseudo(&node->phi_list, phi));
phi->def->phi_node = node;
}
static inline void remove_bb_from_list(struct basic_block_list **list, struct basic_block *entry, int count)
{
delete_ptr_list_entry((struct ptr_list **)list, entry, count);
}
static inline void replace_bb_in_list(struct basic_block_list **list,
struct basic_block *old, struct basic_block *new, int count)
{
replace_ptr_list_entry((struct ptr_list **)list, old, new, count);
}
struct entrypoint {
struct symbol *name;
struct symbol_list *syms;
struct pseudo_list *accesses;
struct basic_block_list *bbs;
struct basic_block *active;
struct instruction *entry;
unsigned int dom_levels; /* max levels in the dom tree */
};
extern void insert_select(struct basic_block *bb, struct instruction *br, struct instruction *phi, pseudo_t if_true, pseudo_t if_false);
struct instruction *alloc_phisrc(pseudo_t pseudo, struct symbol *type);
struct instruction *alloc_phi_node(struct basic_block *bb, struct symbol *type, struct ident *ident);
struct instruction *insert_phi_node(struct basic_block *bb, struct symbol *var);
void add_phi_node(struct basic_block *bb, struct instruction *phi_node);
pseudo_t alloc_phi(struct basic_block *source, pseudo_t pseudo, struct symbol *type);
pseudo_t alloc_pseudo(struct instruction *def);
pseudo_t value_pseudo(long long val);
pseudo_t undef_pseudo(void);
struct entrypoint *linearize_symbol(struct symbol *sym);
int unssa(struct entrypoint *ep);
void show_entry(struct entrypoint *ep);
void show_insn_entry(struct instruction *insn);
const char *show_pseudo(pseudo_t pseudo);
void show_bb(struct basic_block *bb);
void show_insn_bb(struct instruction *insn);
const char *show_instruction(struct instruction *insn);
const char *show_label(struct basic_block *bb);
#endif /* LINEARIZE_H */
sparse-0.6.4/liveness.c 0000664 0000000 0000000 00000016160 14115310122 0015004 0 ustar 00root root 0000000 0000000 /*
* Register - track pseudo usage, maybe eventually try to do register
* allocation.
*
* Copyright (C) 2004 Linus Torvalds
*/
#include
#include "liveness.h"
#include "parse.h"
#include "expression.h"
#include "linearize.h"
#include "flow.h"
static void phi_defines(struct instruction * phi_node, pseudo_t target,
void (*defines)(struct basic_block *, pseudo_t))
{
pseudo_t phi;
FOR_EACH_PTR(phi_node->phi_list, phi) {
struct instruction *def;
if (phi == VOID)
continue;
def = phi->def;
if (!def || !def->bb)
continue;
defines(def->bb, target);
} END_FOR_EACH_PTR(phi);
}
static void asm_liveness(struct basic_block *bb, struct instruction *insn,
void (*def)(struct basic_block *, pseudo_t),
void (*use)(struct basic_block *, pseudo_t))
{
struct asm_constraint *entry;
FOR_EACH_PTR(insn->asm_rules->inputs, entry) {
use(bb, entry->pseudo);
} END_FOR_EACH_PTR(entry);
FOR_EACH_PTR(insn->asm_rules->outputs, entry) {
if (entry->is_memory)
use(bb, entry->pseudo);
else
def(bb, entry->pseudo);
} END_FOR_EACH_PTR(entry);
}
static void track_instruction_usage(struct basic_block *bb, struct instruction *insn,
void (*def)(struct basic_block *, pseudo_t),
void (*use)(struct basic_block *, pseudo_t))
{
pseudo_t pseudo;
#define USES(x) use(bb, insn->x)
#define DEFINES(x) def(bb, insn->x)
switch (insn->opcode) {
case OP_RET:
case OP_COMPUTEDGOTO:
USES(src);
break;
case OP_CBR:
case OP_SWITCH:
USES(cond);
break;
/* Binary */
case OP_BINARY ... OP_BINARY_END:
case OP_FPCMP ... OP_FPCMP_END:
case OP_BINCMP ... OP_BINCMP_END:
USES(src1); USES(src2); DEFINES(target);
break;
/* Uni */
case OP_UNOP ... OP_UNOP_END:
case OP_SYMADDR:
case OP_SLICE:
USES(src); DEFINES(target);
break;
case OP_SEL:
USES(src1); USES(src2); USES(src3); DEFINES(target);
break;
/* Memory */
case OP_LOAD:
USES(src); DEFINES(target);
break;
case OP_STORE:
USES(src); USES(target);
break;
case OP_LABEL:
case OP_SETVAL:
case OP_SETFVAL:
DEFINES(target);
break;
/* Other */
case OP_PHI:
/* Phi-nodes are "backwards" nodes. Their def doesn't matter */
phi_defines(insn, insn->target, def);
break;
case OP_PHISOURCE:
/*
* We don't care about the phi-source define, they get set
* up and expanded by the OP_PHI
*/
USES(phi_src);
break;
case OP_CALL:
USES(func);
if (insn->target != VOID)
DEFINES(target);
FOR_EACH_PTR(insn->arguments, pseudo) {
use(bb, pseudo);
} END_FOR_EACH_PTR(pseudo);
break;
case OP_ASM:
asm_liveness(bb, insn, def, use);
break;
case OP_RANGE:
USES(src1); USES(src2); USES(src3);
break;
case OP_BADOP:
case OP_NOP:
case OP_CONTEXT:
break;
}
}
static int liveness_changed;
static void add_pseudo_exclusive(struct pseudo_list **list, pseudo_t pseudo)
{
if (!pseudo_in_list(*list, pseudo)) {
liveness_changed = 1;
add_pseudo(list, pseudo);
}
}
static inline int trackable_pseudo(pseudo_t pseudo)
{
return pseudo && (pseudo->type == PSEUDO_REG || pseudo->type == PSEUDO_ARG);
}
static void insn_uses(struct basic_block *bb, pseudo_t pseudo)
{
if (trackable_pseudo(pseudo)) {
struct instruction *def = pseudo->def;
if (pseudo->type != PSEUDO_REG || def->bb != bb || def->opcode == OP_PHI)
add_pseudo_exclusive(&bb->needs, pseudo);
}
}
static void insn_defines(struct basic_block *bb, pseudo_t pseudo)
{
assert(trackable_pseudo(pseudo));
add_pseudo(&bb->defines, pseudo);
}
static void track_bb_liveness(struct basic_block *bb)
{
pseudo_t needs;
FOR_EACH_PTR(bb->needs, needs) {
struct basic_block *parent;
FOR_EACH_PTR(bb->parents, parent) {
if (!pseudo_in_list(parent->defines, needs)) {
add_pseudo_exclusive(&parent->needs, needs);
}
} END_FOR_EACH_PTR(parent);
} END_FOR_EACH_PTR(needs);
}
/*
* We need to clear the liveness information if we
* are going to re-run it.
*/
void clear_liveness(struct entrypoint *ep)
{
struct basic_block *bb;
FOR_EACH_PTR(ep->bbs, bb) {
free_ptr_list(&bb->needs);
free_ptr_list(&bb->defines);
} END_FOR_EACH_PTR(bb);
}
/*
* Track inter-bb pseudo liveness. The intra-bb case
* is purely local information.
*/
void track_pseudo_liveness(struct entrypoint *ep)
{
struct basic_block *bb;
/* Add all the bb pseudo usage */
FOR_EACH_PTR(ep->bbs, bb) {
struct instruction *insn;
FOR_EACH_PTR(bb->insns, insn) {
if (!insn->bb)
continue;
assert(insn->bb == bb);
track_instruction_usage(bb, insn, insn_defines, insn_uses);
} END_FOR_EACH_PTR(insn);
} END_FOR_EACH_PTR(bb);
/* Calculate liveness.. */
do {
liveness_changed = 0;
FOR_EACH_PTR_REVERSE(ep->bbs, bb) {
track_bb_liveness(bb);
} END_FOR_EACH_PTR_REVERSE(bb);
} while (liveness_changed);
/* Remove the pseudos from the "defines" list that are used internally */
FOR_EACH_PTR(ep->bbs, bb) {
pseudo_t def;
FOR_EACH_PTR(bb->defines, def) {
struct basic_block *child;
FOR_EACH_PTR(bb->children, child) {
if (pseudo_in_list(child->needs, def))
goto is_used;
} END_FOR_EACH_PTR(child);
DELETE_CURRENT_PTR(def);
is_used:
;
} END_FOR_EACH_PTR(def);
PACK_PTR_LIST(&bb->defines);
} END_FOR_EACH_PTR(bb);
}
static void merge_pseudo_list(struct pseudo_list *src, struct pseudo_list **dest)
{
pseudo_t pseudo;
FOR_EACH_PTR(src, pseudo) {
add_pseudo_exclusive(dest, pseudo);
} END_FOR_EACH_PTR(pseudo);
}
static void track_phi_uses(struct instruction *insn)
{
pseudo_t phi;
FOR_EACH_PTR(insn->phi_list, phi) {
struct instruction *def;
if (phi == VOID || !phi->def)
continue;
def = phi->def;
assert(def->opcode == OP_PHISOURCE);
} END_FOR_EACH_PTR(phi);
}
static void track_bb_phi_uses(struct basic_block *bb)
{
struct instruction *insn;
FOR_EACH_PTR(bb->insns, insn) {
if (insn->bb && insn->opcode == OP_PHI)
track_phi_uses(insn);
} END_FOR_EACH_PTR(insn);
}
static struct pseudo_list **live_list;
static struct pseudo_list *dead_list;
static void death_def(struct basic_block *bb, pseudo_t pseudo)
{
}
static void death_use(struct basic_block *bb, pseudo_t pseudo)
{
if (trackable_pseudo(pseudo) && !pseudo_in_list(*live_list, pseudo)) {
add_pseudo(&dead_list, pseudo);
add_pseudo(live_list, pseudo);
}
}
static void track_pseudo_death_bb(struct basic_block *bb)
{
struct pseudo_list *live = NULL;
struct basic_block *child;
struct instruction *insn;
FOR_EACH_PTR(bb->children, child) {
merge_pseudo_list(child->needs, &live);
} END_FOR_EACH_PTR(child);
live_list = &live;
FOR_EACH_PTR_REVERSE(bb->insns, insn) {
if (!insn->bb)
continue;
dead_list = NULL;
track_instruction_usage(bb, insn, death_def, death_use);
if (dead_list) {
pseudo_t dead;
FOR_EACH_PTR(dead_list, dead) {
struct instruction *deathnote = __alloc_instruction(0);
deathnote->bb = bb;
deathnote->opcode = OP_DEATHNOTE;
deathnote->target = dead;
INSERT_CURRENT(deathnote, insn);
} END_FOR_EACH_PTR(dead);
free_ptr_list(&dead_list);
}
} END_FOR_EACH_PTR_REVERSE(insn);
free_ptr_list(&live);
}
void track_pseudo_death(struct entrypoint *ep)
{
struct basic_block *bb;
FOR_EACH_PTR(ep->bbs, bb) {
track_bb_phi_uses(bb);
} END_FOR_EACH_PTR(bb);
FOR_EACH_PTR(ep->bbs, bb) {
track_pseudo_death_bb(bb);
} END_FOR_EACH_PTR(bb);
}
sparse-0.6.4/liveness.h 0000664 0000000 0000000 00000000343 14115310122 0015005 0 ustar 00root root 0000000 0000000 #ifndef LIVENESS_H
#define LIVENESS_H
struct entrypoint;
/* liveness.c */
void clear_liveness(struct entrypoint *ep);
void track_pseudo_liveness(struct entrypoint *ep);
void track_pseudo_death(struct entrypoint *ep);
#endif
sparse-0.6.4/machine.h 0000664 0000000 0000000 00000005567 14115310122 0014576 0 ustar 00root root 0000000 0000000 #ifndef MACHINE_H
#define MACHINE_H
#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
#define ARCH_BIG_ENDIAN 1
#else
#define ARCH_BIG_ENDIAN 0
#endif
enum bitness {
ARCH_LP32,
ARCH_X32,
ARCH_LP64,
ARCH_LLP64,
};
#ifdef __LP64__
#define ARCH_M64_DEFAULT ARCH_LP64
#elif defined(__x86_64__) || defined(__x86_64)
#define ARCH_M64_DEFAULT ARCH_X32
#else
#define ARCH_M64_DEFAULT ARCH_LP32
#endif
enum machine {
MACH_ARM, MACH_ARM64,
MACH_I386, MACH_X86_64,
MACH_MIPS32, MACH_MIPS64,
MACH_PPC32, MACH_PPC64,
MACH_RISCV32, MACH_RISCV64,
MACH_SPARC32, MACH_SPARC64,
MACH_S390, MACH_S390X,
MACH_ALPHA,
MACH_BFIN,
MACH_H8300,
MACH_M68K,
MACH_MICROBLAZE,
MACH_NDS32,
MACH_NIOS2,
MACH_OPENRISC,
MACH_SH,
MACH_XTENSA,
MACH_UNKNOWN
};
#if defined(__aarch64__)
#define MACH_NATIVE MACH_ARM64
#elif defined(__alpha__) || defined(__alpha)
#define MACH_NATIVE MACH_ALPHA
#elif defined(__arm__)
#define MACH_NATIVE MACH_ARM
#elif defined(__x86_64__) || defined(__x86_64)
#define MACH_NATIVE MACH_X86_64
#elif defined(__i386__) || defined(__i386)
#define MACH_NATIVE MACH_I386
#elif defined(__mips64__) || (defined(__mips) && __mips == 64)
#define MACH_NATIVE MACH_MIPS64
#elif defined(__mips__) || defined(__mips)
#define MACH_NATIVE MACH_MIPS32
#elif defined(__powerpc64__) || defined(__ppc64__)
#define MACH_NATIVE MACH_PPC64
#elif defined(__powerpc__) || defined(__powerpc) || defined(__ppc__)
#define MACH_NATIVE MACH_PPC32
#elif defined(__riscv) && (__riscv_xlen == 64)
#define MACH_NATIVE MACH_RISCV64
#elif defined(__riscv) && (__riscv_xlen == 32)
#define MACH_NATIVE MACH_RISCV32
#elif defined(__sparc_v9__) || defined(__sparcv9)
#define MACH_NATIVE MACH_SPARC64
#elif defined(__sparc__) || defined(__sparc)
#define MACH_NATIVE MACH_SPARC32
#elif defined(__m68k__)
#define MACH_NATIVE MACH_M68K
#elif defined(__s390x__) || defined(__zarch__)
#define MACH_NATIVE MACH_S390X
#elif defined(__s390__)
#define MACH_NATIVE MACH_S390
#else
#define MACH_NATIVE MACH_UNKNOWN
#endif
enum fp_abi {
FP_ABI_HARD,
FP_ABI_SOFT,
FP_ABI_HYBRID,
};
#if defined(__ARM_PCS_VFP)
#define FP_ABI_NATIVE FP_ABI_HARD
#elif defined(__ARM_PCS) && !defined(__SOFTFP__)
#define FP_ABI_NATIVE FP_ABI_HYBRID
#else
#define FP_ABI_NATIVE FP_ABI_SOFT
#endif
enum {
OS_UNKNOWN,
OS_NONE,
OS_UNIX,
OS_CYGWIN,
OS_DARWIN,
OS_FREEBSD,
OS_LINUX,
OS_NETBSD,
OS_OPENBSD,
OS_SUNOS,
};
#if defined(__CYGWIN__)
#define OS_NATIVE OS_CYGWIN
#elif defined(__APPLE__)
#define OS_NATIVE OS_DARWIN
#elif defined(__FreeBSD__)
#define OS_NATIVE OS_FREEBSD
#elif defined(__linux__) || defined(__linux)
#define OS_NATIVE OS_LINUX
#elif defined(__NetBSD__)
#define OS_NATIVE OS_NETBSD
#elif defined(__OpenBSD__)
#define OS_NATIVE OS_OPENBSD
#elif defined(__sun__) || defined(__sun)
#define OS_NATIVE OS_SUNOS
#elif defined(__unix__) || defined(__unix)
#define OS_NATIVE OS_UNIX
#else
#define OS_NATIVE OS_UNKNOWN
#endif
#endif
sparse-0.6.4/memops.c 0000664 0000000 0000000 00000015310 14115310122 0014450 0 ustar 00root root 0000000 0000000 /*
* memops - try to combine memory ops.
*
* Copyright (C) 2004 Linus Torvalds
*/
#include
#include
#include
#include
#include
#include
#include "parse.h"
#include "expression.h"
#include "linearize.h"
#include "simplify.h"
#include "flow.h"
static void rewrite_load_instruction(struct instruction *insn, struct pseudo_list *dominators)
{
pseudo_t new = NULL;
pseudo_t phi;
/*
* Check for somewhat common case of duplicate
* phi nodes.
*/
FOR_EACH_PTR(dominators, phi) {
if (!new)
new = phi->def->phi_src;
else if (new != phi->def->phi_src)
goto complex_phi;
} END_FOR_EACH_PTR(phi);
/*
* All the same pseudo - mark the phi-nodes unused
* and convert the load into a LNOP and replace the
* pseudo.
*/
replace_with_pseudo(insn, new);
FOR_EACH_PTR(dominators, phi) {
kill_instruction(phi->def);
} END_FOR_EACH_PTR(phi);
goto end;
complex_phi:
kill_use(&insn->src);
insn->opcode = OP_PHI;
insn->phi_list = dominators;
end:
repeat_phase |= REPEAT_CSE;
}
static int find_dominating_parents(struct instruction *insn,
struct basic_block *bb, struct pseudo_list **dominators,
int local)
{
struct basic_block *parent;
FOR_EACH_PTR(bb->parents, parent) {
struct instruction *phisrc;
struct instruction *one;
pseudo_t phi;
FOR_EACH_PTR_REVERSE(parent->insns, one) {
int dominance;
if (!one->bb)
continue;
if (one == insn)
goto no_dominance;
dominance = dominates(insn, one, local);
if (dominance < 0) {
if (one->opcode == OP_LOAD)
continue;
return 0;
}
if (!dominance)
continue;
goto found_dominator;
} END_FOR_EACH_PTR_REVERSE(one);
no_dominance:
if (parent->generation == bb->generation)
continue;
parent->generation = bb->generation;
if (!find_dominating_parents(insn, parent, dominators, local))
return 0;
continue;
found_dominator:
phisrc = alloc_phisrc(one->target, one->type);
phisrc->phi_node = insn;
insert_last_instruction(parent, phisrc);
phi = phisrc->target;
phi->ident = phi->ident ? : one->target->ident;
use_pseudo(insn, phi, add_pseudo(dominators, phi));
} END_FOR_EACH_PTR(parent);
return 1;
}
static int address_taken(pseudo_t pseudo)
{
struct pseudo_user *pu;
FOR_EACH_PTR(pseudo->users, pu) {
struct instruction *insn = pu->insn;
if (insn->bb && (insn->opcode != OP_LOAD && insn->opcode != OP_STORE))
return 1;
if (pu->userp != &insn->src)
return 1;
} END_FOR_EACH_PTR(pu);
return 0;
}
static int local_pseudo(pseudo_t pseudo)
{
return pseudo->type == PSEUDO_SYM
&& !(pseudo->sym->ctype.modifiers & (MOD_STATIC | MOD_NONLOCAL))
&& !address_taken(pseudo);
}
static bool compatible_loads(struct instruction *a, struct instruction *b)
{
if (is_integral_type(a->type) && is_float_type(b->type))
return false;
if (is_float_type(a->type) && is_integral_type(b->type))
return false;
return true;
}
static void simplify_loads(struct basic_block *bb)
{
struct instruction *insn;
FOR_EACH_PTR_REVERSE(bb->insns, insn) {
if (!insn->bb)
continue;
if (insn->opcode == OP_LOAD) {
struct instruction *dom;
pseudo_t pseudo = insn->src;
int local = local_pseudo(pseudo);
struct pseudo_list *dominators;
if (insn->is_volatile)
continue;
if (!has_users(insn->target)) {
kill_instruction(insn);
continue;
}
RECURSE_PTR_REVERSE(insn, dom) {
int dominance;
if (!dom->bb)
continue;
dominance = dominates(insn, dom, local);
if (dominance) {
/* possible partial dominance? */
if (dominance < 0) {
if (dom->opcode == OP_LOAD)
continue;
goto next_load;
}
if (!compatible_loads(insn, dom))
goto next_load;
/* Yeehaa! Found one! */
replace_with_pseudo(insn, dom->target);
goto next_load;
}
} END_FOR_EACH_PTR_REVERSE(dom);
/* OK, go find the parents */
bb->generation = ++bb_generation;
dominators = NULL;
if (find_dominating_parents(insn, bb, &dominators, local)) {
/* This happens with initial assignments to structures etc.. */
if (!dominators) {
if (local) {
assert(pseudo->type != PSEUDO_ARG);
replace_with_pseudo(insn, value_pseudo(0));
}
goto next_load;
}
rewrite_load_instruction(insn, dominators);
} else { // cleanup pending phi-sources
int repeat = repeat_phase;
pseudo_t phi;
FOR_EACH_PTR(dominators, phi) {
kill_instruction(phi->def);
} END_FOR_EACH_PTR(phi);
repeat_phase = repeat;
}
}
next_load:
/* Do the next one */;
} END_FOR_EACH_PTR_REVERSE(insn);
}
static bool try_to_kill_store(struct instruction *insn,
struct instruction *dom, int local)
{
int dominance = dominates(insn, dom, local);
if (dominance) {
/* possible partial dominance? */
if (dominance < 0)
return false;
if (insn->target == dom->target && insn->bb == dom->bb) {
// found a memop which makes the store redundant
kill_instruction_force(insn);
return false;
}
if (dom->opcode == OP_LOAD)
return false;
if (dom->is_volatile)
return false;
/* Yeehaa! Found one! */
kill_instruction_force(dom);
}
return true;
}
static void kill_dominated_stores(struct basic_block *bb)
{
struct instruction *insn;
FOR_EACH_PTR_REVERSE(bb->insns, insn) {
if (!insn->bb)
continue;
if (insn->opcode == OP_STORE) {
struct basic_block *par;
struct instruction *dom;
pseudo_t pseudo = insn->src;
int local;
if (!insn->type)
continue;
if (insn->is_volatile)
continue;
local = local_pseudo(pseudo);
RECURSE_PTR_REVERSE(insn, dom) {
if (!dom->bb)
continue;
if (!try_to_kill_store(insn, dom, local))
goto next_store;
} END_FOR_EACH_PTR_REVERSE(dom);
/* OK, we should check the parents now */
FOR_EACH_PTR(bb->parents, par) {
if (bb_list_size(par->children) != 1)
goto next_parent;
FOR_EACH_PTR(par->insns, dom) {
if (!dom->bb)
continue;
if (dom == insn)
goto next_parent;
if (!try_to_kill_store(insn, dom, local))
goto next_parent;
} END_FOR_EACH_PTR(dom);
next_parent:
;
} END_FOR_EACH_PTR(par);
}
next_store:
/* Do the next one */;
} END_FOR_EACH_PTR_REVERSE(insn);
}
void simplify_memops(struct entrypoint *ep)
{
struct basic_block *bb;
pseudo_t pseudo;
FOR_EACH_PTR_REVERSE(ep->bbs, bb) {
simplify_loads(bb);
} END_FOR_EACH_PTR_REVERSE(bb);
FOR_EACH_PTR_REVERSE(ep->bbs, bb) {
kill_dominated_stores(bb);
} END_FOR_EACH_PTR_REVERSE(bb);
FOR_EACH_PTR(ep->accesses, pseudo) {
struct symbol *var = pseudo->sym;
unsigned long mod;
if (!var)
continue;
mod = var->ctype.modifiers;
if (mod & (MOD_VOLATILE | MOD_NONLOCAL | MOD_STATIC))
continue;
kill_dead_stores(ep, pseudo, local_pseudo(pseudo));
} END_FOR_EACH_PTR(pseudo);
}
sparse-0.6.4/obfuscate.c 0000664 0000000 0000000 00000004243 14115310122 0015126 0 ustar 00root root 0000000 0000000 /*
* Example trivial client program that uses the sparse library
* to tokenize, preprocess and parse a C file, and prints out
* the results.
*
* Copyright (C) 2003 Transmeta Corp.
* 2003-2004 Linus Torvalds
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include
#include
#include
#include
#include
#include
#include
#include "lib.h"
#include "allocate.h"
#include "token.h"
#include "parse.h"
#include "symbol.h"
#include "expression.h"
#include "linearize.h"
static void emit_entrypoint(struct entrypoint *ep)
{
}
static void emit_symbol(struct symbol *sym)
{
struct entrypoint *ep;
ep = linearize_symbol(sym);
if (ep)
emit_entrypoint(ep);
}
static void emit_symbol_list(struct symbol_list *list)
{
struct symbol *sym;
FOR_EACH_PTR(list, sym) {
expand_symbol(sym);
emit_symbol(sym);
} END_FOR_EACH_PTR(sym);
}
int main(int argc, char **argv)
{
struct string_list *filelist = NULL;
char *file;
emit_symbol_list(sparse_initialize(argc, argv, &filelist));
FOR_EACH_PTR(filelist, file) {
emit_symbol_list(sparse(file));
} END_FOR_EACH_PTR(file);
return 0;
}
sparse-0.6.4/opcode.c 0000664 0000000 0000000 00000002717 14115310122 0014430 0 ustar 00root root 0000000 0000000 /*
* Copyright (C) 2017 Luc Van Oostenryck
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "opcode.h"
const struct opcode_table opcode_table[OP_LAST] = {
#define OPCODE(OP,NG,SW,SG,TF,N,FL) \
[OP_##OP] = { \
.negate = OP_##NG, \
.swap = OP_##SW, \
.sign = OP_##SG, \
.to_float = OP_##TF, \
.arity = N, \
.flags = FL, \
},
#define OPCODE_RANGE(OP,S,E)
#include "opcode.def"
#undef OPCODE
#undef OPCODE_RANGE
};
sparse-0.6.4/opcode.def 0000664 0000000 0000000 00000015371 14115310122 0014744 0 ustar 00root root 0000000 0000000 // OPCODE negated swaped sign float arity, flags
OPCODE(BADOP, BADOP, BADOP, BADOP, BADOP, 0, OPF_NONE)
/* Entry */
OPCODE(ENTRY, BADOP, BADOP, BADOP, BADOP, 0, OPF_NONE)
/* Terminator */
OPCODE(RET, BADOP, BADOP, BADOP, BADOP, 1, OPF_NONE)
OPCODE(BR, BADOP, BADOP, BADOP, BADOP, 0, OPF_NONE)
OPCODE(CBR, BADOP, BADOP, BADOP, BADOP, 1, OPF_NONE)
OPCODE(SWITCH, BADOP, BADOP, BADOP, BADOP, 1, OPF_NONE)
OPCODE(UNREACH, BADOP, BADOP, BADOP, BADOP, 0, OPF_NONE)
OPCODE(COMPUTEDGOTO, BADOP, BADOP, BADOP, BADOP, 1, OPF_NONE)
OPCODE_RANGE(TERMINATOR, RET, COMPUTEDGOTO)
/* Binary */
OPCODE(ADD, BADOP, BADOP, BADOP, FADD, 2, OPF_TARGET|OPF_COMMU|OPF_ASSOC|OPF_BINOP)
OPCODE(MUL, BADOP, BADOP, BADOP, FMUL, 2, OPF_TARGET|OPF_COMMU|OPF_ASSOC|OPF_BINOP)
OPCODE(SUB, BADOP, BADOP, BADOP, FSUB, 2, OPF_TARGET|OPF_BINOP)
OPCODE(DIVU, BADOP, BADOP, DIVS, FDIV, 2, OPF_TARGET|OPF_BINOP)
OPCODE(DIVS, BADOP, BADOP, DIVU, FDIV, 2, OPF_TARGET|OPF_BINOP)
OPCODE(MODU, BADOP, BADOP, MODS, BADOP, 2, OPF_TARGET|OPF_BINOP)
OPCODE(MODS, BADOP, BADOP, MODU, BADOP, 2, OPF_TARGET|OPF_BINOP)
OPCODE(LSR, BADOP, BADOP, ASR, BADOP, 2, OPF_TARGET|OPF_BINOP)
OPCODE(ASR, BADOP, BADOP, LSR, BADOP, 2, OPF_TARGET|OPF_BINOP)
OPCODE(SHL, BADOP, BADOP, BADOP, BADOP, 2, OPF_TARGET|OPF_BINOP)
/* Floating-point binops */
OPCODE(FADD, BADOP, BADOP, BADOP, BADOP, 2, OPF_TARGET)
OPCODE(FSUB, BADOP, BADOP, BADOP, BADOP, 2, OPF_TARGET)
OPCODE(FMUL, BADOP, BADOP, BADOP, BADOP, 2, OPF_TARGET)
OPCODE(FDIV, BADOP, BADOP, BADOP, BADOP, 2, OPF_TARGET)
/* Logical */
OPCODE(AND, BADOP, BADOP, BADOP, BADOP, 2, OPF_TARGET|OPF_COMMU|OPF_ASSOC|OPF_BINOP)
OPCODE(OR, BADOP, BADOP, BADOP, BADOP, 2, OPF_TARGET|OPF_COMMU|OPF_ASSOC|OPF_BINOP)
OPCODE(XOR, BADOP, BADOP, BADOP, BADOP, 2, OPF_TARGET|OPF_COMMU|OPF_ASSOC|OPF_BINOP)
OPCODE_RANGE(BINARY, ADD, XOR)
/* floating-point comparison */
OPCODE(FCMP_ORD, FCMP_UNO, FCMP_ORD, BADOP, BADOP, 2, OPF_TARGET)
OPCODE(FCMP_OEQ, FCMP_UNE, FCMP_OEQ, BADOP, BADOP, 2, OPF_TARGET)
OPCODE(FCMP_ONE, FCMP_UEQ, FCMP_ONE, BADOP, BADOP, 2, OPF_TARGET)
OPCODE(FCMP_UEQ, FCMP_ONE, FCMP_UEQ, BADOP, BADOP, 2, OPF_TARGET)
OPCODE(FCMP_UNE, FCMP_OEQ, FCMP_UNE, BADOP, BADOP, 2, OPF_TARGET)
OPCODE(FCMP_OLT, FCMP_UGE, FCMP_OGT, BADOP, BADOP, 2, OPF_TARGET)
OPCODE(FCMP_OLE, FCMP_UGT, FCMP_OGE, BADOP, BADOP, 2, OPF_TARGET)
OPCODE(FCMP_OGE, FCMP_ULT, FCMP_OLE, BADOP, BADOP, 2, OPF_TARGET)
OPCODE(FCMP_OGT, FCMP_ULE, FCMP_OLT, BADOP, BADOP, 2, OPF_TARGET)
OPCODE(FCMP_ULT, FCMP_OGE, FCMP_UGT, BADOP, BADOP, 2, OPF_TARGET)
OPCODE(FCMP_ULE, FCMP_OGT, FCMP_UGE, BADOP, BADOP, 2, OPF_TARGET)
OPCODE(FCMP_UGE, FCMP_OLT, FCMP_ULE, BADOP, BADOP, 2, OPF_TARGET)
OPCODE(FCMP_UGT, FCMP_OLE, FCMP_ULT, BADOP, BADOP, 2, OPF_TARGET)
OPCODE(FCMP_UNO, FCMP_ORD, FCMP_UNO, BADOP, BADOP, 2, OPF_TARGET)
OPCODE_RANGE(FPCMP, FCMP_ORD, FCMP_UNO)
/* Binary comparison */
OPCODE(SET_EQ, SET_NE, SET_EQ, SET_EQ, FCMP_OEQ, 2, OPF_TARGET|OPF_BINOP|OPF_COMPARE|OPF_COMMU)
OPCODE(SET_LT, SET_GE, SET_GT, SET_B, FCMP_OLT, 2, OPF_TARGET|OPF_BINOP|OPF_COMPARE|OPF_SIGNED)
OPCODE(SET_LE, SET_GT, SET_GE, SET_BE, FCMP_OLE, 2, OPF_TARGET|OPF_BINOP|OPF_COMPARE|OPF_SIGNED)
OPCODE(SET_GE, SET_LT, SET_LE, SET_AE, FCMP_OGE, 2, OPF_TARGET|OPF_BINOP|OPF_COMPARE|OPF_SIGNED)
OPCODE(SET_GT, SET_LE, SET_LT, SET_A, FCMP_OGT, 2, OPF_TARGET|OPF_BINOP|OPF_COMPARE|OPF_SIGNED)
OPCODE(SET_B, SET_AE, SET_A, SET_LT, FCMP_OLT, 2, OPF_TARGET|OPF_BINOP|OPF_COMPARE|OPF_UNSIGNED)
OPCODE(SET_BE, SET_A, SET_AE, SET_LE, FCMP_OLE, 2, OPF_TARGET|OPF_BINOP|OPF_COMPARE|OPF_UNSIGNED)
OPCODE(SET_AE, SET_B, SET_BE, SET_GE, FCMP_OGE, 2, OPF_TARGET|OPF_BINOP|OPF_COMPARE|OPF_UNSIGNED)
OPCODE(SET_A, SET_BE, SET_B, SET_GT, FCMP_OGT, 2, OPF_TARGET|OPF_BINOP|OPF_COMPARE|OPF_UNSIGNED)
OPCODE(SET_NE, SET_EQ, SET_NE, SET_NE, FCMP_UNE, 2, OPF_TARGET|OPF_BINOP|OPF_COMPARE|OPF_COMMU)
OPCODE_RANGE(BINCMP, SET_EQ, SET_NE)
/* Uni */
OPCODE(NOT, BADOP, BADOP, BADOP, BADOP, 1, OPF_TARGET|OPF_UNOP)
OPCODE(NEG, BADOP, BADOP, BADOP, FNEG, 1, OPF_TARGET|OPF_UNOP)
OPCODE(FNEG, BADOP, BADOP, BADOP, BADOP, 1, OPF_TARGET)
OPCODE(TRUNC, BADOP, BADOP, BADOP, BADOP, 1, OPF_TARGET)
OPCODE(ZEXT, BADOP, BADOP, SEXT, BADOP, 1, OPF_TARGET)
OPCODE(SEXT, BADOP, BADOP, ZEXT, BADOP, 1, OPF_TARGET)
OPCODE(FCVTU, BADOP, BADOP, FCVTS, BADOP, 1, OPF_TARGET)
OPCODE(FCVTS, BADOP, BADOP, FCVTU, BADOP, 1, OPF_TARGET)
OPCODE(UCVTF, BADOP, BADOP, SCVTF, BADOP, 1, OPF_TARGET)
OPCODE(SCVTF, BADOP, BADOP, UCVTF, BADOP, 1, OPF_TARGET)
OPCODE(FCVTF, BADOP, BADOP, BADOP, BADOP, 1, OPF_TARGET)
OPCODE(UTPTR, BADOP, BADOP, BADOP, BADOP, 1, OPF_TARGET)
OPCODE(PTRTU, BADOP, BADOP, BADOP, BADOP, 1, OPF_TARGET)
OPCODE(PTRCAST, BADOP, BADOP, BADOP, BADOP, 1, OPF_TARGET)
OPCODE_RANGE(UNOP, NOT, PTRCAST)
OPCODE(SYMADDR, BADOP, BADOP, BADOP, BADOP, 1, OPF_TARGET)
OPCODE(SLICE, BADOP, BADOP, BADOP, BADOP, 1, OPF_TARGET)
/* Select - three input values */
OPCODE(SEL, BADOP, BADOP, BADOP, BADOP, 3, OPF_TARGET)
OPCODE(FMADD, BADOP, BADOP, BADOP, BADOP, 3, OPF_TARGET)
/* Memory */
OPCODE(LOAD, BADOP, BADOP, BADOP, BADOP, 1, OPF_TARGET)
OPCODE(STORE, BADOP, BADOP, BADOP, BADOP, 1, OPF_NONE)
/* Other */
OPCODE(PHISOURCE, BADOP, BADOP, BADOP, BADOP, 1, OPF_TARGET)
OPCODE(PHI, BADOP, BADOP, BADOP, BADOP, 0, OPF_TARGET)
OPCODE(LABEL, BADOP, BADOP, BADOP, BADOP, 0, OPF_TARGET)
OPCODE(SETVAL, BADOP, BADOP, BADOP, BADOP, 0, OPF_TARGET)
OPCODE(SETFVAL, BADOP, BADOP, BADOP, BADOP, 0, OPF_TARGET)
OPCODE(CALL, BADOP, BADOP, BADOP, BADOP, 1, OPF_TARGET)
OPCODE(INLINED_CALL, BADOP, BADOP, BADOP, BADOP, 0, OPF_NONE)
OPCODE(NOP, BADOP, BADOP, BADOP, BADOP, 0, OPF_NONE)
OPCODE(DEATHNOTE, BADOP, BADOP, BADOP, BADOP, 0, OPF_NONE)
OPCODE(ASM, BADOP, BADOP, BADOP, BADOP, 0, OPF_NONE)
/* Sparse tagging (line numbers, context, whatever) */
OPCODE(CONTEXT, BADOP, BADOP, BADOP, BADOP, 0, OPF_NONE)
OPCODE(RANGE, BADOP, BADOP, BADOP, BADOP, 3, OPF_NONE)
/* Needed to translate SSA back to normal form */
OPCODE(COPY, BADOP, BADOP, BADOP, BADOP, 1, OPF_TARGET)
sparse-0.6.4/opcode.h 0000664 0000000 0000000 00000002043 14115310122 0014425 0 ustar 00root root 0000000 0000000 #ifndef OPCODE_H
#define OPCODE_H
#include "symbol.h"
enum opcode {
#define OPCODE(OP,NG,SW,SG,TF,N,FL) OP_##OP,
#define OPCODE_RANGE(OP,S,E) OP_##OP = OP_##S, OP_##OP##_END = OP_##E,
#include "opcode.def"
#undef OPCODE
#undef OPCODE_RANGE
OP_LAST, /* keep this one last! */
};
extern const struct opcode_table {
int negate:8;
int swap:8;
int sign:8;
int to_float:8;
unsigned int arity:2;
unsigned int :6;
unsigned int flags:8;
#define OPF_NONE 0
#define OPF_TARGET (1 << 0)
#define OPF_COMMU (1 << 1)
#define OPF_ASSOC (1 << 2)
#define OPF_UNOP (1 << 3)
#define OPF_BINOP (1 << 4)
#define OPF_COMPARE (1 << 5)
#define OPF_SIGNED (1 << 6)
#define OPF_UNSIGNED (1 << 7)
} opcode_table[];
static inline int opcode_negate(int opcode)
{
return opcode_table[opcode].negate;
}
static inline int opcode_swap(int opcode)
{
return opcode_table[opcode].swap;
}
static inline int opcode_float(int opcode, struct symbol *type)
{
if (!type || !is_float_type(type))
return opcode;
return opcode_table[opcode].to_float;
}
#endif
sparse-0.6.4/optimize.c 0000664 0000000 0000000 00000005011 14115310122 0015005 0 ustar 00root root 0000000 0000000 // SPDX-License-Identifier: MIT
//
// Copyright (C) 2004 Linus Torvalds
// Copyright (C) 2004 Christopher Li
///
// Optimization main loop
// ----------------------
#include
#include "optimize.h"
#include "flowgraph.h"
#include "linearize.h"
#include "liveness.h"
#include "simplify.h"
#include "flow.h"
#include "cse.h"
#include "ir.h"
#include "ssa.h"
int repeat_phase;
static void clear_symbol_pseudos(struct entrypoint *ep)
{
pseudo_t pseudo;
FOR_EACH_PTR(ep->accesses, pseudo) {
pseudo->sym->pseudo = NULL;
} END_FOR_EACH_PTR(pseudo);
}
static void clean_up_insns(struct entrypoint *ep)
{
struct basic_block *bb;
FOR_EACH_PTR(ep->bbs, bb) {
struct instruction *insn;
FOR_EACH_PTR(bb->insns, insn) {
if (!insn->bb)
continue;
repeat_phase |= simplify_instruction(insn);
if (!insn->bb)
continue;
assert(insn->bb == bb);
cse_collect(insn);
} END_FOR_EACH_PTR(insn);
} END_FOR_EACH_PTR(bb);
}
static void cleanup_cfg(struct entrypoint *ep)
{
kill_unreachable_bbs(ep);
domtree_build(ep);
}
///
// optimization main loop
void optimize(struct entrypoint *ep)
{
if (fdump_ir & PASS_LINEARIZE)
show_entry(ep);
/*
* Do trivial flow simplification - branches to
* branches, kill dead basicblocks etc
*/
kill_unreachable_bbs(ep);
ir_validate(ep);
cfg_postorder(ep);
if (simplify_cfg_early(ep))
kill_unreachable_bbs(ep);
ir_validate(ep);
domtree_build(ep);
/*
* Turn symbols into pseudos
*/
if (fpasses & PASS_MEM2REG)
ssa_convert(ep);
ir_validate(ep);
if (fdump_ir & PASS_MEM2REG)
show_entry(ep);
if (!(fpasses & PASS_OPTIM))
return;
repeat:
/*
* Remove trivial instructions, and try to CSE
* the rest.
*/
do {
simplify_memops(ep);
do {
repeat_phase = 0;
clean_up_insns(ep);
if (repeat_phase & REPEAT_CFG_CLEANUP)
kill_unreachable_bbs(ep);
cse_eliminate(ep);
simplify_memops(ep);
} while (repeat_phase);
pack_basic_blocks(ep);
if (repeat_phase & REPEAT_CFG_CLEANUP)
cleanup_cfg(ep);
} while (repeat_phase);
vrfy_flow(ep);
/* Cleanup */
clear_symbol_pseudos(ep);
/* And track pseudo register usage */
track_pseudo_liveness(ep);
/*
* Some flow optimizations can only effectively
* be done when we've done liveness analysis. But
* if they trigger, we need to start all over
* again
*/
if (simplify_flow(ep)) {
clear_liveness(ep);
if (repeat_phase & REPEAT_CFG_CLEANUP)
cleanup_cfg(ep);
goto repeat;
}
/* Finally, add deathnotes to pseudos now that we have them */
if (dbg_dead)
track_pseudo_death(ep);
}
sparse-0.6.4/optimize.h 0000664 0000000 0000000 00000000172 14115310122 0015015 0 ustar 00root root 0000000 0000000 #ifndef OPTIMIZE_H
#define OPTIMIZE_H
struct entrypoint;
/* optimize.c */
void optimize(struct entrypoint *ep);
#endif
sparse-0.6.4/options.c 0000664 0000000 0000000 00000057734 14115310122 0014663 0 ustar 00root root 0000000 0000000 // SPDX-License-Identifier: MIT
/*
* 'sparse' library helper routines.
*
* Copyright (C) 2003 Transmeta Corp.
* 2003-2004 Linus Torvalds
* 2017-2020 Luc Van Oostenryck
*/
#include "options.h"
#include "lib.h"
#include "machine.h"
#include "target.h"
#include
#include
#include
#include
#ifndef __GNUC__
# define __GNUC__ 2
# define __GNUC_MINOR__ 95
# define __GNUC_PATCHLEVEL__ 0
#endif
enum flag_type {
FLAG_OFF,
FLAG_ON,
FLAG_FORCE_OFF
};
int die_if_error = 0;
int do_output = 1;
int gcc_major = __GNUC__;
int gcc_minor = __GNUC_MINOR__;
int gcc_patchlevel = __GNUC_PATCHLEVEL__;
int has_error = 0;
int optimize_level;
int optimize_size;
int preprocess_only;
int preprocessing;
int verbose;
#define CMDLINE_INCLUDE 20
int cmdline_include_nr = 0;
char *cmdline_include[CMDLINE_INCLUDE];
const char *base_filename;
const char *diag_prefix = "";
const char *gcc_base_dir = GCC_BASE;
const char *multiarch_dir = MULTIARCH_TRIPLET;
const char *outfile = NULL;
enum standard standard = STANDARD_GNU89;
int arch_big_endian = ARCH_BIG_ENDIAN;
int arch_cmodel = CMODEL_UNKNOWN;
int arch_fp_abi = FP_ABI_NATIVE;
int arch_m64 = ARCH_M64_DEFAULT;
int arch_msize_long = 0;
int arch_os = OS_NATIVE;
int dbg_compound = 0;
int dbg_dead = 0;
int dbg_domtree = 0;
int dbg_entry = 0;
int dbg_ir = 0;
int dbg_postorder = 0;
int dump_macro_defs = 0;
int dump_macros_only = 0;
unsigned long fdump_ir;
int fhosted = 1;
unsigned int fmax_errors = 100;
unsigned int fmax_warnings = 100;
int fmem_report = 0;
unsigned long long fmemcpy_max_count = 100000;
unsigned long fpasses = ~0UL;
int fpic = 0;
int fpie = 0;
int fshort_wchar = 0;
int funsigned_bitfields = 0;
int funsigned_char = 0;
int Waddress = 0;
int Waddress_space = 1;
int Wbitwise = 1;
int Wbitwise_pointer = 0;
int Wcast_from_as = 0;
int Wcast_to_as = 0;
int Wcast_truncate = 1;
int Wconstant_suffix = 0;
int Wconstexpr_not_const = 0;
int Wcontext = 1;
int Wdecl = 1;
int Wdeclarationafterstatement = -1;
int Wdefault_bitfield_sign = 0;
int Wdesignated_init = 1;
int Wdo_while = 0;
int Wenum_mismatch = 1;
int Wexternal_function_has_definition = 1;
int Wflexible_array_array = 1;
int Wflexible_array_nested = 0;
int Wflexible_array_sizeof = 0;
int Wflexible_array_union = 0;
int Wimplicit_int = 1;
int Winit_cstring = 0;
int Wint_to_pointer_cast = 1;
int Wmemcpy_max_count = 1;
int Wnewline_eof = 1;
int Wnon_pointer_null = 1;
int Wold_initializer = 1;
int Wold_style_definition = 1;
int Wone_bit_signed_bitfield = 1;
int Woverride_init = 1;
int Woverride_init_all = 0;
int Woverride_init_whole_range = 0;
int Wparen_string = 0;
int Wpast_deep_designator = 0;
int Wpedantic = 0;
int Wpointer_arith = 0;
int Wpointer_to_int_cast = 1;
int Wptr_subtraction_blows = 0;
int Wreturn_void = 0;
int Wshadow = 0;
int Wshift_count_negative = 1;
int Wshift_count_overflow = 1;
int Wsizeof_bool = 0;
int Wsparse_error = FLAG_FORCE_OFF;
int Wstrict_prototypes = 1;
int Wtautological_compare = 0;
int Wtransparent_union = 0;
int Wtypesign = 0;
int Wundef = 0;
int Wuninitialized = 1;
int Wunion_cast = 0;
int Wuniversal_initializer = 0;
int Wunknown_attribute = 0;
int Wvla = 1;
////////////////////////////////////////////////////////////////////////////////
// Helpers for option parsing
static const char *match_option(const char *arg, const char *prefix)
{
unsigned int n = strlen(prefix);
if (strncmp(arg, prefix, n) == 0)
return arg + n;
return NULL;
}
struct val_map {
const char *name;
int val;
};
static int handle_subopt_val(const char *opt, const char *arg, const struct val_map *map, int *flag)
{
const char *name;
if (*arg++ != '=')
die("missing argument for option '%s'", opt);
for (;(name = map->name); map++) {
if (strcmp(name, arg) == 0 || strcmp(name, "*") == 0) {
*flag = map->val;
return 1;
}
if (strcmp(name, "?") == 0)
die("invalid argument '%s' in option '%s'", arg, opt);
}
return 0;
}
struct mask_map {
const char *name;
unsigned long mask;
};
static int apply_mask(unsigned long *val, const char *str, unsigned len, const struct mask_map *map, int neg)
{
const char *name;
for (;(name = map->name); map++) {
if (!strncmp(name, str, len) && !name[len]) {
if (neg == 0)
*val |= map->mask;
else
*val &= ~map->mask;
return 0;
}
}
return 1;
}
static int handle_suboption_mask(const char *arg, const char *opt, const struct mask_map *map, unsigned long *flag)
{
if (*opt == '\0') {
apply_mask(flag, "", 0, map, 0);
return 1;
}
if (*opt++ != '=')
return 0;
while (1) {
unsigned int len = strcspn(opt, ",+");
int neg = 0;
if (len == 0)
goto end;
if (!strncmp(opt, "no-", 3)) {
opt += 3;
len -= 3;
neg = 1;
}
if (apply_mask(flag, opt, len, map, neg))
die("error: wrong option '%.*s' for \'%s\'", len, opt, arg);
end:
opt += len;
if (*opt++ == '\0')
break;
}
return 1;
}
#define OPT_INVERSE 1
#define OPT_VAL 2
struct flag {
const char *name;
int *flag;
int (*fun)(const char *arg, const char *opt, const struct flag *, int options);
unsigned long mask;
int val;
};
static int handle_switches(const char *ori, const char *opt, const struct flag *flags)
{
const char *arg = opt;
int val = 1;
// Prefixe "no-" mean to turn flag off.
if (strncmp(arg, "no-", 3) == 0) {
arg += 3;
val = 0;
}
for (; flags->name; flags++) {
const char *opt = match_option(arg, flags->name);
int rc;
if (!opt)
continue;
if (flags->fun) {
int options = 0;
if (!val)
options |= OPT_INVERSE;
if ((rc = flags->fun(ori, opt, flags, options)))
return rc;
}
// boolean flag
if (opt[0] == '\0' && flags->flag) {
if (flags->mask & OPT_VAL)
val = flags->val;
if (flags->mask & OPT_INVERSE)
val = !val;
*flags->flag = val;
return 1;
}
}
// not handled
return 0;
}
static char **handle_onoff_switch(char *arg, char **next, const struct flag flags[])
{
int flag = FLAG_ON;
char *p = arg + 1;
unsigned i;
// Prefixes "no" and "no-" mean to turn warning off.
if (p[0] == 'n' && p[1] == 'o') {
p += 2;
if (p[0] == '-')
p++;
flag = FLAG_FORCE_OFF;
}
for (i = 0; flags[i].name; i++) {
if (!strcmp(p,flags[i].name)) {
*flags[i].flag = flag;
return next;
}
}
// Unknown.
return NULL;
}
static void handle_onoff_switch_finalize(const struct flag flags[])
{
unsigned i;
for (i = 0; flags[i].name; i++) {
if (*flags[i].flag == FLAG_FORCE_OFF)
*flags[i].flag = FLAG_OFF;
}
}
static int handle_switch_setval(const char *arg, const char *opt, const struct flag *flag, int options)
{
*(flag->flag) = flag->mask;
return 1;
}
#define OPTNUM_ZERO_IS_INF 1
#define OPTNUM_UNLIMITED 2
#define OPT_NUMERIC(NAME, TYPE, FUNCTION) \
static int opt_##NAME(const char *arg, const char *opt, TYPE *ptr, int flag) \
{ \
char *end; \
TYPE val; \
\
val = FUNCTION(opt, &end, 0); \
if (*end != '\0' || end == opt) { \
if ((flag & OPTNUM_UNLIMITED) && !strcmp(opt, "unlimited")) \
val = ~val; \
else \
die("error: wrong argument to \'%s\'", arg); \
} \
if ((flag & OPTNUM_ZERO_IS_INF) && val == 0) \
val = ~val; \
*ptr = val; \
return 1; \
}
OPT_NUMERIC(ullong, unsigned long long, strtoull)
OPT_NUMERIC(uint, unsigned int, strtoul)
////////////////////////////////////////////////////////////////////////////////
// Option parsing
static char **handle_switch_a(char *arg, char **next)
{
if (!strcmp(arg, "ansi"))
standard = STANDARD_C89;
return next;
}
static char **handle_switch_D(char *arg, char **next)
{
const char *name = arg + 1;
const char *value = "1";
if (!*name) {
arg = *++next;
if (!arg)
die("argument to `-D' is missing");
name = arg;
}
for (;;arg++) {
char c;
c = *arg;
if (!c)
break;
if (c == '=') {
*arg = '\0';
value = arg + 1;
break;
}
}
add_pre_buffer("#define %s %s\n", name, value);
return next;
}
static char **handle_switch_d(char *arg, char **next)
{
char *arg_char = arg + 1;
/*
* -d, where is a sequence of characters, not preceded
* by a space. If you specify characters whose behaviour conflicts,
* the result is undefined.
*/
while (*arg_char) {
switch (*arg_char) {
case 'M': /* dump just the macro definitions */
dump_macros_only = 1;
dump_macro_defs = 0;
break;
case 'D': /* like 'M', but also output pre-processed text */
dump_macro_defs = 1;
dump_macros_only = 0;
break;
case 'N': /* like 'D', but only output macro names not bodies */
break;
case 'I': /* like 'D', but also output #include directives */
break;
case 'U': /* like 'D', but only output expanded macros */
break;
}
arg_char++;
}
return next;
}
static char **handle_switch_E(char *arg, char **next)
{
if (arg[1] == '\0')
preprocess_only = 1;
return next;
}
static int handle_ftabstop(const char *arg, const char *opt, const struct flag *flag, int options)
{
unsigned long val;
char *end;
if (*opt == '\0')
die("error: missing argument to \"%s\"", arg);
/* we silently ignore silly values */
val = strtoul(opt, &end, 10);
if (*end == '\0' && 1 <= val && val <= 100)
tabstop = val;
return 1;
}
static int handle_fpasses(const char *arg, const char *opt, const struct flag *flag, int options)
{
unsigned long mask;
mask = flag->mask;
if (*opt == '\0') {
if (options & OPT_INVERSE)
fpasses &= ~mask;
else
fpasses |= mask;
return 1;
}
if (options & OPT_INVERSE)
return 0;
if (!strcmp(opt, "-enable")) {
fpasses |= mask;
return 1;
}
if (!strcmp(opt, "-disable")) {
fpasses &= ~mask;
return 1;
}
if (!strcmp(opt, "=last")) {
// clear everything above
mask |= mask - 1;
fpasses &= mask;
return 1;
}
return 0;
}
static int handle_fdiagnostic_prefix(const char *arg, const char *opt, const struct flag *flag, int options)
{
switch (*opt) {
case '\0':
diag_prefix = "sparse: ";
return 1;
case '=':
diag_prefix = xasprintf("%s: ", opt+1);
return 1;
default:
return 0;
}
}
static int handle_fdump_ir(const char *arg, const char *opt, const struct flag *flag, int options)
{
static const struct mask_map dump_ir_options[] = {
{ "", PASS_LINEARIZE },
{ "linearize", PASS_LINEARIZE },
{ "mem2reg", PASS_MEM2REG },
{ "final", PASS_FINAL },
{ },
};
return handle_suboption_mask(arg, opt, dump_ir_options, &fdump_ir);
}
static int handle_fmemcpy_max_count(const char *arg, const char *opt, const struct flag *flag, int options)
{
opt_ullong(arg, opt, &fmemcpy_max_count, OPTNUM_ZERO_IS_INF|OPTNUM_UNLIMITED);
return 1;
}
static int handle_fmax_errors(const char *arg, const char *opt, const struct flag *flag, int options)
{
opt_uint(arg, opt, &fmax_errors, OPTNUM_UNLIMITED);
return 1;
}
static int handle_fmax_warnings(const char *arg, const char *opt, const struct flag *flag, int options)
{
opt_uint(arg, opt, &fmax_warnings, OPTNUM_UNLIMITED);
return 1;
}
static struct flag fflags[] = {
{ "diagnostic-prefix", NULL, handle_fdiagnostic_prefix },
{ "dump-ir", NULL, handle_fdump_ir },
{ "freestanding", &fhosted, NULL, OPT_INVERSE },
{ "hosted", &fhosted },
{ "linearize", NULL, handle_fpasses, PASS_LINEARIZE },
{ "max-errors=", NULL, handle_fmax_errors },
{ "max-warnings=", NULL, handle_fmax_warnings },
{ "mem-report", &fmem_report },
{ "memcpy-max-count=", NULL, handle_fmemcpy_max_count },
{ "tabstop=", NULL, handle_ftabstop },
{ "mem2reg", NULL, handle_fpasses, PASS_MEM2REG },
{ "optim", NULL, handle_fpasses, PASS_OPTIM },
{ "pic", &fpic, handle_switch_setval, 1 },
{ "PIC", &fpic, handle_switch_setval, 2 },
{ "pie", &fpie, handle_switch_setval, 1 },
{ "PIE", &fpie, handle_switch_setval, 2 },
{ "signed-bitfields", &funsigned_bitfields, NULL, OPT_INVERSE },
{ "unsigned-bitfields", &funsigned_bitfields, NULL, },
{ "signed-char", &funsigned_char, NULL, OPT_INVERSE },
{ "short-wchar", &fshort_wchar },
{ "unsigned-char", &funsigned_char, NULL, },
{ },
};
static char **handle_switch_f(char *arg, char **next)
{
if (handle_switches(arg-1, arg+1, fflags))
return next;
return next;
}
static char **handle_switch_G(char *arg, char **next)
{
if (!strcmp(arg, "G") && *next)
return next + 1; // "-G 0"
else
return next; // "-G0" or (bogus) terminal "-G"
}
static char **handle_base_dir(char *arg, char **next)
{
gcc_base_dir = *++next;
if (!gcc_base_dir)
die("missing argument for -gcc-base-dir option");
return next;
}
static char **handle_switch_g(char *arg, char **next)
{
if (!strcmp(arg, "gcc-base-dir"))
return handle_base_dir(arg, next);
return next;
}
static char **handle_switch_I(char *arg, char **next)
{
char *path = arg+1;
switch (arg[1]) {
case '-':
add_pre_buffer("#split_include\n");
break;
case '\0': /* Plain "-I" */
path = *++next;
if (!path)
die("missing argument for -I option");
/* Fall through */
default:
add_pre_buffer("#add_include \"%s/\"\n", path);
}
return next;
}
static void add_cmdline_include(char *filename)
{
if (cmdline_include_nr >= CMDLINE_INCLUDE)
die("too many include files for %s\n", filename);
cmdline_include[cmdline_include_nr++] = filename;
}
static char **handle_switch_i(char *arg, char **next)
{
if (*next && !strcmp(arg, "include"))
add_cmdline_include(*++next);
else if (*next && !strcmp(arg, "imacros"))
add_cmdline_include(*++next);
else if (*next && !strcmp(arg, "isystem")) {
char *path = *++next;
if (!path)
die("missing argument for -isystem option");
add_pre_buffer("#add_isystem \"%s/\"\n", path);
} else if (*next && !strcmp(arg, "idirafter")) {
char *path = *++next;
if (!path)
die("missing argument for -idirafter option");
add_pre_buffer("#add_dirafter \"%s/\"\n", path);
}
return next;
}
static char **handle_switch_M(char *arg, char **next)
{
if (!strcmp(arg, "MF") || !strcmp(arg,"MQ") || !strcmp(arg,"MT")) {
if (!*next)
die("missing argument for -%s option", arg);
return next + 1;
}
return next;
}
static int handle_march(const char *opt, const char *arg, const struct flag *flag, int options)
{
if (arch_target->parse_march)
arch_target->parse_march(arg);
return 1;
}
static int handle_mcmodel(const char *opt, const char *arg, const struct flag *flag, int options)
{
static const struct val_map cmodels[] = {
{ "kernel", CMODEL_KERNEL },
{ "large", CMODEL_LARGE },
{ "medany", CMODEL_MEDANY },
{ "medium", CMODEL_MEDIUM },
{ "medlow", CMODEL_MEDLOW },
{ "small", CMODEL_SMALL },
{ "tiny", CMODEL_TINY },
{ },
};
return handle_subopt_val(opt, arg, cmodels, flag->flag);
}
static int handle_mfloat_abi(const char *opt, const char *arg, const struct flag *flag, int options) {
static const struct val_map fp_abis[] = {
{ "hard", FP_ABI_HARD },
{ "soft", FP_ABI_SOFT },
{ "softfp", FP_ABI_HYBRID },
{ "?" },
};
return handle_subopt_val(opt, arg, fp_abis, flag->flag);
}
static char **handle_multiarch_dir(char *arg, char **next)
{
multiarch_dir = *++next;
if (!multiarch_dir)
die("missing argument for -multiarch-dir option");
return next;
}
static const struct flag mflags[] = {
{ "64", &arch_m64, NULL, OPT_VAL, ARCH_LP64 },
{ "32", &arch_m64, NULL, OPT_VAL, ARCH_LP32 },
{ "31", &arch_m64, NULL, OPT_VAL, ARCH_LP32 },
{ "16", &arch_m64, NULL, OPT_VAL, ARCH_LP32 },
{ "x32",&arch_m64, NULL, OPT_VAL, ARCH_X32 },
{ "size-llp64", &arch_m64, NULL, OPT_VAL, ARCH_LLP64 },
{ "size-long", &arch_msize_long },
{ "arch=", NULL, handle_march },
{ "big-endian", &arch_big_endian, NULL },
{ "little-endian", &arch_big_endian, NULL, OPT_INVERSE },
{ "cmodel", &arch_cmodel, handle_mcmodel },
{ "float-abi", &arch_fp_abi, handle_mfloat_abi },
{ "hard-float", &arch_fp_abi, NULL, OPT_VAL, FP_ABI_HARD },
{ "soft-float", &arch_fp_abi, NULL, OPT_VAL, FP_ABI_SOFT },
{ }
};
static char **handle_switch_m(char *arg, char **next)
{
if (!strcmp(arg, "multiarch-dir")) {
return handle_multiarch_dir(arg, next);
} else {
handle_switches(arg-1, arg+1, mflags);
}
return next;
}
static char **handle_nostdinc(char *arg, char **next)
{
add_pre_buffer("#nostdinc\n");
return next;
}
static char **handle_switch_n(char *arg, char **next)
{
if (!strcmp(arg, "nostdinc"))
return handle_nostdinc(arg, next);
return next;
}
static char **handle_switch_O(char *arg, char **next)
{
int level = 1;
if (arg[1] >= '0' && arg[1] <= '9')
level = arg[1] - '0';
optimize_level = level;
optimize_size = arg[1] == 's';
return next;
}
static char **handle_switch_o(char *arg, char **next)
{
if (!strcmp(arg, "o")) { // "-o foo"
if (!*++next)
die("argument to '-o' is missing");
outfile = *next;
}
// else "-ofoo"
return next;
}
static const struct flag pflags[] = {
{ "pedantic", &Wpedantic, NULL, OPT_VAL, FLAG_ON },
{ }
};
static char **handle_switch_p(char *arg, char **next)
{
handle_switches(arg-1, arg, pflags);
return next;
}
static char **handle_switch_s(const char *arg, char **next)
{
if ((arg = match_option(arg, "std="))) {
if (!strcmp(arg, "c89") ||
!strcmp(arg, "iso9899:1990"))
standard = STANDARD_C89;
else if (!strcmp(arg, "iso9899:199409"))
standard = STANDARD_C94;
else if (!strcmp(arg, "c99") ||
!strcmp(arg, "c9x") ||
!strcmp(arg, "iso9899:1999") ||
!strcmp(arg, "iso9899:199x"))
standard = STANDARD_C99;
else if (!strcmp(arg, "gnu89"))
standard = STANDARD_GNU89;
else if (!strcmp(arg, "gnu99") || !strcmp(arg, "gnu9x"))
standard = STANDARD_GNU99;
else if (!strcmp(arg, "c11") ||
!strcmp(arg, "c1x") ||
!strcmp(arg, "iso9899:2011"))
standard = STANDARD_C11;
else if (!strcmp(arg, "gnu11"))
standard = STANDARD_GNU11;
else if (!strcmp(arg, "c17") ||
!strcmp(arg, "c18") ||
!strcmp(arg, "iso9899:2017") ||
!strcmp(arg, "iso9899:2018"))
standard = STANDARD_C17;
else if (!strcmp(arg, "gnu17") ||
!strcmp(arg, "gnu18"))
standard = STANDARD_GNU17;
else
die("Unsupported C dialect");
}
return next;
}
static char **handle_switch_U(char *arg, char **next)
{
const char *name = arg + 1;
if (*name == '\0') {
name = *++next;
if (!name)
die("argument to `-U' is missing");
}
add_pre_buffer("#undef %s\n", name);
return next;
}
static struct flag debugs[] = {
{ "compound", &dbg_compound},
{ "dead", &dbg_dead},
{ "domtree", &dbg_domtree},
{ "entry", &dbg_entry},
{ "ir", &dbg_ir},
{ "postorder", &dbg_postorder},
{ }
};
static char **handle_switch_v(char *arg, char **next)
{
char ** ret = handle_onoff_switch(arg, next, debugs);
if (ret)
return ret;
// Unknown.
do {
verbose++;
} while (*++arg == 'v');
return next;
}
static void handle_switch_v_finalize(void)
{
handle_onoff_switch_finalize(debugs);
}
static const struct flag warnings[] = {
{ "address", &Waddress },
{ "address-space", &Waddress_space },
{ "bitwise", &Wbitwise },
{ "bitwise-pointer", &Wbitwise_pointer},
{ "cast-from-as", &Wcast_from_as },
{ "cast-to-as", &Wcast_to_as },
{ "cast-truncate", &Wcast_truncate },
{ "constant-suffix", &Wconstant_suffix },
{ "constexpr-not-const", &Wconstexpr_not_const},
{ "context", &Wcontext },
{ "decl", &Wdecl },
{ "declaration-after-statement", &Wdeclarationafterstatement },
{ "default-bitfield-sign", &Wdefault_bitfield_sign },
{ "designated-init", &Wdesignated_init },
{ "do-while", &Wdo_while },
{ "enum-mismatch", &Wenum_mismatch },
{ "external-function-has-definition", &Wexternal_function_has_definition },
{ "flexible-array-array", &Wflexible_array_array },
{ "flexible-array-nested", &Wflexible_array_nested },
{ "flexible-array-sizeof", &Wflexible_array_sizeof },
{ "flexible-array-union", &Wflexible_array_union },
{ "implicit-int", &Wimplicit_int },
{ "init-cstring", &Winit_cstring },
{ "int-to-pointer-cast", &Wint_to_pointer_cast },
{ "memcpy-max-count", &Wmemcpy_max_count },
{ "non-pointer-null", &Wnon_pointer_null },
{ "newline-eof", &Wnewline_eof },
{ "old-initializer", &Wold_initializer },
{ "old-style-definition", &Wold_style_definition },
{ "one-bit-signed-bitfield", &Wone_bit_signed_bitfield },
{ "override-init", &Woverride_init },
{ "override-init-all", &Woverride_init_all },
{ "paren-string", &Wparen_string },
{ "past-deep-designator", &Wpast_deep_designator },
{ "pedantic", &Wpedantic },
{ "pointer-to-int-cast", &Wpointer_to_int_cast },
{ "ptr-subtraction-blows", &Wptr_subtraction_blows },
{ "return-void", &Wreturn_void },
{ "shadow", &Wshadow },
{ "shift-count-negative", &Wshift_count_negative },
{ "shift-count-overflow", &Wshift_count_overflow },
{ "sizeof-bool", &Wsizeof_bool },
{ "strict-prototypes", &Wstrict_prototypes },
{ "pointer-arith", &Wpointer_arith },
{ "sparse-error", &Wsparse_error },
{ "tautological-compare", &Wtautological_compare },
{ "transparent-union", &Wtransparent_union },
{ "typesign", &Wtypesign },
{ "undef", &Wundef },
{ "uninitialized", &Wuninitialized },
{ "union-cast", &Wunion_cast },
{ "universal-initializer", &Wuniversal_initializer },
{ "unknown-attribute", &Wunknown_attribute },
{ "vla", &Wvla },
{ }
};
static char **handle_switch_W(char *arg, char **next)
{
char ** ret = handle_onoff_switch(arg, next, warnings);
if (ret)
return ret;
if (!strcmp(arg, "Wsparse-all")) {
int i;
for (i = 0; warnings[i].name; i++) {
if (*warnings[i].flag != FLAG_FORCE_OFF)
*warnings[i].flag = FLAG_ON;
}
}
// Unknown.
return next;
}
static void handle_switch_W_finalize(void)
{
handle_onoff_switch_finalize(warnings);
/* default Wdeclarationafterstatement based on the C dialect */
if (-1 == Wdeclarationafterstatement) {
switch (standard) {
case STANDARD_C89:
case STANDARD_C94:
Wdeclarationafterstatement = 1;
break;
default:
Wdeclarationafterstatement = 0;
break;
}
}
}
static char **handle_switch_x(char *arg, char **next)
{
if (!*++next)
die("missing argument for -x option");
return next;
}
static char **handle_arch(char *arg, char **next)
{
enum machine mach;
if (*arg++ != '=')
die("missing argument for --arch option");
mach = target_parse(arg);
if (mach != MACH_UNKNOWN)
target_config(mach);
return next;
}
static char **handle_param(char *arg, char **next)
{
char *value = NULL;
/* For now just skip any '--param=*' or '--param *' */
if (*arg == '\0') {
value = *++next;
} else if (isspace((unsigned char)*arg) || *arg == '=') {
value = ++arg;
}
if (!value)
die("missing argument for --param option");
return next;
}
static char **handle_os(char *arg, char **next)
{
if (*arg++ != '=')
die("missing argument for --os option");
target_os(arg);
return next;
}
static char **handle_version(char *arg, char **next)
{
printf("%s\n", sparse_version);
exit(0);
}
struct switches {
const char *name;
char **(*fn)(char *, char **);
unsigned int prefix:1;
};
static char **handle_long_options(char *arg, char **next)
{
static struct switches cmd[] = {
{ "arch", handle_arch, 1 },
{ "os", handle_os, 1 },
{ "param", handle_param, 1 },
{ "version", handle_version },
{ NULL, NULL }
};
struct switches *s = cmd;
while (s->name) {
int optlen = strlen(s->name);
if (!strncmp(s->name, arg, optlen + !s->prefix))
return s->fn(arg + optlen, next);
s++;
}
return next;
}
char **handle_switch(char *arg, char **next)
{
switch (*arg) {
case 'a': return handle_switch_a(arg, next);
case 'D': return handle_switch_D(arg, next);
case 'd': return handle_switch_d(arg, next);
case 'E': return handle_switch_E(arg, next);
case 'f': return handle_switch_f(arg, next);
case 'g': return handle_switch_g(arg, next);
case 'G': return handle_switch_G(arg, next);
case 'I': return handle_switch_I(arg, next);
case 'i': return handle_switch_i(arg, next);
case 'M': return handle_switch_M(arg, next);
case 'm': return handle_switch_m(arg, next);
case 'n': return handle_switch_n(arg, next);
case 'o': return handle_switch_o(arg, next);
case 'O': return handle_switch_O(arg, next);
case 'p': return handle_switch_p(arg, next);
case 's': return handle_switch_s(arg, next);
case 'U': return handle_switch_U(arg, next);
case 'v': return handle_switch_v(arg, next);
case 'W': return handle_switch_W(arg, next);
case 'x': return handle_switch_x(arg, next);
case '-': return handle_long_options(arg + 1, next);
default:
break;
}
/*
* Ignore unknown command line options:
* they're probably gcc switches
*/
return next;
}
void handle_switch_finalize(void)
{
handle_switch_v_finalize();
handle_switch_W_finalize();
}
sparse-0.6.4/options.h 0000664 0000000 0000000 00000006645 14115310122 0014663 0 ustar 00root root 0000000 0000000 #ifndef OPTIONS_H
#define OPTIONS_H
enum {
CMODEL_UNKNOWN,
CMODEL_KERNEL,
CMODEL_LARGE,
CMODEL_MEDANY,
CMODEL_MEDIUM,
CMODEL_MEDLOW,
CMODEL_PIC,
CMODEL_SMALL,
CMODEL_TINY,
CMODEL_LAST,
};
enum standard {
STANDARD_NONE,
STANDARD_GNU,
STANDARD_C89,
STANDARD_GNU89 = STANDARD_C89 | STANDARD_GNU,
STANDARD_C94,
STANDARD_GNU94 = STANDARD_C94 | STANDARD_GNU,
STANDARD_C99,
STANDARD_GNU99 = STANDARD_C99 | STANDARD_GNU,
STANDARD_C11,
STANDARD_GNU11 = STANDARD_C11 | STANDARD_GNU,
STANDARD_C17,
STANDARD_GNU17 = STANDARD_C17 | STANDARD_GNU,
};
extern int die_if_error;
extern int do_output;
extern int gcc_major;
extern int gcc_minor;
extern int gcc_patchlevel;
extern int optimize_level;
extern int optimize_size;
extern int preprocess_only;
extern int preprocessing;
extern int repeat_phase;
extern int verbose;
extern int cmdline_include_nr;
extern char *cmdline_include[];
extern const char *base_filename;
extern const char *diag_prefix;
extern const char *gcc_base_dir;
extern const char *multiarch_dir;
extern const char *outfile;
extern enum standard standard;
extern unsigned int tabstop;
extern int arch_big_endian;
extern int arch_cmodel;
extern int arch_fp_abi;
extern int arch_m64;
extern int arch_msize_long;
extern int arch_os;
extern int dbg_compound;
extern int dbg_dead;
extern int dbg_domtree;
extern int dbg_entry;
extern int dbg_ir;
extern int dbg_postorder;
extern int dump_macro_defs;
extern int dump_macros_only;
extern unsigned long fdump_ir;
extern int fhosted;
extern unsigned int fmax_errors;
extern unsigned int fmax_warnings;
extern int fmem_report;
extern unsigned long long fmemcpy_max_count;
extern unsigned long fpasses;
extern int fpic;
extern int fpie;
extern int fshort_wchar;
extern int funsigned_bitfields;
extern int funsigned_char;
extern int Waddress;
extern int Waddress_space;
extern int Wbitwise;
extern int Wbitwise_pointer;
extern int Wcast_from_as;
extern int Wcast_to_as;
extern int Wcast_truncate;
extern int Wconstant_suffix;
extern int Wconstexpr_not_const;
extern int Wcontext;
extern int Wdecl;
extern int Wdeclarationafterstatement;
extern int Wdefault_bitfield_sign;
extern int Wdesignated_init;
extern int Wdo_while;
extern int Wenum_mismatch;
extern int Wexternal_function_has_definition;
extern int Wflexible_array_array;
extern int Wflexible_array_nested;
extern int Wflexible_array_sizeof;
extern int Wflexible_array_union;
extern int Wimplicit_int;
extern int Winit_cstring;
extern int Wint_to_pointer_cast;
extern int Wmemcpy_max_count;
extern int Wnewline_eof;
extern int Wnon_pointer_null;
extern int Wold_initializer;
extern int Wold_style_definition;
extern int Wone_bit_signed_bitfield;
extern int Woverride_init;
extern int Woverride_init_all;
extern int Woverride_init_whole_range;
extern int Wparen_string;
extern int Wpast_deep_designator;
extern int Wpedantic;
extern int Wpointer_arith;
extern int Wpointer_to_int_cast;
extern int Wptr_subtraction_blows;
extern int Wreturn_void;
extern int Wshadow;
extern int Wshift_count_negative;
extern int Wshift_count_overflow;
extern int Wsizeof_bool;
extern int Wsparse_error;
extern int Wstrict_prototypes;
extern int Wtautological_compare;
extern int Wtransparent_union;
extern int Wtypesign;
extern int Wundef;
extern int Wuninitialized;
extern int Wunion_cast;
extern int Wuniversal_initializer;
extern int Wunknown_attribute;
extern int Wvla;
extern char **handle_switch(char *arg, char **next);
extern void handle_switch_finalize(void);
#endif
sparse-0.6.4/parse.c 0000664 0000000 0000000 00000247177 14115310122 0014304 0 ustar 00root root 0000000 0000000 /*
* Stupid C parser, version 1e-6.
*
* Let's see how hard this is to do.
*
* Copyright (C) 2003 Transmeta Corp.
* 2003-2004 Linus Torvalds
* Copyright (C) 2004 Christopher Li
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include "lib.h"
#include "allocate.h"
#include "token.h"
#include "parse.h"
#include "symbol.h"
#include "scope.h"
#include "expression.h"
#include "target.h"
static struct symbol_list **function_symbol_list;
struct symbol_list *function_computed_target_list;
struct statement_list *function_computed_goto_list;
static struct token *statement(struct token *token, struct statement **tree);
static struct token *handle_attributes(struct token *token, struct decl_state *ctx);
typedef struct token *declarator_t(struct token *, struct symbol *, struct decl_state *);
static declarator_t
struct_specifier, union_specifier, enum_specifier,
attribute_specifier, typeof_specifier,
storage_specifier, thread_specifier;
static declarator_t generic_qualifier;
static declarator_t autotype_specifier;
static struct token *parse_if_statement(struct token *token, struct statement *stmt);
static struct token *parse_return_statement(struct token *token, struct statement *stmt);
static struct token *parse_loop_iterator(struct token *token, struct statement *stmt);
static struct token *parse_default_statement(struct token *token, struct statement *stmt);
static struct token *parse_case_statement(struct token *token, struct statement *stmt);
static struct token *parse_switch_statement(struct token *token, struct statement *stmt);
static struct token *parse_for_statement(struct token *token, struct statement *stmt);
static struct token *parse_while_statement(struct token *token, struct statement *stmt);
static struct token *parse_do_statement(struct token *token, struct statement *stmt);
static struct token *parse_goto_statement(struct token *token, struct statement *stmt);
static struct token *parse_context_statement(struct token *token, struct statement *stmt);
static struct token *parse_range_statement(struct token *token, struct statement *stmt);
static struct token *parse_asm_statement(struct token *token, struct statement *stmt);
static struct token *toplevel_asm_declaration(struct token *token, struct symbol_list **list);
static struct token *parse_static_assert(struct token *token, struct symbol_list **unused);
typedef struct token *attr_t(struct token *, struct symbol *,
struct decl_state *);
static attr_t
attribute_packed, attribute_aligned, attribute_modifier,
attribute_function,
attribute_bitwise,
attribute_address_space, attribute_context,
attribute_designated_init,
attribute_transparent_union, ignore_attribute,
attribute_mode, attribute_force;
typedef struct symbol *to_mode_t(struct symbol *);
static to_mode_t
to_QI_mode, to_HI_mode, to_SI_mode, to_DI_mode, to_TI_mode;
static to_mode_t to_pointer_mode, to_word_mode;
enum {
Set_T = 1,
Set_S = 2,
Set_Char = 4,
Set_Int = 8,
Set_Double = 16,
Set_Float = 32,
Set_Signed = 64,
Set_Unsigned = 128,
Set_Short = 256,
Set_Long = 512,
Set_Vlong = 1024,
Set_Int128 = 2048,
Set_Any = Set_T | Set_Short | Set_Long | Set_Signed | Set_Unsigned
};
enum {
CInt = 0, CSInt, CUInt, CReal,
};
static void asm_modifier(struct token *token, unsigned long *mods, unsigned long mod)
{
if (*mods & mod)
warning(token->pos, "duplicated asm modifier");
*mods |= mod;
}
static struct symbol_op typedef_op = {
.type = KW_MODIFIER,
.declarator = storage_specifier,
};
static struct symbol_op inline_op = {
.type = KW_MODIFIER,
.declarator = generic_qualifier,
.asm_modifier = asm_modifier,
};
static struct symbol_op noreturn_op = {
.type = KW_MODIFIER,
.declarator = generic_qualifier,
};
static declarator_t alignas_specifier;
static struct symbol_op alignas_op = {
.type = KW_MODIFIER,
.declarator = alignas_specifier,
};
static struct symbol_op auto_op = {
.type = KW_MODIFIER,
.declarator = storage_specifier,
};
static struct symbol_op register_op = {
.type = KW_MODIFIER,
.declarator = storage_specifier,
};
static struct symbol_op static_op = {
.type = KW_MODIFIER|KW_STATIC,
.declarator = storage_specifier,
};
static struct symbol_op extern_op = {
.type = KW_MODIFIER,
.declarator = storage_specifier,
};
static struct symbol_op thread_op = {
.type = KW_MODIFIER,
.declarator = thread_specifier,
};
static struct symbol_op const_op = {
.type = KW_QUALIFIER,
.declarator = generic_qualifier,
};
static struct symbol_op volatile_op = {
.type = KW_QUALIFIER,
.declarator = generic_qualifier,
.asm_modifier = asm_modifier,
};
static struct symbol_op restrict_op = {
.type = KW_QUALIFIER,
.declarator = generic_qualifier,
};
static struct symbol_op atomic_op = {
.type = KW_QUALIFIER,
.declarator = generic_qualifier,
};
static struct symbol_op typeof_op = {
.type = KW_SPECIFIER,
.declarator = typeof_specifier,
.test = Set_Any,
.set = Set_S|Set_T,
};
static struct symbol_op autotype_op = {
.type = KW_SPECIFIER,
.declarator = autotype_specifier,
.test = Set_Any,
.set = Set_S|Set_T,
};
static struct symbol_op attribute_op = {
.type = KW_ATTRIBUTE,
.declarator = attribute_specifier,
};
static struct symbol_op struct_op = {
.type = KW_SPECIFIER,
.declarator = struct_specifier,
.test = Set_Any,
.set = Set_S|Set_T,
};
static struct symbol_op union_op = {
.type = KW_SPECIFIER,
.declarator = union_specifier,
.test = Set_Any,
.set = Set_S|Set_T,
};
static struct symbol_op enum_op = {
.type = KW_SPECIFIER,
.declarator = enum_specifier,
.test = Set_Any,
.set = Set_S|Set_T,
};
static struct symbol_op spec_op = {
.type = KW_SPECIFIER | KW_EXACT,
.test = Set_Any,
.set = Set_S|Set_T,
};
static struct symbol_op char_op = {
.type = KW_SPECIFIER,
.test = Set_T|Set_Long|Set_Short,
.set = Set_T|Set_Char,
.class = CInt,
};
static struct symbol_op int_op = {
.type = KW_SPECIFIER,
.test = Set_T,
.set = Set_T|Set_Int,
};
static struct symbol_op double_op = {
.type = KW_SPECIFIER,
.test = Set_T|Set_Signed|Set_Unsigned|Set_Short|Set_Vlong,
.set = Set_T|Set_Double,
.class = CReal,
};
static struct symbol_op float_op = {
.type = KW_SPECIFIER,
.test = Set_T|Set_Signed|Set_Unsigned|Set_Short|Set_Long,
.set = Set_T|Set_Float,
.class = CReal,
};
static struct symbol_op short_op = {
.type = KW_SPECIFIER,
.test = Set_S|Set_Char|Set_Float|Set_Double|Set_Long|Set_Short,
.set = Set_Short,
};
static struct symbol_op signed_op = {
.type = KW_SPECIFIER,
.test = Set_S|Set_Float|Set_Double|Set_Signed|Set_Unsigned,
.set = Set_Signed,
.class = CSInt,
};
static struct symbol_op unsigned_op = {
.type = KW_SPECIFIER,
.test = Set_S|Set_Float|Set_Double|Set_Signed|Set_Unsigned,
.set = Set_Unsigned,
.class = CUInt,
};
static struct symbol_op long_op = {
.type = KW_SPECIFIER,
.test = Set_S|Set_Char|Set_Float|Set_Short|Set_Vlong,
.set = Set_Long,
};
static struct symbol_op int128_op = {
.type = KW_SPECIFIER,
.test = Set_S|Set_T|Set_Char|Set_Short|Set_Int|Set_Float|Set_Double|Set_Long|Set_Vlong|Set_Int128,
.set = Set_T|Set_Int128|Set_Vlong,
.class = CInt,
};
static struct symbol_op if_op = {
.statement = parse_if_statement,
};
static struct symbol_op return_op = {
.statement = parse_return_statement,
};
static struct symbol_op loop_iter_op = {
.statement = parse_loop_iterator,
};
static struct symbol_op default_op = {
.statement = parse_default_statement,
};
static struct symbol_op case_op = {
.statement = parse_case_statement,
};
static struct symbol_op switch_op = {
.statement = parse_switch_statement,
};
static struct symbol_op for_op = {
.statement = parse_for_statement,
};
static struct symbol_op while_op = {
.statement = parse_while_statement,
};
static struct symbol_op do_op = {
.statement = parse_do_statement,
};
static struct symbol_op goto_op = {
.statement = parse_goto_statement,
};
static struct symbol_op __context___op = {
.statement = parse_context_statement,
.attribute = attribute_context,
};
static struct symbol_op range_op = {
.statement = parse_range_statement,
};
static struct symbol_op asm_op = {
.type = KW_ASM,
.statement = parse_asm_statement,
.toplevel = toplevel_asm_declaration,
};
static struct symbol_op static_assert_op = {
.toplevel = parse_static_assert,
};
static struct symbol_op packed_op = {
.attribute = attribute_packed,
};
static struct symbol_op aligned_op = {
.attribute = attribute_aligned,
};
static struct symbol_op attr_mod_op = {
.attribute = attribute_modifier,
};
static struct symbol_op attr_fun_op = {
.attribute = attribute_function,
};
static struct symbol_op attr_bitwise_op = {
.attribute = attribute_bitwise,
};
static struct symbol_op attr_force_op = {
.attribute = attribute_force,
};
static struct symbol_op address_space_op = {
.attribute = attribute_address_space,
};
static struct symbol_op mode_op = {
.attribute = attribute_mode,
};
static struct symbol_op context_op = {
.attribute = attribute_context,
};
static struct symbol_op designated_init_op = {
.attribute = attribute_designated_init,
};
static struct symbol_op transparent_union_op = {
.attribute = attribute_transparent_union,
};
static struct symbol_op ignore_attr_op = {
.attribute = ignore_attribute,
};
static struct symbol_op mode_QI_op = {
.type = KW_MODE,
.to_mode = to_QI_mode
};
static struct symbol_op mode_HI_op = {
.type = KW_MODE,
.to_mode = to_HI_mode
};
static struct symbol_op mode_SI_op = {
.type = KW_MODE,
.to_mode = to_SI_mode
};
static struct symbol_op mode_DI_op = {
.type = KW_MODE,
.to_mode = to_DI_mode
};
static struct symbol_op mode_TI_op = {
.type = KW_MODE,
.to_mode = to_TI_mode
};
static struct symbol_op mode_pointer_op = {
.type = KW_MODE,
.to_mode = to_pointer_mode
};
static struct symbol_op mode_word_op = {
.type = KW_MODE,
.to_mode = to_word_mode
};
/*
* Define the keyword and their effects.
* The entries in the 'typedef' and put in NS_TYPEDEF and
* are automatically set as reserved keyword while the ones
* in the 'keyword' table are just put in NS_KEYWORD.
*
* The entries are added via the 3 macros:
* N() for entries with "name" only,
* D() for entries with "name" & "__name__",
* A() for entries with "name", "__name" & "__name__",
* U() for entries with "__name" & "__name__".
*/
static struct init_keyword {
const char *name;
struct symbol_op *op;
struct symbol *type;
unsigned long mods;
} typedefs[] = {
#define N(I, O,...) { I, O,##__VA_ARGS__ }
#define D(I, O,...) N(I,O,##__VA_ARGS__ ), \
N("__" I "__",O,##__VA_ARGS__)
#define A(I, O,...) N(I,O,##__VA_ARGS__ ), \
N("__" I,O,##__VA_ARGS__), \
N("__" I "__",O,##__VA_ARGS__)
#define U(I, O,...) N("__" I,O,##__VA_ARGS__), \
N("__" I "__",O,##__VA_ARGS__)
/* Storage classes */
N("auto", &auto_op, .mods = MOD_AUTO),
N("register", ®ister_op, .mods = MOD_REGISTER),
N("static", &static_op, .mods = MOD_STATIC),
N("extern", &extern_op, .mods = MOD_EXTERN),
N("__thread", &thread_op),
N("_Thread_local", &thread_op),
A("inline", &inline_op, .mods = MOD_INLINE),
/* Typedef ... */
N("typedef", &typedef_op, .mods = MOD_USERTYPE),
A("typeof", &typeof_op),
N("__auto_type", &autotype_op),
/* Type qualifiers */
A("const", &const_op, .mods = MOD_CONST),
A("volatile", &volatile_op, .mods = MOD_VOLATILE),
A("restrict", &restrict_op, .mods = MOD_RESTRICT),
N("_Atomic", &atomic_op, .mods = MOD_ATOMIC),
N("_Noreturn", &noreturn_op, .mods = MOD_NORETURN),
N("_Alignas", &alignas_op),
U("attribute", &attribute_op),
/* Type specifiers */
N("struct", &struct_op),
N("union", &union_op),
N("enum", &enum_op),
N("void", &spec_op, .type = &void_ctype),
N("char", &char_op),
N("short", &short_op),
N("int", &int_op),
N("long", &long_op),
N("float", &float_op),
N("double", &double_op),
A("signed", &signed_op),
N("unsigned", &unsigned_op),
N("__int128", &int128_op),
N("_Bool", &spec_op, .type = &bool_ctype),
/* Predeclared types */
N("__builtin_va_list", &spec_op, .type = &ptr_ctype),
N("__builtin_ms_va_list",&spec_op, .type = &ptr_ctype),
N("__int128_t", &spec_op, .type = &sint128_ctype),
N("__uint128_t", &spec_op, .type = &uint128_ctype),
N("_Float32", &spec_op, .type = &float32_ctype),
N("_Float32x", &spec_op, .type = &float32x_ctype),
N("_Float64", &spec_op, .type = &float64_ctype),
N("_Float64x", &spec_op, .type = &float64x_ctype),
N("_Float128", &spec_op, .type = &float128_ctype),
}, keywords[] = {
/* Statements */
N("if", &if_op),
N("return", &return_op),
N("break", &loop_iter_op),
N("continue", &loop_iter_op),
N("default", &default_op),
N("case", &case_op),
N("switch", &switch_op),
N("for", &for_op),
N("while", &while_op),
N("do", &do_op),
N("goto", &goto_op),
A("asm", &asm_op),
N("context", &context_op),
N("__context__", &__context___op),
N("__range__", &range_op),
N("_Static_assert", &static_assert_op),
/* Attributes */
D("packed", &packed_op),
D("aligned", &aligned_op),
D("nocast", &attr_mod_op, .mods = MOD_NOCAST),
D("noderef", &attr_mod_op, .mods = MOD_NODEREF),
D("safe", &attr_mod_op, .mods = MOD_SAFE),
D("unused", &attr_mod_op, .mods = MOD_UNUSED),
D("externally_visible", &attr_mod_op, .mods = MOD_EXT_VISIBLE),
D("force", &attr_force_op),
D("bitwise", &attr_bitwise_op, .mods = MOD_BITWISE),
D("address_space", &address_space_op),
D("designated_init", &designated_init_op),
D("transparent_union", &transparent_union_op),
D("noreturn", &attr_fun_op, .mods = MOD_NORETURN),
D("pure", &attr_fun_op, .mods = MOD_PURE),
A("const", &attr_fun_op, .mods = MOD_PURE),
D("gnu_inline", &attr_fun_op, .mods = MOD_GNU_INLINE),
/* Modes */
D("mode", &mode_op),
D("QI", &mode_QI_op),
D("HI", &mode_HI_op),
D("SI", &mode_SI_op),
D("DI", &mode_DI_op),
D("TI", &mode_TI_op),
D("byte", &mode_QI_op),
D("pointer", &mode_pointer_op),
D("word", &mode_word_op),
};
static const char *ignored_attributes[] = {
#define GCC_ATTR(x) \
STRINGIFY(x), \
STRINGIFY(__##x##__),
#include "gcc-attr-list.h"
#undef GCC_ATTR
"bounded",
"__bounded__",
"__noclone",
"__nonnull",
"__nothrow",
};
static void init_keyword(int stream, struct init_keyword *kw, enum namespace ns)
{
struct symbol *sym = create_symbol(stream, kw->name, SYM_KEYWORD, ns);
sym->ident->keyword = 1;
sym->ident->reserved |= (ns == NS_TYPEDEF);
sym->ctype.modifiers = kw->mods;
sym->ctype.base_type = kw->type;
sym->op = kw->op;
}
void init_parser(int stream)
{
int i;
for (i = 0; i < ARRAY_SIZE(typedefs); i++)
init_keyword(stream, &typedefs[i], NS_TYPEDEF);
for (i = 0; i < ARRAY_SIZE(keywords); i++)
init_keyword(stream, &keywords[i], NS_KEYWORD);
for (i = 0; i < ARRAY_SIZE(ignored_attributes); i++) {
const char * name = ignored_attributes[i];
struct symbol *sym = create_symbol(stream, name, SYM_KEYWORD,
NS_KEYWORD);
if (!sym->op) {
sym->ident->keyword = 1;
sym->op = &ignore_attr_op;
}
}
}
static struct token *skip_to(struct token *token, int op)
{
while (!match_op(token, op) && !eof_token(token))
token = token->next;
return token;
}
static struct token bad_token = { .pos.type = TOKEN_BAD };
struct token *expect(struct token *token, int op, const char *where)
{
if (!match_op(token, op)) {
if (token != &bad_token) {
bad_token.next = token;
sparse_error(token->pos, "Expected %s %s", show_special(op), where);
sparse_error(token->pos, "got %s", show_token(token));
}
if (op == ';')
return skip_to(token, op);
return &bad_token;
}
return token->next;
}
///
// issue an error message on new parsing errors
// @token: the current token
// @errmsg: the error message
// If the current token is from a previous error, an error message
// has already been issued, so nothing more is done.
// Otherwise, @errmsg is displayed followed by the current token.
static void unexpected(struct token *token, const char *errmsg)
{
if (token == &bad_token)
return;
sparse_error(token->pos, "%s", errmsg);
sparse_error(token->pos, "got %s", show_token(token));
}
// Add a symbol to the list of function-local symbols
static void fn_local_symbol(struct symbol *sym)
{
if (function_symbol_list)
add_symbol(function_symbol_list, sym);
}
struct statement *alloc_statement(struct position pos, int type)
{
struct statement *stmt = __alloc_statement(0);
stmt->type = type;
stmt->pos = pos;
return stmt;
}
static struct token *struct_declaration_list(struct token *token, struct symbol_list **list);
static void apply_ctype(struct position pos, struct ctype *dst, struct ctype *src);
static void apply_modifiers(struct position pos, struct decl_state *ctx)
{
struct symbol *ctype;
if (!ctx->mode)
return;
ctype = ctx->mode->to_mode(ctx->ctype.base_type);
if (!ctype)
sparse_error(pos, "don't know how to apply mode to %s",
show_typename(ctx->ctype.base_type));
else
ctx->ctype.base_type = ctype;
}
static struct symbol * alloc_indirect_symbol(struct position pos, struct ctype *ctype, int type)
{
struct symbol *sym = alloc_symbol(pos, type);
sym->ctype.base_type = ctype->base_type;
sym->ctype.modifiers = ctype->modifiers;
ctype->base_type = sym;
ctype->modifiers = 0;
return sym;
}
/*
* NOTE! NS_LABEL is not just a different namespace,
* it also ends up using function scope instead of the
* regular symbol scope.
*/
struct symbol *label_symbol(struct token *token, int used)
{
struct symbol *sym = lookup_symbol(token->ident, NS_LABEL);
if (!sym) {
sym = alloc_symbol(token->pos, SYM_LABEL);
bind_symbol(sym, token->ident, NS_LABEL);
if (used)
sym->used = 1;
fn_local_symbol(sym);
}
return sym;
}
static struct token *struct_union_enum_specifier(enum type type,
struct token *token, struct decl_state *ctx,
struct token *(*parse)(struct token *, struct symbol *))
{
struct decl_state attr = { };
struct symbol *sym;
struct position *repos;
token = handle_attributes(token, &attr);
if (token_type(token) == TOKEN_IDENT) {
sym = lookup_symbol(token->ident, NS_STRUCT);
if (!sym ||
(is_outer_scope(sym->scope) &&
(match_op(token->next,';') || match_op(token->next,'{')))) {
// Either a new symbol, or else an out-of-scope
// symbol being redefined.
sym = alloc_symbol(token->pos, type);
bind_symbol(sym, token->ident, NS_STRUCT);
}
if (sym->type != type)
error_die(token->pos, "invalid tag applied to %s", show_typename (sym));
ctx->ctype.base_type = sym;
repos = &token->pos;
token = token->next;
if (!match_op(token, '{'))
return token;
// The following test is actually wrong for empty
// structs, but (1) they are not C99, (2) gcc does
// the same thing, and (3) it's easier.
if (sym->symbol_list)
error_die(token->pos, "redefinition of %s", show_typename (sym));
sym->pos = *repos;
// Mark the structure as needing re-examination
sym->examined = 0;
} else if (match_op(token, '{')) {
// private struct/union/enum type
sym = alloc_symbol(token->pos, type);
set_current_scope(sym); // used by dissect
ctx->ctype.base_type = sym;
} else {
sparse_error(token->pos, "expected declaration");
ctx->ctype.base_type = &bad_ctype;
return token;
}
token = parse(token->next, sym);
token = expect(token, '}', "at end of specifier");
attr.ctype.base_type = sym;
token = handle_attributes(token, &attr);
apply_ctype(token->pos, &sym->ctype, &attr.ctype);
sym->packed = attr.packed;
sym->endpos = token->pos;
return token;
}
static struct token *parse_struct_declaration(struct token *token, struct symbol *sym)
{
struct symbol *field, *last = NULL;
struct token *res;
res = struct_declaration_list(token, &sym->symbol_list);
FOR_EACH_PTR(sym->symbol_list, field) {
if (!field->ident) {
struct symbol *base = field->ctype.base_type;
if (base && base->type == SYM_BITFIELD)
continue;
}
if (last)
last->next_subobject = field;
last = field;
} END_FOR_EACH_PTR(field);
return res;
}
static struct token *parse_union_declaration(struct token *token, struct symbol *sym)
{
return struct_declaration_list(token, &sym->symbol_list);
}
static struct token *struct_specifier(struct token *token, struct symbol *sym, struct decl_state *ctx)
{
return struct_union_enum_specifier(SYM_STRUCT, token, ctx, parse_struct_declaration);
}
static struct token *union_specifier(struct token *token, struct symbol *sym, struct decl_state *ctx)
{
return struct_union_enum_specifier(SYM_UNION, token, ctx, parse_union_declaration);
}
///
// safe right shift
//
// This allow to use a shift amount as big (or bigger)
// than the width of the value to be shifted, in which case
// the result is, of course, 0.
static unsigned long long rshift(unsigned long long val, unsigned int n)
{
if (n >= (sizeof(val) * 8))
return 0;
return val >> n;
}
struct range {
long long neg;
unsigned long long pos;
};
static void update_range(struct range *range, unsigned long long uval, struct symbol *vtype)
{
long long sval = uval;
if (is_signed_type(vtype) && (sval < 0)) {
if (sval < range->neg)
range->neg = sval;
} else {
if (uval > range->pos)
range->pos = uval;
}
}
static int type_is_ok(struct symbol *type, struct range range)
{
int shift = type->bit_size;
int is_unsigned = type->ctype.modifiers & MOD_UNSIGNED;
if (!is_unsigned)
shift--;
if (rshift(range.pos, shift))
return 0;
if (range.neg == 0)
return 1;
if (is_unsigned)
return 0;
if (rshift(~range.neg, shift))
return 0;
return 1;
}
static struct range type_range(struct symbol *type)
{
struct range range;
unsigned int size = type->bit_size;
unsigned long long max;
long long min;
if (is_signed_type(type)) {
min = sign_bit(size);
max = min - 1;
} else {
min = 0;
max = bits_mask(size);
}
range.pos = max;
range.neg = min;
return range;
}
static int val_in_range(struct range *range, long long sval, struct symbol *vtype)
{
unsigned long long uval = sval;
if (is_signed_type(vtype) && (sval < 0))
return range->neg <= sval;
else
return uval <= range->pos;
}
static void cast_enum_list(struct symbol_list *list, struct symbol *base_type)
{
struct range irange = type_range(&int_ctype);
struct symbol *sym;
FOR_EACH_PTR(list, sym) {
struct expression *expr = sym->initializer;
struct symbol *ctype;
long long val;
if (expr->type != EXPR_VALUE)
continue;
ctype = expr->ctype;
val = get_expression_value(expr);
if (is_int_type(ctype) && val_in_range(&irange, val, ctype)) {
expr->ctype = &int_ctype;
continue;
}
cast_value(expr, base_type, expr, ctype);
expr->ctype = base_type;
} END_FOR_EACH_PTR(sym);
}
static struct token *parse_enum_declaration(struct token *token, struct symbol *parent)
{
unsigned long long lastval = 0;
struct symbol *ctype = NULL, *base_type = NULL;
struct range range = { };
int mix_bitwise = 0;
parent->examined = 1;
parent->ctype.base_type = &int_ctype;
while (token_type(token) == TOKEN_IDENT) {
struct expression *expr = NULL;
struct token *next = token->next;
struct decl_state ctx = { };
struct symbol *sym;
// FIXME: only 'deprecated' should be accepted
next = handle_attributes(next, &ctx);
if (match_op(next, '=')) {
next = constant_expression(next->next, &expr);
lastval = get_expression_value(expr);
ctype = &void_ctype;
if (expr && expr->ctype)
ctype = expr->ctype;
} else if (!ctype) {
ctype = &int_ctype;
} else if (is_int_type(ctype)) {
lastval++;
} else {
error_die(token->pos, "can't increment the last enum member");
}
if (!expr) {
expr = alloc_expression(token->pos, EXPR_VALUE);
expr->value = lastval;
expr->ctype = ctype;
}
sym = alloc_symbol(token->pos, SYM_NODE);
bind_symbol(sym, token->ident, NS_SYMBOL);
sym->ctype.modifiers &= ~MOD_ADDRESSABLE;
sym->initializer = expr;
sym->enum_member = 1;
sym->ctype.base_type = parent;
add_ptr_list(&parent->symbol_list, sym);
if (base_type != &bad_ctype) {
if (ctype->type == SYM_NODE)
ctype = ctype->ctype.base_type;
if (ctype->type == SYM_ENUM) {
if (ctype == parent)
ctype = base_type;
else
ctype = ctype->ctype.base_type;
}
/*
* base_type rules:
* - if all enums are of the same type, then
* the base_type is that type (two first
* cases)
* - if enums are of different types, they
* all have to be integer types, and the
* base type is at least "int_ctype".
* - otherwise the base_type is "bad_ctype".
*/
if (!base_type || ctype == &bad_ctype) {
base_type = ctype;
} else if (ctype == base_type) {
/* nothing */
} else if (is_int_type(base_type) && is_int_type(ctype)) {
base_type = &int_ctype;
} else if (is_restricted_type(base_type) != is_restricted_type(ctype)) {
if (!mix_bitwise++) {
warning(expr->pos, "mixed bitwiseness");
}
} else if (is_restricted_type(base_type) && base_type != ctype) {
sparse_error(expr->pos, "incompatible restricted type");
info(expr->pos, " expected: %s", show_typename(base_type));
info(expr->pos, " got: %s", show_typename(ctype));
base_type = &bad_ctype;
} else if (base_type != &bad_ctype) {
sparse_error(token->pos, "bad enum definition");
base_type = &bad_ctype;
}
parent->ctype.base_type = base_type;
}
if (is_int_type(base_type)) {
update_range(&range, lastval, ctype);
}
token = next;
sym->endpos = token->pos;
if (!match_op(token, ','))
break;
token = token->next;
}
if (!base_type) {
sparse_error(token->pos, "empty enum definition");
base_type = &bad_ctype;
}
else if (!is_int_type(base_type))
;
else if (type_is_ok(&uint_ctype, range))
base_type = &uint_ctype;
else if (type_is_ok(&int_ctype, range))
base_type = &int_ctype;
else if (type_is_ok(&ulong_ctype, range))
base_type = &ulong_ctype;
else if (type_is_ok(&long_ctype, range))
base_type = &long_ctype;
else if (type_is_ok(&ullong_ctype, range))
base_type = &ullong_ctype;
else if (type_is_ok(&llong_ctype, range))
base_type = &llong_ctype;
else
base_type = &bad_ctype;
parent->ctype.base_type = base_type;
parent->ctype.modifiers |= (base_type->ctype.modifiers & MOD_UNSIGNED);
parent->examined = 0;
if (mix_bitwise)
return token;
cast_enum_list(parent->symbol_list, base_type);
return token;
}
static struct token *enum_specifier(struct token *token, struct symbol *sym, struct decl_state *ctx)
{
struct token *ret = struct_union_enum_specifier(SYM_ENUM, token, ctx, parse_enum_declaration);
struct ctype *ctype = &ctx->ctype.base_type->ctype;
if (!ctype->base_type)
ctype->base_type = &incomplete_ctype;
return ret;
}
static struct token *typeof_specifier(struct token *token, struct symbol *sym, struct decl_state *ctx)
{
if (!match_op(token, '(')) {
sparse_error(token->pos, "expected '(' after typeof");
return token;
}
if (lookup_type(token->next)) {
struct symbol *sym;
token = typename(token->next, &sym, NULL);
ctx->ctype.base_type = sym->ctype.base_type;
apply_ctype(token->pos, &ctx->ctype, &sym->ctype);
} else {
struct symbol *typeof_sym = alloc_symbol(token->pos, SYM_TYPEOF);
token = parse_expression(token->next, &typeof_sym->initializer);
typeof_sym->endpos = token->pos;
if (!typeof_sym->initializer) {
sparse_error(token->pos, "expected expression after the '(' token");
typeof_sym = &bad_ctype;
}
ctx->ctype.base_type = typeof_sym;
}
return expect(token, ')', "after typeof");
}
static struct token *autotype_specifier(struct token *token, struct symbol *sym, struct decl_state *ctx)
{
ctx->ctype.base_type = &autotype_ctype;
ctx->autotype = 1;
return token;
}
static struct token *ignore_attribute(struct token *token, struct symbol *attr, struct decl_state *ctx)
{
struct expression *expr = NULL;
if (match_op(token, '('))
token = parens_expression(token, &expr, "in attribute");
return token;
}
static struct token *attribute_packed(struct token *token, struct symbol *attr, struct decl_state *ctx)
{
if (!ctx->ctype.alignment) {
ctx->ctype.alignment = 1;
ctx->packed = 1;
}
return token;
}
static struct token *attribute_aligned(struct token *token, struct symbol *attr, struct decl_state *ctx)
{
int alignment = max_alignment;
struct expression *expr = NULL;
if (match_op(token, '(')) {
token = parens_expression(token, &expr, "in attribute");
if (expr)
alignment = const_expression_value(expr);
}
if (alignment & (alignment-1)) {
warning(token->pos, "I don't like non-power-of-2 alignments");
return token;
} else if (alignment > ctx->ctype.alignment)
ctx->ctype.alignment = alignment;
return token;
}
static void apply_mod(struct position *pos, unsigned long *mods, unsigned long mod)
{
if (*mods & mod & ~MOD_DUP_OK)
warning(*pos, "duplicate %s", modifier_name(mod));
*mods |= mod;
}
static void apply_qualifier(struct position *pos, struct ctype *ctx, unsigned long qual)
{
apply_mod(pos, &ctx->modifiers, qual);
}
static struct token *attribute_modifier(struct token *token, struct symbol *attr, struct decl_state *ctx)
{
apply_mod(&token->pos, &ctx->ctype.modifiers, attr->ctype.modifiers);
return token;
}
static struct token *attribute_function(struct token *token, struct symbol *attr, struct decl_state *ctx)
{
apply_mod(&token->pos, &ctx->f_modifiers, attr->ctype.modifiers);
return token;
}
static struct token *attribute_bitwise(struct token *token, struct symbol *attr, struct decl_state *ctx)
{
if (Wbitwise)
attribute_modifier(token, attr, ctx);
return token;
}
static struct ident *numerical_address_space(int asn)
{
char buff[32];
if (!asn)
return NULL;
sprintf(buff, "", asn);
return built_in_ident(buff);
}
static struct token *attribute_address_space(struct token *token, struct symbol *attr, struct decl_state *ctx)
{
struct expression *expr = NULL;
struct ident *as = NULL;
struct token *next;
token = expect(token, '(', "after address_space attribute");
switch (token_type(token)) {
case TOKEN_NUMBER:
next = primary_expression(token, &expr);
if (expr->type != EXPR_VALUE)
goto invalid;
as = numerical_address_space(expr->value);
break;
case TOKEN_IDENT:
next = token->next;
as = token->ident;
break;
default:
next = token->next;
invalid:
as = NULL;
warning(token->pos, "invalid address space name");
}
if (Waddress_space && as) {
if (ctx->ctype.as)
sparse_error(token->pos,
"multiple address spaces given: %s & %s",
show_as(ctx->ctype.as), show_as(as));
ctx->ctype.as = as;
}
token = expect(next, ')', "after address_space attribute");
return token;
}
static struct symbol *to_QI_mode(struct symbol *ctype)
{
if (ctype->ctype.base_type != &int_type)
return NULL;
if (ctype == &char_ctype)
return ctype;
return ctype->ctype.modifiers & MOD_UNSIGNED ? &uchar_ctype
: &schar_ctype;
}
static struct symbol *to_HI_mode(struct symbol *ctype)
{
if (ctype->ctype.base_type != &int_type)
return NULL;
return ctype->ctype.modifiers & MOD_UNSIGNED ? &ushort_ctype
: &sshort_ctype;
}
static struct symbol *to_SI_mode(struct symbol *ctype)
{
if (ctype->ctype.base_type != &int_type)
return NULL;
return ctype->ctype.modifiers & MOD_UNSIGNED ? &uint_ctype
: &sint_ctype;
}
static struct symbol *to_DI_mode(struct symbol *ctype)
{
if (ctype->ctype.base_type != &int_type)
return NULL;
return ctype->ctype.modifiers & MOD_UNSIGNED ? &ullong_ctype
: &sllong_ctype;
}
static struct symbol *to_TI_mode(struct symbol *ctype)
{
if (ctype->ctype.base_type != &int_type)
return NULL;
return ctype->ctype.modifiers & MOD_UNSIGNED ? &uint128_ctype
: &sint128_ctype;
}
static struct symbol *to_pointer_mode(struct symbol *ctype)
{
if (ctype->ctype.base_type != &int_type)
return NULL;
return ctype->ctype.modifiers & MOD_UNSIGNED ? uintptr_ctype
: intptr_ctype;
}
static struct symbol *to_word_mode(struct symbol *ctype)
{
if (ctype->ctype.base_type != &int_type)
return NULL;
return ctype->ctype.modifiers & MOD_UNSIGNED ? &ulong_ctype
: &slong_ctype;
}
static struct token *attribute_mode(struct token *token, struct symbol *attr, struct decl_state *ctx)
{
token = expect(token, '(', "after mode attribute");
if (token_type(token) == TOKEN_IDENT) {
struct symbol *mode = lookup_keyword(token->ident, NS_KEYWORD);
if (mode && mode->op->type & KW_MODE)
ctx->mode = mode->op;
else
sparse_error(token->pos, "unknown mode attribute %s", show_ident(token->ident));
token = token->next;
} else
sparse_error(token->pos, "expect attribute mode symbol\n");
token = expect(token, ')', "after mode attribute");
return token;
}
static struct token *attribute_context(struct token *token, struct symbol *attr, struct decl_state *ctx)
{
struct context *context = alloc_context();
struct expression *args[3];
int idx = 0;
token = expect(token, '(', "after context attribute");
token = conditional_expression(token, &args[0]);
token = expect(token, ',', "after context 1st argument");
token = conditional_expression(token, &args[1]);
if (match_op(token, ',')) {
token = token->next;
token = conditional_expression(token, &args[2]);
token = expect(token, ')', "after context 3rd argument");
context->context = args[0];
idx++;
} else {
token = expect(token, ')', "after context 2nd argument");
}
context->in = get_expression_value(args[idx++]);
context->out = get_expression_value(args[idx++]);
add_ptr_list(&ctx->ctype.contexts, context);
return token;
}
static struct token *attribute_designated_init(struct token *token, struct symbol *attr, struct decl_state *ctx)
{
if (ctx->ctype.base_type && ctx->ctype.base_type->type == SYM_STRUCT)
ctx->ctype.base_type->designated_init = 1;
else
warning(token->pos, "attribute designated_init applied to non-structure type");
return token;
}
static struct token *attribute_transparent_union(struct token *token, struct symbol *attr, struct decl_state *ctx)
{
if (Wtransparent_union)
warning(token->pos, "attribute __transparent_union__");
if (ctx->ctype.base_type && ctx->ctype.base_type->type == SYM_UNION)
ctx->ctype.base_type->transparent_union = 1;
else
warning(token->pos, "attribute __transparent_union__ applied to non-union type");
return token;
}
static struct token *recover_unknown_attribute(struct token *token)
{
struct expression *expr = NULL;
if (Wunknown_attribute)
warning(token->pos, "unknown attribute '%s'", show_ident(token->ident));
token = token->next;
if (match_op(token, '('))
token = parens_expression(token, &expr, "in attribute");
return token;
}
static struct token *attribute_specifier(struct token *token, struct symbol *sym, struct decl_state *ctx)
{
token = expect(token, '(', "after attribute");
token = expect(token, '(', "after attribute");
while (token_type(token) == TOKEN_IDENT) {
struct symbol *attr = lookup_keyword(token->ident, NS_KEYWORD);
if (attr && attr->op->attribute)
token = attr->op->attribute(token->next, attr, ctx);
else
token = recover_unknown_attribute(token);
if (!match_op(token, ','))
break;
token = token->next;
}
token = expect(token, ')', "after attribute");
token = expect(token, ')', "after attribute");
return token;
}
static unsigned long decl_modifiers(struct decl_state *ctx)
{
unsigned long mods = ctx->ctype.modifiers & MOD_DECLARE;
ctx->ctype.modifiers &= ~MOD_DECLARE;
return ctx->storage_class | mods;
}
static struct token *storage_specifier(struct token *next, struct symbol *sym, struct decl_state *ctx)
{
int is_tls = ctx->ctype.modifiers & MOD_TLS;
unsigned long class = sym->ctype.modifiers;
const char *storage = modifier_name(class);
/* __thread can be used alone, or with extern or static */
if (is_tls && (class & ~(MOD_STATIC|MOD_EXTERN)))
sparse_error(next->pos, "__thread cannot be used with '%s'", storage);
else if (!ctx->storage_class)
ctx->storage_class = class;
else if (ctx->storage_class == class)
sparse_error(next->pos, "duplicate %s", storage);
else
sparse_error(next->pos, "multiple storage classes");
return next;
}
static struct token *thread_specifier(struct token *next, struct symbol *sym, struct decl_state *ctx)
{
/* This GCC extension can be used alone, or with extern or static */
if (!(ctx->storage_class & ~(MOD_STATIC|MOD_EXTERN))) {
apply_qualifier(&next->pos, &ctx->ctype, MOD_TLS);
} else {
sparse_error(next->pos, "__thread cannot be used with '%s'",
modifier_name(ctx->storage_class));
}
return next;
}
static struct token *attribute_force(struct token *token, struct symbol *attr, struct decl_state *ctx)
{
ctx->forced = 1;
return token;
}
static struct token *alignas_specifier(struct token *token, struct symbol *sym, struct decl_state *ctx)
{
int alignment = 0;
if (!match_op(token, '(')) {
sparse_error(token->pos, "expected '(' after _Alignas");
return token;
}
if (lookup_type(token->next)) {
struct symbol *sym = NULL;
token = typename(token->next, &sym, NULL);
sym = examine_symbol_type(sym);
alignment = sym->ctype.alignment;
token = expect(token, ')', "after _Alignas(...");
} else {
struct expression *expr = NULL;
token = parens_expression(token, &expr, "after _Alignas");
if (!expr)
return token;
alignment = const_expression_value(expr);
}
if (alignment < 0) {
warning(token->pos, "non-positive alignment");
return token;
}
if (alignment & (alignment-1)) {
warning(token->pos, "non-power-of-2 alignment");
return token;
}
if (alignment > ctx->ctype.alignment)
ctx->ctype.alignment = alignment;
return token;
}
static struct token *generic_qualifier(struct token *next, struct symbol *sym, struct decl_state *ctx)
{
apply_qualifier(&next->pos, &ctx->ctype, sym->ctype.modifiers);
return next;
}
static void apply_ctype(struct position pos, struct ctype *dst, struct ctype *src)
{
unsigned long mod = src->modifiers;
if (mod)
apply_qualifier(&pos, dst, mod);
/* Context */
concat_ptr_list((struct ptr_list *)src->contexts,
(struct ptr_list **)&dst->contexts);
/* Alignment */
if (src->alignment > dst->alignment)
dst->alignment = src->alignment;
/* Address space */
if (src->as)
dst->as = src->as;
}
static void specifier_conflict(struct position pos, int what, struct ident *new)
{
const char *old;
if (what & (Set_S | Set_T))
goto Catch_all;
if (what & Set_Char)
old = "char";
else if (what & Set_Double)
old = "double";
else if (what & Set_Float)
old = "float";
else if (what & Set_Signed)
old = "signed";
else if (what & Set_Unsigned)
old = "unsigned";
else if (what & Set_Short)
old = "short";
else if (what & Set_Long)
old = "long";
else
old = "long long";
sparse_error(pos, "impossible combination of type specifiers: %s %s",
old, show_ident(new));
return;
Catch_all:
sparse_error(pos, "two or more data types in declaration specifiers");
}
static struct symbol * const int_types[] =
{&char_ctype, &short_ctype, &int_ctype, &long_ctype, &llong_ctype, &int128_ctype};
static struct symbol * const signed_types[] =
{&schar_ctype, &sshort_ctype, &sint_ctype, &slong_ctype, &sllong_ctype,
&sint128_ctype};
static struct symbol * const unsigned_types[] =
{&uchar_ctype, &ushort_ctype, &uint_ctype, &ulong_ctype, &ullong_ctype,
&uint128_ctype};
static struct symbol * const real_types[] =
{&float_ctype, &double_ctype, &ldouble_ctype};
static struct symbol * const * const types[] = {
[CInt] = int_types + 2,
[CSInt] = signed_types + 2,
[CUInt] = unsigned_types + 2,
[CReal] = real_types + 1,
};
struct symbol *ctype_integer(int size, int want_unsigned)
{
return types[want_unsigned ? CUInt : CInt][size];
}
static struct token *handle_qualifiers(struct token *t, struct decl_state *ctx)
{
while (token_type(t) == TOKEN_IDENT) {
struct symbol *s = lookup_keyword(t->ident, NS_TYPEDEF);
if (!s)
break;
if (!(s->op->type & (KW_ATTRIBUTE | KW_QUALIFIER)))
break;
t = t->next;
if (s->op->declarator)
t = s->op->declarator(t, s, ctx);
}
return t;
}
static struct token *declaration_specifiers(struct token *token, struct decl_state *ctx)
{
int seen = 0;
int class = CInt;
int rank = 0;
while (token_type(token) == TOKEN_IDENT) {
struct symbol *s = lookup_symbol(token->ident,
NS_TYPEDEF | NS_SYMBOL);
if (!s || !(s->namespace & NS_TYPEDEF))
break;
if (s->type != SYM_KEYWORD) {
if (seen & Set_Any)
break;
seen |= Set_S | Set_T;
ctx->ctype.base_type = s->ctype.base_type;
apply_ctype(token->pos, &ctx->ctype, &s->ctype);
token = token->next;
continue;
}
if (s->op->type & KW_SPECIFIER) {
if (seen & s->op->test) {
specifier_conflict(token->pos,
seen & s->op->test,
token->ident);
break;
}
seen |= s->op->set;
class += s->op->class;
if (s->op->set & Set_Int128)
rank = 3;
else if (s->op->set & Set_Char)
rank = -2;
if (s->op->set & (Set_Short|Set_Float)) {
rank = -1;
} else if (s->op->set & Set_Long && rank++) {
if (class == CReal) {
specifier_conflict(token->pos,
Set_Vlong,
&double_ident);
break;
}
seen |= Set_Vlong;
}
}
token = token->next;
if (s->op->declarator) // Note: this eats attributes
token = s->op->declarator(token, s, ctx);
if (s->op->type & KW_EXACT) {
ctx->ctype.base_type = s->ctype.base_type;
ctx->ctype.modifiers |= s->ctype.modifiers;
}
}
if (!(seen & Set_S)) { /* not set explicitly? */
struct symbol *base = &incomplete_ctype;
if (seen & Set_Any)
base = types[class][rank];
ctx->ctype.base_type = base;
}
if (ctx->ctype.modifiers & MOD_BITWISE) {
struct symbol *type;
ctx->ctype.modifiers &= ~MOD_BITWISE;
if (!is_int_type(ctx->ctype.base_type)) {
sparse_error(token->pos, "invalid modifier");
return token;
}
type = alloc_symbol(token->pos, SYM_BASETYPE);
*type = *ctx->ctype.base_type;
type->ctype.modifiers &= ~MOD_SPECIFIER;
type->ctype.base_type = ctx->ctype.base_type;
type->type = SYM_RESTRICT;
ctx->ctype.base_type = type;
create_fouled(type);
}
return token;
}
static struct token *abstract_array_declarator(struct token *token, struct symbol *sym)
{
struct expression *expr = NULL;
int has_static = 0;
while (token_type(token) == TOKEN_IDENT) {
struct symbol *sym = lookup_keyword(token->ident, NS_TYPEDEF);
if (!sym || !(sym->op->type & (KW_STATIC|KW_QUALIFIER)))
break;
if (has_static && (sym->op->type & KW_STATIC))
sparse_error(token->pos, "duplicate array static declarator");
has_static |= (sym->op->type & KW_STATIC);
token = token->next;
}
if (match_op(token, '*') && match_op(token->next, ']')) {
// FIXME: '[*]' is treated like '[]'
token = token->next;
} else {
token = assignment_expression(token, &expr);
}
sym->array_size = expr;
return token;
}
static struct token *parameter_type_list(struct token *, struct symbol *);
static struct token *identifier_list(struct token *, struct symbol *);
static struct token *declarator(struct token *token, struct decl_state *ctx);
static struct token *handle_asm_name(struct token *token, struct decl_state *ctx)
{
struct expression *expr;
struct symbol *keyword;
if (token_type(token) != TOKEN_IDENT)
return token;
keyword = lookup_keyword(token->ident, NS_KEYWORD);
if (!keyword)
return token;
if (!(keyword->op->type & KW_ASM))
return token;
token = token->next;
token = expect(token, '(', "after asm");
token = string_expression(token, &expr, "asm name");
token = expect(token, ')', "after asm");
return token;
}
///
// test if @token is '__attribute__' (or one of its variant)
static bool match_attribute(struct token *token)
{
struct symbol *sym;
if (token_type(token) != TOKEN_IDENT)
return false;
sym = lookup_keyword(token->ident, NS_TYPEDEF);
if (!sym || !sym->op)
return false;
return sym->op->type & KW_ATTRIBUTE;
}
static struct token *skip_attribute(struct token *token)
{
token = token->next;
if (match_op(token, '(')) {
int depth = 1;
token = token->next;
while (depth && !eof_token(token)) {
if (token_type(token) == TOKEN_SPECIAL) {
if (token->special == '(')
depth++;
else if (token->special == ')')
depth--;
}
token = token->next;
}
}
return token;
}
static struct token *skip_attributes(struct token *token)
{
while (match_attribute(token)) {
token = expect(token->next, '(', "after attribute");
token = expect(token, '(', "after attribute");
while (token_type(token) == TOKEN_IDENT) {
token = skip_attribute(token);
if (!match_op(token, ','))
break;
token = token->next;
}
token = expect(token, ')', "after attribute");
token = expect(token, ')', "after attribute");
}
return token;
}
static struct token *handle_attributes(struct token *token, struct decl_state *ctx)
{
while (match_attribute(token))
token = attribute_specifier(token->next, NULL, ctx);
return token;
}
static int is_nested(struct token *token, struct token **p,
int prefer_abstract)
{
/*
* This can be either a parameter list or a grouping.
* For the direct (non-abstract) case, we know if must be
* a parameter list if we already saw the identifier.
* For the abstract case, we know if must be a parameter
* list if it is empty or starts with a type.
*/
struct token *next = token->next;
*p = next = skip_attributes(next);
if (token_type(next) == TOKEN_IDENT) {
if (lookup_type(next))
return !prefer_abstract;
return 1;
}
if (match_op(next, ')') || match_op(next, SPECIAL_ELLIPSIS))
return 0;
return 1;
}
enum kind {
Empty, K_R, Proto, Bad_Func,
};
static enum kind which_func(struct token *token,
struct ident **n,
int prefer_abstract)
{
struct token *next = token->next;
if (token_type(next) == TOKEN_IDENT) {
if (lookup_type(next))
return Proto;
/* identifier list not in definition; complain */
if (prefer_abstract)
warning(token->pos,
"identifier list not in definition");
return K_R;
}
if (token_type(next) != TOKEN_SPECIAL)
return Bad_Func;
if (next->special == ')') {
/* don't complain about those */
if (!n || match_op(next->next, ';') || match_op(next->next, ','))
return Empty;
if (Wstrict_prototypes)
warning(next->pos,
"non-ANSI function declaration of function '%s'",
show_ident(*n));
return Empty;
}
if (next->special == SPECIAL_ELLIPSIS) {
warning(next->pos,
"variadic functions must have one named argument");
return Proto;
}
return Bad_Func;
}
static struct token *direct_declarator(struct token *token, struct decl_state *ctx)
{
struct ctype *ctype = &ctx->ctype;
struct token *next;
struct ident **p = ctx->ident;
if (ctx->ident && token_type(token) == TOKEN_IDENT) {
*ctx->ident = token->ident;
token = token->next;
} else if (match_op(token, '(') &&
is_nested(token, &next, ctx->prefer_abstract)) {
struct symbol *base_type = ctype->base_type;
if (token->next != next)
next = handle_attributes(token->next, ctx);
token = declarator(next, ctx);
token = expect(token, ')', "in nested declarator");
while (ctype->base_type != base_type)
ctype = &ctype->base_type->ctype;
p = NULL;
}
if (match_op(token, '(')) {
enum kind kind = which_func(token, p, ctx->prefer_abstract);
struct symbol *fn;
fn = alloc_indirect_symbol(token->pos, ctype, SYM_FN);
ctype->modifiers |= ctx->f_modifiers;
token = token->next;
if (kind == K_R)
token = identifier_list(token, fn);
else if (kind == Proto)
token = parameter_type_list(token, fn);
token = expect(token, ')', "in function declarator");
fn->endpos = token->pos;
return token;
}
while (match_op(token, '[')) {
struct symbol *array;
array = alloc_indirect_symbol(token->pos, ctype, SYM_ARRAY);
token = abstract_array_declarator(token->next, array);
token = expect(token, ']', "in abstract_array_declarator");
array->endpos = token->pos;
ctype = &array->ctype;
}
return token;
}
static struct token *pointer(struct token *token, struct decl_state *ctx)
{
while (match_op(token,'*')) {
struct symbol *ptr = alloc_symbol(token->pos, SYM_PTR);
ptr->ctype.modifiers = ctx->ctype.modifiers;
ptr->ctype.base_type = ctx->ctype.base_type;
ptr->ctype.as = ctx->ctype.as;
ptr->ctype.contexts = ctx->ctype.contexts;
ctx->ctype.modifiers = 0;
ctx->ctype.base_type = ptr;
ctx->ctype.as = NULL;
ctx->ctype.contexts = NULL;
ctx->ctype.alignment = 0;
token = handle_qualifiers(token->next, ctx);
ctx->ctype.base_type->endpos = token->pos;
}
return token;
}
static struct token *declarator(struct token *token, struct decl_state *ctx)
{
token = pointer(token, ctx);
return direct_declarator(token, ctx);
}
static struct token *handle_bitfield(struct token *token, struct decl_state *ctx)
{
struct ctype *ctype = &ctx->ctype;
struct expression *expr;
struct symbol *bitfield;
long long width;
if (ctype->base_type != &int_type && !is_int_type(ctype->base_type)) {
sparse_error(token->pos, "invalid bitfield specifier for type %s.",
show_typename(ctype->base_type));
// Parse this to recover gracefully.
return conditional_expression(token->next, &expr);
}
bitfield = alloc_indirect_symbol(token->pos, ctype, SYM_BITFIELD);
token = conditional_expression(token->next, &expr);
width = const_expression_value(expr);
bitfield->bit_size = width;
if (width < 0 || width > INT_MAX || (*ctx->ident && width == 0)) {
sparse_error(token->pos, "bitfield '%s' has invalid width (%lld)",
show_ident(*ctx->ident), width);
width = -1;
} else if (*ctx->ident) {
struct symbol *base_type = bitfield->ctype.base_type;
struct symbol *bitfield_type = base_type == &int_type ? bitfield : base_type;
int is_signed = !(bitfield_type->ctype.modifiers & MOD_UNSIGNED);
if (Wone_bit_signed_bitfield && width == 1 && is_signed) {
// Valid values are either {-1;0} or {0}, depending on integer
// representation. The latter makes for very efficient code...
sparse_error(token->pos, "dubious one-bit signed bitfield");
}
if (Wdefault_bitfield_sign &&
bitfield_type->type != SYM_ENUM &&
!(bitfield_type->ctype.modifiers & MOD_EXPLICITLY_SIGNED) &&
is_signed) {
// The sign of bitfields is unspecified by default.
warning(token->pos, "dubious bitfield without explicit `signed' or `unsigned'");
}
}
bitfield->bit_size = width;
bitfield->endpos = token->pos;
bitfield->ident = *ctx->ident;
return token;
}
static struct token *declaration_list(struct token *token, struct symbol_list **list)
{
struct decl_state ctx = {.prefer_abstract = 0};
struct ctype saved;
unsigned long mod;
token = declaration_specifiers(token, &ctx);
mod = decl_modifiers(&ctx);
saved = ctx.ctype;
for (;;) {
struct symbol *decl = alloc_symbol(token->pos, SYM_NODE);
ctx.ident = &decl->ident;
token = declarator(token, &ctx);
if (match_op(token, ':'))
token = handle_bitfield(token, &ctx);
token = handle_attributes(token, &ctx);
apply_modifiers(token->pos, &ctx);
decl->ctype = ctx.ctype;
decl->ctype.modifiers |= mod;
decl->endpos = token->pos;
add_symbol(list, decl);
if (!match_op(token, ','))
break;
token = token->next;
ctx.ctype = saved;
}
return token;
}
static struct token *struct_declaration_list(struct token *token, struct symbol_list **list)
{
while (!match_op(token, '}')) {
if (match_ident(token, &_Static_assert_ident)) {
token = parse_static_assert(token, NULL);
continue;
}
if (!match_op(token, ';'))
token = declaration_list(token, list);
if (!match_op(token, ';')) {
sparse_error(token->pos, "expected ; at end of declaration");
break;
}
token = token->next;
}
return token;
}
static struct token *parameter_declaration(struct token *token, struct symbol *sym)
{
struct decl_state ctx = {.prefer_abstract = 1};
token = declaration_specifiers(token, &ctx);
ctx.ident = &sym->ident;
token = declarator(token, &ctx);
token = handle_attributes(token, &ctx);
apply_modifiers(token->pos, &ctx);
sym->ctype = ctx.ctype;
sym->ctype.modifiers |= decl_modifiers(&ctx);
sym->endpos = token->pos;
sym->forced_arg = ctx.forced;
return token;
}
struct token *typename(struct token *token, struct symbol **p, int *forced)
{
struct decl_state ctx = {.prefer_abstract = 1};
unsigned long class;
struct symbol *sym = alloc_symbol(token->pos, SYM_NODE);
*p = sym;
token = declaration_specifiers(token, &ctx);
token = declarator(token, &ctx);
apply_modifiers(token->pos, &ctx);
sym->ctype = ctx.ctype;
sym->endpos = token->pos;
class = ctx.storage_class;
if (forced)
*forced = ctx.forced;
if (class)
warning(sym->pos, "storage class in typename (%s%s)",
modifier_string(class), show_typename(sym));
return token;
}
static struct token *expression_statement(struct token *token, struct expression **tree)
{
token = parse_expression(token, tree);
return expect(token, ';', "at end of statement");
}
static struct token *parse_asm_operands(struct token *token, struct statement *stmt,
struct asm_operand_list **inout)
{
/* Allow empty operands */
if (match_op(token->next, ':') || match_op(token->next, ')'))
return token->next;
do {
struct asm_operand *op = __alloc_asm_operand(0);
if (match_op(token->next, '[') &&
token_type(token->next->next) == TOKEN_IDENT &&
match_op(token->next->next->next, ']')) {
op->name = token->next->next->ident;
token = token->next->next->next;
}
token = token->next;
token = string_expression(token, &op->constraint, "asm constraint");
token = parens_expression(token, &op->expr, "in asm parameter");
add_ptr_list(inout, op);
} while (match_op(token, ','));
return token;
}
static struct token *parse_asm_clobbers(struct token *token, struct statement *stmt,
struct expression_list **clobbers)
{
struct expression *expr;
do {
token = primary_expression(token->next, &expr);
if (expr)
add_expression(clobbers, expr);
} while (match_op(token, ','));
return token;
}
static struct token *parse_asm_labels(struct token *token, struct statement *stmt,
struct symbol_list **labels)
{
struct symbol *label;
do {
token = token->next; /* skip ':' and ',' */
if (token_type(token) != TOKEN_IDENT)
return token;
label = label_symbol(token, 1);
add_symbol(labels, label);
token = token->next;
} while (match_op(token, ','));
return token;
}
static struct token *parse_asm_statement(struct token *token, struct statement *stmt)
{
unsigned long mods = 0;
token = token->next;
stmt->type = STMT_ASM;
while (token_type(token) == TOKEN_IDENT) {
struct symbol *s = lookup_keyword(token->ident, NS_TYPEDEF);
if (s && s->op->asm_modifier)
s->op->asm_modifier(token, &mods, s->ctype.modifiers);
else if (token->ident == &goto_ident)
asm_modifier(token, &mods, MOD_ASM_GOTO);
token = token->next;
}
token = expect(token, '(', "after asm");
token = string_expression(token, &stmt->asm_string, "inline asm");
if (match_op(token, ':'))
token = parse_asm_operands(token, stmt, &stmt->asm_outputs);
if (match_op(token, ':'))
token = parse_asm_operands(token, stmt, &stmt->asm_inputs);
if (match_op(token, ':'))
token = parse_asm_clobbers(token, stmt, &stmt->asm_clobbers);
if (match_op(token, ':') && (mods & MOD_ASM_GOTO))
token = parse_asm_labels(token, stmt, &stmt->asm_labels);
token = expect(token, ')', "after asm");
return expect(token, ';', "at end of asm-statement");
}
static struct token *parse_static_assert(struct token *token, struct symbol_list **unused)
{
struct expression *cond = NULL, *message = NULL;
token = expect(token->next, '(', "after _Static_assert");
token = constant_expression(token, &cond);
if (!cond)
sparse_error(token->pos, "Expected constant expression");
if (match_op(token, ',')) {
token = token->next;
token = string_expression(token, &message, "_Static_assert()");
if (!message)
cond = NULL;
}
token = expect(token, ')', "after diagnostic message in _Static_assert");
token = expect(token, ';', "after _Static_assert()");
if (cond && !const_expression_value(cond) && cond->type == EXPR_VALUE) {
const char *sep = "", *msg = "";
if (message) {
sep = ": ";
msg = show_string(message->string);
}
sparse_error(cond->pos, "static assertion failed%s%s", sep, msg);
}
return token;
}
/* Make a statement out of an expression */
static struct statement *make_statement(struct expression *expr)
{
struct statement *stmt;
if (!expr)
return NULL;
stmt = alloc_statement(expr->pos, STMT_EXPRESSION);
stmt->expression = expr;
return stmt;
}
/*
* All iterators have two symbols associated with them:
* the "continue" and "break" symbols, which are targets
* for continue and break statements respectively.
*
* They are in a special name-space, but they follow
* all the normal visibility rules, so nested iterators
* automatically work right.
*/
static void start_iterator(struct statement *stmt)
{
struct symbol *cont, *brk;
start_block_scope();
cont = alloc_symbol(stmt->pos, SYM_NODE);
bind_symbol(cont, &continue_ident, NS_ITERATOR);
brk = alloc_symbol(stmt->pos, SYM_NODE);
bind_symbol(brk, &break_ident, NS_ITERATOR);
stmt->type = STMT_ITERATOR;
stmt->iterator_break = brk;
stmt->iterator_continue = cont;
fn_local_symbol(brk);
fn_local_symbol(cont);
}
static void end_iterator(struct statement *stmt)
{
end_block_scope();
}
static struct statement *start_function(struct symbol *sym)
{
struct symbol *ret;
struct statement *stmt = alloc_statement(sym->pos, STMT_COMPOUND);
start_function_scope();
ret = alloc_symbol(sym->pos, SYM_NODE);
ret->ctype = sym->ctype.base_type->ctype;
ret->ctype.modifiers &= ~(MOD_STORAGE | MOD_QUALIFIER | MOD_TLS | MOD_ACCESS | MOD_NOCAST | MOD_NODEREF);
ret->ctype.modifiers |= (MOD_AUTO | MOD_REGISTER);
bind_symbol(ret, &return_ident, NS_ITERATOR);
stmt->ret = ret;
fn_local_symbol(ret);
// Currently parsed symbol for __func__/__FUNCTION__/__PRETTY_FUNCTION__
current_fn = sym;
return stmt;
}
static void end_function(struct symbol *sym)
{
current_fn = NULL;
end_function_scope();
}
/*
* A "switch()" statement, like an iterator, has a
* the "break" symbol associated with it. It works
* exactly like the iterator break - it's the target
* for any break-statements in scope, and means that
* "break" handling doesn't even need to know whether
* it's breaking out of an iterator or a switch.
*
* In addition, the "case" symbol is a marker for the
* case/default statements to find the switch statement
* that they are associated with.
*/
static void start_switch(struct statement *stmt)
{
struct symbol *brk, *switch_case;
start_block_scope();
brk = alloc_symbol(stmt->pos, SYM_NODE);
bind_symbol(brk, &break_ident, NS_ITERATOR);
switch_case = alloc_symbol(stmt->pos, SYM_NODE);
bind_symbol(switch_case, &case_ident, NS_ITERATOR);
switch_case->stmt = stmt;
stmt->type = STMT_SWITCH;
stmt->switch_break = brk;
stmt->switch_case = switch_case;
fn_local_symbol(brk);
fn_local_symbol(switch_case);
}
static void end_switch(struct statement *stmt)
{
if (!stmt->switch_case->symbol_list)
warning(stmt->pos, "switch with no cases");
end_block_scope();
}
static void add_case_statement(struct statement *stmt)
{
struct symbol *target = lookup_symbol(&case_ident, NS_ITERATOR);
struct symbol *sym;
if (!target) {
sparse_error(stmt->pos, "not in switch scope");
stmt->type = STMT_NONE;
return;
}
sym = alloc_symbol(stmt->pos, SYM_NODE);
add_symbol(&target->symbol_list, sym);
sym->stmt = stmt;
stmt->case_label = sym;
fn_local_symbol(sym);
}
static struct token *parse_return_statement(struct token *token, struct statement *stmt)
{
struct symbol *target = lookup_symbol(&return_ident, NS_ITERATOR);
if (!target)
error_die(token->pos, "internal error: return without a function target");
stmt->type = STMT_RETURN;
stmt->ret_target = target;
return expression_statement(token->next, &stmt->ret_value);
}
static void validate_for_loop_decl(struct symbol *sym)
{
unsigned long storage = sym->ctype.modifiers & MOD_STORAGE;
if (storage & ~(MOD_AUTO | MOD_REGISTER)) {
const char *name = show_ident(sym->ident);
sparse_error(sym->pos, "non-local var '%s' in for-loop initializer", name);
sym->ctype.modifiers &= ~MOD_STORAGE;
}
}
static struct token *parse_for_statement(struct token *token, struct statement *stmt)
{
struct symbol_list *syms;
struct expression *e1, *e2, *e3;
struct statement *iterator;
start_iterator(stmt);
token = expect(token->next, '(', "after 'for'");
syms = NULL;
e1 = NULL;
/* C99 variable declaration? */
if (lookup_type(token)) {
token = external_declaration(token, &syms, validate_for_loop_decl);
} else {
token = parse_expression(token, &e1);
token = expect(token, ';', "in 'for'");
}
token = parse_expression(token, &e2);
token = expect(token, ';', "in 'for'");
token = parse_expression(token, &e3);
token = expect(token, ')', "in 'for'");
token = statement(token, &iterator);
stmt->iterator_syms = syms;
stmt->iterator_pre_statement = make_statement(e1);
stmt->iterator_pre_condition = e2;
stmt->iterator_post_statement = make_statement(e3);
stmt->iterator_post_condition = NULL;
stmt->iterator_statement = iterator;
end_iterator(stmt);
return token;
}
static struct token *parse_while_statement(struct token *token, struct statement *stmt)
{
struct expression *expr;
struct statement *iterator;
start_iterator(stmt);
token = parens_expression(token->next, &expr, "after 'while'");
token = statement(token, &iterator);
stmt->iterator_pre_condition = expr;
stmt->iterator_post_condition = NULL;
stmt->iterator_statement = iterator;
end_iterator(stmt);
return token;
}
static struct token *parse_do_statement(struct token *token, struct statement *stmt)
{
struct expression *expr;
struct statement *iterator;
start_iterator(stmt);
token = statement(token->next, &iterator);
if (token_type(token) == TOKEN_IDENT && token->ident == &while_ident)
token = token->next;
else
sparse_error(token->pos, "expected 'while' after 'do'");
token = parens_expression(token, &expr, "after 'do-while'");
stmt->iterator_post_condition = expr;
stmt->iterator_statement = iterator;
end_iterator(stmt);
if (iterator && iterator->type != STMT_COMPOUND && Wdo_while)
warning(iterator->pos, "do-while statement is not a compound statement");
return expect(token, ';', "after statement");
}
static struct token *parse_if_statement(struct token *token, struct statement *stmt)
{
stmt->type = STMT_IF;
token = parens_expression(token->next, &stmt->if_conditional, "after if");
token = statement(token, &stmt->if_true);
if (token_type(token) != TOKEN_IDENT)
return token;
if (token->ident != &else_ident)
return token;
return statement(token->next, &stmt->if_false);
}
static inline struct token *case_statement(struct token *token, struct statement *stmt)
{
stmt->type = STMT_CASE;
token = expect(token, ':', "after default/case");
add_case_statement(stmt);
return statement(token, &stmt->case_statement);
}
static struct token *parse_case_statement(struct token *token, struct statement *stmt)
{
token = parse_expression(token->next, &stmt->case_expression);
if (match_op(token, SPECIAL_ELLIPSIS))
token = parse_expression(token->next, &stmt->case_to);
return case_statement(token, stmt);
}
static struct token *parse_default_statement(struct token *token, struct statement *stmt)
{
return case_statement(token->next, stmt);
}
static struct token *parse_loop_iterator(struct token *token, struct statement *stmt)
{
struct symbol *target = lookup_symbol(token->ident, NS_ITERATOR);
stmt->type = STMT_GOTO;
stmt->goto_label = target;
if (!target)
sparse_error(stmt->pos, "break/continue not in iterator scope");
return expect(token->next, ';', "at end of statement");
}
static struct token *parse_switch_statement(struct token *token, struct statement *stmt)
{
stmt->type = STMT_SWITCH;
start_switch(stmt);
token = parens_expression(token->next, &stmt->switch_expression, "after 'switch'");
token = statement(token, &stmt->switch_statement);
end_switch(stmt);
return token;
}
static void warn_label_usage(struct position def, struct position use, struct ident *ident)
{
const char *id = show_ident(ident);
sparse_error(use, "label '%s' used outside statement expression", id);
info(def, " label '%s' defined here", id);
current_fn->bogus_linear = 1;
}
void check_label_usage(struct symbol *label, struct position use_pos)
{
struct statement *def = label->stmt;
if (def) {
if (!is_in_scope(def->label_scope, label_scope))
warn_label_usage(def->pos, use_pos, label->ident);
} else if (!label->label_scope) {
label->label_scope = label_scope;
label->label_pos = use_pos;
}
}
static struct token *parse_goto_statement(struct token *token, struct statement *stmt)
{
stmt->type = STMT_GOTO;
token = token->next;
if (match_op(token, '*')) {
token = parse_expression(token->next, &stmt->goto_expression);
add_statement(&function_computed_goto_list, stmt);
} else if (token_type(token) == TOKEN_IDENT) {
struct symbol *label = label_symbol(token, 1);
stmt->goto_label = label;
check_label_usage(label, stmt->pos);
token = token->next;
} else {
sparse_error(token->pos, "Expected identifier or goto expression");
}
return expect(token, ';', "at end of statement");
}
static struct token *parse_context_statement(struct token *token, struct statement *stmt)
{
stmt->type = STMT_CONTEXT;
token = token->next;
token = expect(token, '(', "after __context__ statement");
token = assignment_expression(token, &stmt->expression);
if (!stmt->expression)
unexpected(token, "expression expected after '('");
if (match_op(token, ',')) {
token = token->next;
stmt->context = stmt->expression;
token = assignment_expression(token, &stmt->expression);
if (!stmt->expression)
unexpected(token, "expression expected after ','");
}
token = expect(token, ')', "at end of __context__ statement");
return expect(token, ';', "at end of statement");
}
static struct token *parse_range_statement(struct token *token, struct statement *stmt)
{
stmt->type = STMT_RANGE;
token = token->next;
token = expect(token, '(', "after __range__ statement");
token = assignment_expression(token, &stmt->range_expression);
token = expect(token, ',', "after range expression");
token = assignment_expression(token, &stmt->range_low);
token = expect(token, ',', "after low range");
token = assignment_expression(token, &stmt->range_high);
token = expect(token, ')', "after range statement");
return expect(token, ';', "after range statement");
}
static struct token *handle_label_attributes(struct token *token, struct symbol *label)
{
struct decl_state ctx = { };
token = handle_attributes(token, &ctx);
label->label_modifiers = ctx.ctype.modifiers;
return token;
}
static struct token *statement(struct token *token, struct statement **tree)
{
struct statement *stmt = alloc_statement(token->pos, STMT_NONE);
*tree = stmt;
if (token_type(token) == TOKEN_IDENT) {
struct symbol *s = lookup_keyword(token->ident, NS_KEYWORD);
if (s && s->op->statement)
return s->op->statement(token, stmt);
if (match_op(token->next, ':')) {
struct symbol *s = label_symbol(token, 0);
token = handle_label_attributes(token->next->next, s);
if (s->stmt) {
sparse_error(stmt->pos, "label '%s' redefined", show_ident(s->ident));
// skip the label to avoid multiple definitions
return statement(token, tree);
}
stmt->type = STMT_LABEL;
stmt->label_identifier = s;
stmt->label_scope = label_scope;
if (s->label_scope) {
if (!is_in_scope(label_scope, s->label_scope))
warn_label_usage(stmt->pos, s->label_pos, s->ident);
}
s->stmt = stmt;
if (match_op(token, '}')) {
warning(token->pos, "statement expected after label");
stmt->label_statement = alloc_statement(token->pos, STMT_NONE);
return token;
}
return statement(token, &stmt->label_statement);
}
}
if (match_op(token, '{')) {
token = compound_statement(token->next, stmt);
return expect(token, '}', "at end of compound statement");
}
stmt->type = STMT_EXPRESSION;
return expression_statement(token, &stmt->expression);
}
/* gcc extension - __label__ ident-list; in the beginning of compound stmt */
static struct token *label_statement(struct token *token)
{
while (token_type(token) == TOKEN_IDENT) {
struct symbol *sym = alloc_symbol(token->pos, SYM_LABEL);
/* it's block-scope, but we want label namespace */
bind_symbol_with_scope(sym, token->ident, NS_LABEL, block_scope);
fn_local_symbol(sym);
token = token->next;
if (!match_op(token, ','))
break;
token = token->next;
}
return expect(token, ';', "at end of label declaration");
}
static struct token * statement_list(struct token *token, struct statement_list **list)
{
int seen_statement = 0;
while (token_type(token) == TOKEN_IDENT &&
token->ident == &__label___ident)
token = label_statement(token->next);
for (;;) {
struct statement * stmt;
if (eof_token(token))
break;
if (match_op(token, '}'))
break;
if (match_ident(token, &_Static_assert_ident)) {
token = parse_static_assert(token, NULL);
continue;
}
if (lookup_type(token)) {
if (seen_statement) {
warning(token->pos, "mixing declarations and code");
seen_statement = 0;
}
stmt = alloc_statement(token->pos, STMT_DECLARATION);
token = external_declaration(token, &stmt->declaration, NULL);
} else {
seen_statement = Wdeclarationafterstatement;
token = statement(token, &stmt);
}
add_statement(list, stmt);
}
return token;
}
static struct token *identifier_list(struct token *token, struct symbol *fn)
{
struct symbol_list **list = &fn->arguments;
for (;;) {
struct symbol *sym = alloc_symbol(token->pos, SYM_NODE);
sym->ident = token->ident;
token = token->next;
sym->endpos = token->pos;
sym->ctype.base_type = &incomplete_ctype;
add_symbol(list, sym);
if (!match_op(token, ',') ||
token_type(token->next) != TOKEN_IDENT ||
lookup_type(token->next))
break;
token = token->next;
}
return token;
}
static struct token *parameter_type_list(struct token *token, struct symbol *fn)
{
struct symbol_list **list = &fn->arguments;
for (;;) {
struct symbol *sym;
if (match_op(token, SPECIAL_ELLIPSIS)) {
fn->variadic = 1;
token = token->next;
break;
}
sym = alloc_symbol(token->pos, SYM_NODE);
token = parameter_declaration(token, sym);
if (sym->ctype.base_type == &void_ctype) {
/* Special case: (void) */
if (!*list && !sym->ident)
break;
warning(token->pos, "void parameter");
}
add_symbol(list, sym);
if (!match_op(token, ','))
break;
token = token->next;
}
return token;
}
struct token *compound_statement(struct token *token, struct statement *stmt)
{
stmt->type = STMT_COMPOUND;
start_block_scope();
token = statement_list(token, &stmt->stmts);
end_block_scope();
return token;
}
static struct expression *identifier_expression(struct token *token)
{
struct expression *expr = alloc_expression(token->pos, EXPR_IDENTIFIER);
expr->expr_ident = token->ident;
return expr;
}
static struct expression *index_expression(struct expression *from, struct expression *to)
{
int idx_from, idx_to;
struct expression *expr = alloc_expression(from->pos, EXPR_INDEX);
idx_from = const_expression_value(from);
idx_to = idx_from;
if (to) {
idx_to = const_expression_value(to);
if (idx_to < idx_from || idx_from < 0)
warning(from->pos, "nonsense array initializer index range");
}
expr->idx_from = idx_from;
expr->idx_to = idx_to;
return expr;
}
static struct token *single_initializer(struct expression **ep, struct token *token)
{
int expect_equal = 0;
struct token *next = token->next;
struct expression **tail = ep;
int nested;
*ep = NULL;
if ((token_type(token) == TOKEN_IDENT) && match_op(next, ':')) {
struct expression *expr = identifier_expression(token);
if (Wold_initializer)
warning(token->pos, "obsolete struct initializer, use C99 syntax");
token = initializer(&expr->ident_expression, next->next);
if (expr->ident_expression)
*ep = expr;
return token;
}
for (tail = ep, nested = 0; ; nested++, next = token->next) {
if (match_op(token, '.') && (token_type(next) == TOKEN_IDENT)) {
struct expression *expr = identifier_expression(next);
*tail = expr;
tail = &expr->ident_expression;
expect_equal = 1;
token = next->next;
} else if (match_op(token, '[')) {
struct expression *from = NULL, *to = NULL, *expr;
token = constant_expression(token->next, &from);
if (!from) {
sparse_error(token->pos, "Expected constant expression");
break;
}
if (match_op(token, SPECIAL_ELLIPSIS))
token = constant_expression(token->next, &to);
expr = index_expression(from, to);
*tail = expr;
tail = &expr->idx_expression;
token = expect(token, ']', "at end of initializer index");
if (nested)
expect_equal = 1;
} else {
break;
}
}
if (nested && !expect_equal) {
if (!match_op(token, '='))
warning(token->pos, "obsolete array initializer, use C99 syntax");
else
expect_equal = 1;
}
if (expect_equal)
token = expect(token, '=', "at end of initializer index");
token = initializer(tail, token);
if (!*tail)
*ep = NULL;
return token;
}
static struct token *initializer_list(struct expression_list **list, struct token *token)
{
struct expression *expr;
for (;;) {
token = single_initializer(&expr, token);
if (!expr)
break;
add_expression(list, expr);
if (!match_op(token, ','))
break;
token = token->next;
}
return token;
}
struct token *initializer(struct expression **tree, struct token *token)
{
if (match_op(token, '{')) {
struct expression *expr = alloc_expression(token->pos, EXPR_INITIALIZER);
*tree = expr;
if (!Wuniversal_initializer) {
struct token *next = token->next;
// '{ 0 }' is equivalent to '{ }' except for some
// warnings, like using 0 to initialize a null-pointer.
if (match_token_zero(next)) {
if (match_op(next->next, '}'))
expr->zero_init = 1;
}
}
token = initializer_list(&expr->expr_list, token->next);
return expect(token, '}', "at end of initializer");
}
return assignment_expression(token, tree);
}
static void declare_argument(struct symbol *sym, struct symbol *fn)
{
if (!sym->ident) {
sparse_error(sym->pos, "no identifier for function argument");
return;
}
if (sym->ctype.base_type == &incomplete_ctype) {
sym->ctype.base_type = &int_ctype;
if (Wimplicit_int) {
sparse_error(sym->pos, "missing type declaration for parameter '%s'",
show_ident(sym->ident));
}
}
bind_symbol(sym, sym->ident, NS_SYMBOL);
}
static struct token *parse_function_body(struct token *token, struct symbol *decl,
struct symbol_list **list)
{
struct symbol_list **old_symbol_list;
struct symbol *base_type = decl->ctype.base_type;
struct statement *stmt, **p;
struct symbol *prev;
struct symbol *arg;
old_symbol_list = function_symbol_list;
if (decl->ctype.modifiers & MOD_INLINE) {
function_symbol_list = &decl->inline_symbol_list;
p = &base_type->inline_stmt;
} else {
function_symbol_list = &decl->symbol_list;
p = &base_type->stmt;
}
function_computed_target_list = NULL;
function_computed_goto_list = NULL;
if ((decl->ctype.modifiers & (MOD_EXTERN|MOD_INLINE)) == MOD_EXTERN) {
if (Wexternal_function_has_definition)
warning(decl->pos, "function '%s' with external linkage has definition", show_ident(decl->ident));
}
if (!(decl->ctype.modifiers & MOD_STATIC))
decl->ctype.modifiers |= MOD_EXTERN;
stmt = start_function(decl);
*p = stmt;
FOR_EACH_PTR (base_type->arguments, arg) {
declare_argument(arg, base_type);
} END_FOR_EACH_PTR(arg);
token = statement_list(token->next, &stmt->stmts);
end_function(decl);
if (!(decl->ctype.modifiers & MOD_INLINE))
add_symbol(list, decl);
check_declaration(decl);
decl->definition = decl;
prev = decl->same_symbol;
if (prev && prev->definition) {
warning(decl->pos, "multiple definitions for function '%s'",
show_ident(decl->ident));
info(prev->definition->pos, " the previous one is here");
} else {
while (prev) {
rebind_scope(prev, decl->scope);
prev->definition = decl;
prev = prev->same_symbol;
}
}
function_symbol_list = old_symbol_list;
if (function_computed_goto_list) {
if (!function_computed_target_list)
warning(decl->pos, "function '%s' has computed goto but no targets?", show_ident(decl->ident));
else {
FOR_EACH_PTR(function_computed_goto_list, stmt) {
stmt->target_list = function_computed_target_list;
} END_FOR_EACH_PTR(stmt);
}
}
return expect(token, '}', "at end of function");
}
static void promote_k_r_types(struct symbol *arg)
{
struct symbol *base = arg->ctype.base_type;
if (base && base->ctype.base_type == &int_type && base->rank < 0) {
arg->ctype.base_type = &int_ctype;
}
}
static void apply_k_r_types(struct symbol_list *argtypes, struct symbol *fn)
{
struct symbol_list *real_args = fn->ctype.base_type->arguments;
struct symbol *arg;
FOR_EACH_PTR(real_args, arg) {
struct symbol *type;
/* This is quadratic in the number of arguments. We _really_ don't care */
FOR_EACH_PTR(argtypes, type) {
if (type->ident == arg->ident)
goto match;
} END_FOR_EACH_PTR(type);
if (Wimplicit_int) {
warning(arg->pos, "missing type declaration for parameter '%s'",
show_ident(arg->ident));
}
type = alloc_symbol(arg->pos, SYM_NODE);
type->ident = arg->ident;
type->ctype.base_type = &int_ctype;
match:
type->used = 1;
/* "char" and "short" promote to "int" */
promote_k_r_types(type);
arg->ctype = type->ctype;
} END_FOR_EACH_PTR(arg);
FOR_EACH_PTR(argtypes, arg) {
if (!arg->used)
warning(arg->pos, "nonsensical parameter declaration '%s'", show_ident(arg->ident));
} END_FOR_EACH_PTR(arg);
}
static struct token *parse_k_r_arguments(struct token *token, struct symbol *decl,
struct symbol_list **list)
{
struct symbol_list *args = NULL;
if (Wold_style_definition)
warning(token->pos, "non-ANSI definition of function '%s'", show_ident(decl->ident));
do {
token = declaration_list(token, &args);
if (!match_op(token, ';')) {
sparse_error(token->pos, "expected ';' at end of parameter declaration");
break;
}
token = token->next;
} while (lookup_type(token));
apply_k_r_types(args, decl);
if (!match_op(token, '{')) {
sparse_error(token->pos, "expected function body");
return token;
}
return parse_function_body(token, decl, list);
}
static struct token *toplevel_asm_declaration(struct token *token, struct symbol_list **list)
{
struct symbol *anon = alloc_symbol(token->pos, SYM_NODE);
struct symbol *fn = alloc_symbol(token->pos, SYM_FN);
struct statement *stmt;
anon->ctype.base_type = fn;
stmt = alloc_statement(token->pos, STMT_NONE);
fn->stmt = stmt;
token = parse_asm_statement(token, stmt);
// FIXME: add_symbol(list, anon);
return token;
}
struct token *external_declaration(struct token *token, struct symbol_list **list,
validate_decl_t validate_decl)
{
struct ident *ident = NULL;
struct symbol *decl;
struct decl_state ctx = { .ident = &ident };
struct ctype saved;
struct symbol *base_type;
unsigned long mod;
int is_typedef;
/* Top-level inline asm or static assertion? */
if (token_type(token) == TOKEN_IDENT) {
struct symbol *s = lookup_keyword(token->ident, NS_KEYWORD);
if (s && s->op->toplevel)
return s->op->toplevel(token, list);
}
/* Parse declaration-specifiers, if any */
token = declaration_specifiers(token, &ctx);
mod = decl_modifiers(&ctx);
decl = alloc_symbol(token->pos, SYM_NODE);
/* Just a type declaration? */
if (match_op(token, ';')) {
apply_modifiers(token->pos, &ctx);
return token->next;
}
saved = ctx.ctype;
token = declarator(token, &ctx);
token = handle_asm_name(token, &ctx);
token = handle_attributes(token, &ctx);
apply_modifiers(token->pos, &ctx);
decl->ctype = ctx.ctype;
decl->ctype.modifiers |= mod;
decl->endpos = token->pos;
/* Just a type declaration? */
if (!ident) {
warning(token->pos, "missing identifier in declaration");
return expect(token, ';', "at the end of type declaration");
}
/* type define declaration? */
is_typedef = ctx.storage_class == MOD_USERTYPE;
/* Typedefs don't have meaningful storage */
if (is_typedef)
decl->ctype.modifiers |= MOD_USERTYPE;
bind_symbol(decl, ident, is_typedef ? NS_TYPEDEF: NS_SYMBOL);
base_type = decl->ctype.base_type;
if (is_typedef) {
if (base_type && !base_type->ident) {
switch (base_type->type) {
case SYM_STRUCT:
case SYM_UNION:
case SYM_ENUM:
case SYM_RESTRICT:
base_type->ident = ident;
break;
default:
break;
}
}
} else if (base_type && base_type->type == SYM_FN) {
if (base_type->ctype.base_type == &autotype_ctype) {
sparse_error(decl->pos, "'%s()' has __auto_type return type",
show_ident(decl->ident));
base_type->ctype.base_type = &int_ctype;
}
if (base_type->ctype.base_type == &incomplete_ctype) {
warning(decl->pos, "'%s()' has implicit return type",
show_ident(decl->ident));
base_type->ctype.base_type = &int_ctype;
}
/* apply attributes placed after the declarator */
decl->ctype.modifiers |= ctx.f_modifiers;
/* K&R argument declaration? */
if (lookup_type(token))
return parse_k_r_arguments(token, decl, list);
if (match_op(token, '{'))
return parse_function_body(token, decl, list);
if (!(decl->ctype.modifiers & MOD_STATIC))
decl->ctype.modifiers |= MOD_EXTERN;
} else if (base_type == &void_ctype && !(decl->ctype.modifiers & MOD_EXTERN)) {
sparse_error(token->pos, "void declaration");
}
if (base_type == &incomplete_ctype) {
warning(decl->pos, "'%s' has implicit type", show_ident(decl->ident));
decl->ctype.base_type = &int_ctype;;
}
for (;;) {
if (!is_typedef && match_op(token, '=')) {
struct token *next = token->next;
token = initializer(&decl->initializer, next);
if (token == next)
sparse_error(token->pos, "expression expected before '%s'", show_token(token));
}
if (!is_typedef) {
if (validate_decl)
validate_decl(decl);
if (decl->initializer && decl->ctype.modifiers & MOD_EXTERN) {
warning(decl->pos, "symbol with external linkage has initializer");
decl->ctype.modifiers &= ~MOD_EXTERN;
}
if (!(decl->ctype.modifiers & (MOD_EXTERN | MOD_INLINE))) {
add_symbol(list, decl);
fn_local_symbol(decl);
}
}
check_declaration(decl);
if (decl->same_symbol) {
decl->definition = decl->same_symbol->definition;
decl->op = decl->same_symbol->op;
if (is_typedef) {
// TODO: handle -std=c89 --pedantic
check_duplicates(decl);
}
}
if (ctx.autotype) {
const char *msg = NULL;
if (decl->ctype.base_type != &autotype_ctype)
msg = "on non-identifier";
else if (match_op(token, ','))
msg = "on declaration list";
else if (!decl->initializer)
msg = "without initializer";
else if (decl->initializer->type == EXPR_SYMBOL &&
decl->initializer->symbol == decl)
msg = "on self-init var";
if (msg) {
sparse_error(decl->pos, "__auto_type %s", msg);
decl->ctype.base_type = &bad_ctype;
}
}
if (!match_op(token, ','))
break;
token = token->next;
ident = NULL;
decl = alloc_symbol(token->pos, SYM_NODE);
ctx.ctype = saved;
token = handle_attributes(token, &ctx);
token = declarator(token, &ctx);
token = handle_asm_name(token, &ctx);
token = handle_attributes(token, &ctx);
apply_modifiers(token->pos, &ctx);
decl->ctype = ctx.ctype;
decl->ctype.modifiers |= mod;
decl->endpos = token->pos;
if (!ident) {
sparse_error(token->pos, "expected identifier name in type definition");
return token;
}
if (is_typedef)
decl->ctype.modifiers |= MOD_USERTYPE;
bind_symbol(decl, ident, is_typedef ? NS_TYPEDEF: NS_SYMBOL);
/* Function declarations are automatically extern unless specifically static */
base_type = decl->ctype.base_type;
if (!is_typedef && base_type && base_type->type == SYM_FN) {
if (!(decl->ctype.modifiers & MOD_STATIC))
decl->ctype.modifiers |= MOD_EXTERN;
}
}
return expect(token, ';', "at end of declaration");
}
sparse-0.6.4/parse.dtd 0000664 0000000 0000000 00000003147 14115310122 0014620 0 ustar 00root root 0000000 0000000
sparse-0.6.4/parse.h 0000664 0000000 0000000 00000010560 14115310122 0014271 0 ustar 00root root 0000000 0000000 #ifndef PARSE_H
#define PARSE_H
/*
* Basic parsing data structures. Statements and symbols.
*
* Copyright (C) 2003 Transmeta Corp.
* 2003 Linus Torvalds
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "symbol.h"
enum statement_type {
STMT_NONE,
STMT_DECLARATION,
STMT_EXPRESSION,
STMT_COMPOUND,
STMT_IF,
STMT_RETURN,
STMT_CASE,
STMT_SWITCH,
STMT_ITERATOR,
STMT_LABEL,
STMT_GOTO,
STMT_ASM,
STMT_CONTEXT,
STMT_RANGE,
};
struct statement {
enum statement_type type;
struct position pos;
union {
struct /* declaration */ {
struct symbol_list *declaration;
};
struct /* context */ {
struct expression *expression;
struct expression *context;
};
struct /* return_statement */ {
struct expression *ret_value;
struct symbol *ret_target;
};
struct /* if_statement */ {
struct expression *if_conditional;
struct statement *if_true;
struct statement *if_false;
};
struct /* compound_struct */ {
struct statement_list *stmts;
struct symbol *ret;
struct symbol *inline_fn;
struct statement *args;
};
struct /* labeled_struct */ {
struct symbol *label_identifier;
struct scope *label_scope;
struct statement *label_statement;
};
struct /* case_struct */ {
struct expression *case_expression;
struct expression *case_to;
struct statement *case_statement;
struct symbol *case_label;
};
struct /* switch_struct */ {
struct expression *switch_expression;
struct statement *switch_statement;
struct symbol *switch_break, *switch_case;
};
struct /* iterator_struct */ {
struct symbol *iterator_break;
struct symbol *iterator_continue;
struct symbol_list *iterator_syms;
struct statement *iterator_pre_statement;
struct expression *iterator_pre_condition;
struct statement *iterator_statement;
struct statement *iterator_post_statement;
struct expression *iterator_post_condition;
};
struct /* goto_struct */ {
struct symbol *goto_label;
/* computed gotos have these: */
struct expression *goto_expression;
struct symbol_list *target_list;
};
struct /* asm */ {
struct expression *asm_string;
struct asm_operand_list *asm_outputs;
struct asm_operand_list *asm_inputs;
struct expression_list *asm_clobbers;
struct symbol_list *asm_labels;
};
struct /* range */ {
struct expression *range_expression;
struct expression *range_low;
struct expression *range_high;
};
};
};
extern struct symbol_list *function_computed_target_list;
extern struct statement_list *function_computed_goto_list;
extern struct token *parse_expression(struct token *, struct expression **);
extern struct symbol *label_symbol(struct token *token, int used);
extern void check_label_usage(struct symbol *label, struct position use_pos);
extern int show_statement(struct statement *);
extern void show_statement_list(struct statement_list *, const char *);
extern int show_expression(struct expression *);
typedef void (*validate_decl_t)(struct symbol *decl);
extern struct token *external_declaration(struct token *, struct symbol_list **, validate_decl_t);
extern struct symbol *ctype_integer(int size, int want_unsigned);
extern int inline_function(struct expression *expr, struct symbol *sym);
extern void uninline(struct symbol *sym);
extern void init_parser(int);
struct token *expect(struct token *, int, const char *);
#endif /* PARSE_H */
sparse-0.6.4/pre-process.c 0000664 0000000 0000000 00000156525 14115310122 0015430 0 ustar 00root root 0000000 0000000 /*
* Do C preprocessing, based on a token list gathered by
* the tokenizer.
*
* This may not be the smartest preprocessor on the planet.
*
* Copyright (C) 2003 Transmeta Corp.
* 2003-2004 Linus Torvalds
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "lib.h"
#include "allocate.h"
#include "parse.h"
#include "token.h"
#include "symbol.h"
#include "expression.h"
#include "scope.h"
static struct ident_list *macros; // only needed for -dD
static int false_nesting = 0;
static int counter_macro = 0; // __COUNTER__ expansion
static int include_level = 0;
static int expanding = 0;
#define INCLUDEPATHS 300
const char *includepath[INCLUDEPATHS+1] = {
"",
"/usr/include",
"/usr/local/include",
NULL
};
static const char **quote_includepath = includepath;
static const char **angle_includepath = includepath + 1;
static const char **isys_includepath = includepath + 1;
static const char **sys_includepath = includepath + 1;
static const char **dirafter_includepath = includepath + 3;
#define dirty_stream(stream) \
do { \
if (!stream->dirty) { \
stream->dirty = 1; \
if (!stream->ifndef) \
stream->protect = NULL; \
} \
} while(0)
#define end_group(stream) \
do { \
if (stream->ifndef == stream->top_if) { \
stream->ifndef = NULL; \
if (!stream->dirty) \
stream->protect = NULL; \
else if (stream->protect) \
stream->dirty = 0; \
} \
} while(0)
#define nesting_error(stream) \
do { \
stream->dirty = 1; \
stream->ifndef = NULL; \
stream->protect = NULL; \
} while(0)
static struct token *alloc_token(struct position *pos)
{
struct token *token = __alloc_token(0);
token->pos.stream = pos->stream;
token->pos.line = pos->line;
token->pos.pos = pos->pos;
token->pos.whitespace = 1;
return token;
}
/* Expand symbol 'sym' at '*list' */
static int expand(struct token **, struct symbol *);
static void replace_with_string(struct token *token, const char *str)
{
int size = strlen(str) + 1;
struct string *s = __alloc_string(size);
s->length = size;
memcpy(s->data, str, size);
token_type(token) = TOKEN_STRING;
token->string = s;
}
static void replace_with_integer(struct token *token, unsigned int val)
{
char *buf = __alloc_bytes(11);
sprintf(buf, "%u", val);
token_type(token) = TOKEN_NUMBER;
token->number = buf;
}
static struct symbol *lookup_macro(struct ident *ident)
{
struct symbol *sym = lookup_symbol(ident, NS_MACRO | NS_UNDEF);
if (sym && sym->namespace != NS_MACRO)
sym = NULL;
return sym;
}
static int token_defined(struct token *token)
{
if (token_type(token) == TOKEN_IDENT) {
struct symbol *sym = lookup_macro(token->ident);
if (sym) {
sym->used_in = file_scope;
return 1;
}
return 0;
}
sparse_error(token->pos, "expected preprocessor identifier");
return 0;
}
static void replace_with_bool(struct token *token, bool val)
{
static const char *string[] = { "0", "1" };
token_type(token) = TOKEN_NUMBER;
token->number = string[val];
}
static void replace_with_defined(struct token *token)
{
replace_with_bool(token, token_defined(token));
}
static void expand_line(struct token *token)
{
replace_with_integer(token, token->pos.line);
}
static void expand_file(struct token *token)
{
replace_with_string(token, stream_name(token->pos.stream));
}
static void expand_basefile(struct token *token)
{
replace_with_string(token, base_filename);
}
static time_t t = 0;
static void expand_date(struct token *token)
{
static char buffer[12]; /* __DATE__: 3 + ' ' + 2 + ' ' + 4 + '\0' */
if (!t)
time(&t);
strftime(buffer, 12, "%b %e %Y", localtime(&t));
replace_with_string(token, buffer);
}
static void expand_time(struct token *token)
{
static char buffer[9]; /* __TIME__: 2 + ':' + 2 + ':' + 2 + '\0' */
if (!t)
time(&t);
strftime(buffer, 9, "%T", localtime(&t));
replace_with_string(token, buffer);
}
static void expand_counter(struct token *token)
{
replace_with_integer(token, counter_macro++);
}
static void expand_include_level(struct token *token)
{
replace_with_integer(token, include_level - 1);
}
static int expand_one_symbol(struct token **list)
{
struct token *token = *list;
struct symbol *sym;
if (token->pos.noexpand)
return 1;
sym = lookup_macro(token->ident);
if (!sym)
return 1;
if (sym->expand_simple) {
sym->expand_simple(token);
return 1;
} else {
int rc;
sym->used_in = file_scope;
expanding = 1;
rc = expand(list, sym);
expanding = 0;
return rc;
}
}
static inline struct token *scan_next(struct token **where)
{
struct token *token = *where;
if (token_type(token) != TOKEN_UNTAINT)
return token;
do {
token->ident->tainted = 0;
token = token->next;
} while (token_type(token) == TOKEN_UNTAINT);
*where = token;
return token;
}
static void expand_list(struct token **list)
{
struct token *next;
while (!eof_token(next = scan_next(list))) {
if (token_type(next) != TOKEN_IDENT || expand_one_symbol(list))
list = &next->next;
}
}
static void preprocessor_line(struct stream *stream, struct token **line);
static struct token *collect_arg(struct token *prev, int vararg, struct position *pos, int count)
{
struct stream *stream = input_streams + prev->pos.stream;
struct token **p = &prev->next;
struct token *next;
int nesting = 0;
while (!eof_token(next = scan_next(p))) {
if (next->pos.newline && match_op(next, '#')) {
if (!next->pos.noexpand) {
preprocessor_line(stream, p);
__free_token(next); /* Free the '#' token */
continue;
}
}
switch (token_type(next)) {
case TOKEN_STREAMEND:
case TOKEN_STREAMBEGIN:
*p = &eof_token_entry;
return next;
case TOKEN_STRING:
case TOKEN_WIDE_STRING:
if (count > 1)
next->string->immutable = 1;
break;
}
if (false_nesting) {
*p = next->next;
__free_token(next);
continue;
}
if (match_op(next, '(')) {
nesting++;
} else if (match_op(next, ')')) {
if (!nesting--)
break;
} else if (match_op(next, ',') && !nesting && !vararg) {
break;
}
next->pos.stream = pos->stream;
next->pos.line = pos->line;
next->pos.pos = pos->pos;
next->pos.newline = 0;
p = &next->next;
}
*p = &eof_token_entry;
return next;
}
/*
* We store arglist as [arg1] ... eof
*/
struct arg {
struct token *arg;
struct token *expanded;
struct token *str;
int n_normal;
int n_quoted;
int n_str;
};
static int collect_arguments(struct token *start, struct token *arglist, struct arg *args, struct token *what)
{
int wanted = arglist->count.normal;
struct token *next = NULL;
int count = 0;
arglist = arglist->next; /* skip counter */
if (!wanted) {
next = collect_arg(start, 0, &what->pos, 0);
if (eof_token(next))
goto Eclosing;
if (!eof_token(start->next) || !match_op(next, ')')) {
count++;
goto Emany;
}
} else {
for (count = 0; count < wanted; count++) {
struct argcount *p = &arglist->next->count;
next = collect_arg(start, p->vararg, &what->pos, p->normal);
if (eof_token(next))
goto Eclosing;
if (p->vararg && wanted == 1 && eof_token(start->next))
break;
arglist = arglist->next->next;
args[count].arg = start->next;
args[count].n_normal = p->normal;
args[count].n_quoted = p->quoted;
args[count].n_str = p->str;
if (match_op(next, ')')) {
count++;
break;
}
start = next;
}
if (count == wanted && !match_op(next, ')'))
goto Emany;
if (count == wanted - 1) {
struct argcount *p = &arglist->next->count;
if (!p->vararg)
goto Efew;
args[count].arg = NULL;
args[count].n_normal = p->normal;
args[count].n_quoted = p->quoted;
args[count].n_str = p->str;
}
if (count < wanted - 1)
goto Efew;
}
what->next = next->next;
return 1;
Efew:
sparse_error(what->pos, "macro \"%s\" requires %d arguments, but only %d given",
show_token(what), wanted, count);
goto out;
Emany:
while (match_op(next, ',')) {
next = collect_arg(next, 0, &what->pos, 0);
count++;
}
if (eof_token(next))
goto Eclosing;
sparse_error(what->pos, "macro \"%s\" passed %d arguments, but takes just %d",
show_token(what), count, wanted);
goto out;
Eclosing:
sparse_error(what->pos, "unterminated argument list invoking macro \"%s\"",
show_token(what));
out:
what->next = next->next;
return 0;
}
static struct token *dup_list(struct token *list)
{
struct token *res = NULL;
struct token **p = &res;
while (!eof_token(list)) {
struct token *newtok = __alloc_token(0);
*newtok = *list;
*p = newtok;
p = &newtok->next;
list = list->next;
}
return res;
}
static const char *show_token_sequence(struct token *token, int quote)
{
static char buffer[MAX_STRING];
char *ptr = buffer;
int whitespace = 0;
if (!token && !quote)
return "";
while (!eof_token(token)) {
const char *val = quote ? quote_token(token) : show_token(token);
int len = strlen(val);
if (ptr + whitespace + len >= buffer + sizeof(buffer)) {
sparse_error(token->pos, "too long token expansion");
break;
}
if (whitespace)
*ptr++ = ' ';
memcpy(ptr, val, len);
ptr += len;
token = token->next;
whitespace = token->pos.whitespace;
}
*ptr = 0;
return buffer;
}
static struct token *stringify(struct token *arg)
{
const char *s = show_token_sequence(arg, 1);
int size = strlen(s)+1;
struct token *token = __alloc_token(0);
struct string *string = __alloc_string(size);
memcpy(string->data, s, size);
string->length = size;
token->pos = arg->pos;
token_type(token) = TOKEN_STRING;
token->string = string;
token->next = &eof_token_entry;
return token;
}
static void expand_arguments(int count, struct arg *args)
{
int i;
for (i = 0; i < count; i++) {
struct token *arg = args[i].arg;
if (!arg)
arg = &eof_token_entry;
if (args[i].n_str)
args[i].str = stringify(arg);
if (args[i].n_normal) {
if (!args[i].n_quoted) {
args[i].expanded = arg;
args[i].arg = NULL;
} else if (eof_token(arg)) {
args[i].expanded = arg;
} else {
args[i].expanded = dup_list(arg);
}
expand_list(&args[i].expanded);
}
}
}
/*
* Possibly valid combinations:
* - ident + ident -> ident
* - ident + number -> ident unless number contains '.', '+' or '-'.
* - 'L' + char constant -> wide char constant
* - 'L' + string literal -> wide string literal
* - number + number -> number
* - number + ident -> number
* - number + '.' -> number
* - number + '+' or '-' -> number, if number used to end on [eEpP].
* - '.' + number -> number, if number used to start with a digit.
* - special + special -> either special or an error.
*/
static enum token_type combine(struct token *left, struct token *right, char *p)
{
int len;
enum token_type t1 = token_type(left), t2 = token_type(right);
if (t1 != TOKEN_IDENT && t1 != TOKEN_NUMBER && t1 != TOKEN_SPECIAL)
return TOKEN_ERROR;
if (t1 == TOKEN_IDENT && left->ident == &L_ident) {
if (t2 >= TOKEN_CHAR && t2 < TOKEN_WIDE_CHAR)
return t2 + TOKEN_WIDE_CHAR - TOKEN_CHAR;
if (t2 == TOKEN_STRING)
return TOKEN_WIDE_STRING;
}
if (t2 != TOKEN_IDENT && t2 != TOKEN_NUMBER && t2 != TOKEN_SPECIAL)
return TOKEN_ERROR;
strcpy(p, show_token(left));
strcat(p, show_token(right));
len = strlen(p);
if (len >= 256)
return TOKEN_ERROR;
if (t1 == TOKEN_IDENT) {
if (t2 == TOKEN_SPECIAL)
return TOKEN_ERROR;
if (t2 == TOKEN_NUMBER && strpbrk(p, "+-."))
return TOKEN_ERROR;
return TOKEN_IDENT;
}
if (t1 == TOKEN_NUMBER) {
if (t2 == TOKEN_SPECIAL) {
switch (right->special) {
case '.':
break;
case '+': case '-':
if (strchr("eEpP", p[len - 2]))
break;
default:
return TOKEN_ERROR;
}
}
return TOKEN_NUMBER;
}
if (p[0] == '.' && isdigit((unsigned char)p[1]))
return TOKEN_NUMBER;
return TOKEN_SPECIAL;
}
static int merge(struct token *left, struct token *right)
{
static char buffer[512];
enum token_type res = combine(left, right, buffer);
int n;
switch (res) {
case TOKEN_IDENT:
left->ident = built_in_ident(buffer);
left->pos.noexpand = 0;
return 1;
case TOKEN_NUMBER:
token_type(left) = TOKEN_NUMBER; /* could be . + num */
left->number = xstrdup(buffer);
return 1;
case TOKEN_SPECIAL:
if (buffer[2] && buffer[3])
break;
for (n = SPECIAL_BASE; n < SPECIAL_ARG_SEPARATOR; n++) {
if (!memcmp(buffer, combinations[n-SPECIAL_BASE], 3)) {
left->special = n;
return 1;
}
}
break;
case TOKEN_WIDE_CHAR:
case TOKEN_WIDE_STRING:
token_type(left) = res;
left->pos.noexpand = 0;
left->string = right->string;
return 1;
case TOKEN_WIDE_CHAR_EMBEDDED_0 ... TOKEN_WIDE_CHAR_EMBEDDED_3:
token_type(left) = res;
left->pos.noexpand = 0;
memcpy(left->embedded, right->embedded, 4);
return 1;
default:
;
}
sparse_error(left->pos, "'##' failed: concatenation is not a valid token");
return 0;
}
static struct token *dup_token(struct token *token, struct position *streampos)
{
struct token *alloc = alloc_token(streampos);
token_type(alloc) = token_type(token);
alloc->pos.newline = token->pos.newline;
alloc->pos.whitespace = token->pos.whitespace;
alloc->number = token->number;
alloc->pos.noexpand = token->pos.noexpand;
return alloc;
}
static struct token **copy(struct token **where, struct token *list, int *count)
{
int need_copy = --*count;
while (!eof_token(list)) {
struct token *token;
if (need_copy)
token = dup_token(list, &list->pos);
else
token = list;
if (token_type(token) == TOKEN_IDENT && token->ident->tainted)
token->pos.noexpand = 1;
*where = token;
where = &token->next;
list = list->next;
}
*where = &eof_token_entry;
return where;
}
static int handle_kludge(struct token **p, struct arg *args)
{
struct token *t = (*p)->next->next;
while (1) {
struct arg *v = &args[t->argnum];
if (token_type(t->next) != TOKEN_CONCAT) {
if (v->arg) {
/* ignore the first ## */
*p = (*p)->next;
return 0;
}
/* skip the entire thing */
*p = t;
return 1;
}
if (v->arg && !eof_token(v->arg))
return 0; /* no magic */
t = t->next->next;
}
}
static struct token **substitute(struct token **list, struct token *body, struct arg *args)
{
struct position *base_pos = &(*list)->pos;
int *count;
enum {Normal, Placeholder, Concat} state = Normal;
for (; !eof_token(body); body = body->next) {
struct token *added, *arg;
struct token **tail;
struct token *t;
switch (token_type(body)) {
case TOKEN_GNU_KLUDGE:
/*
* GNU kludge: if we had ##, behaviour
* depends on whether we had enough arguments to have
* a vararg. If we did, ## is just ignored. Otherwise
* both , and ## are ignored. Worse, there can be
* an arbitrary number of ## in between; if all of
* those are empty, we act as if they hadn't been there,
* otherwise we act as if the kludge didn't exist.
*/
t = body;
if (handle_kludge(&body, args)) {
if (state == Concat)
state = Normal;
else
state = Placeholder;
continue;
}
added = dup_token(t, base_pos);
token_type(added) = TOKEN_SPECIAL;
tail = &added->next;
break;
case TOKEN_STR_ARGUMENT:
arg = args[body->argnum].str;
count = &args[body->argnum].n_str;
goto copy_arg;
case TOKEN_QUOTED_ARGUMENT:
arg = args[body->argnum].arg;
count = &args[body->argnum].n_quoted;
if (!arg || eof_token(arg)) {
if (state == Concat)
state = Normal;
else
state = Placeholder;
continue;
}
goto copy_arg;
case TOKEN_MACRO_ARGUMENT:
arg = args[body->argnum].expanded;
count = &args[body->argnum].n_normal;
if (eof_token(arg)) {
state = Normal;
continue;
}
copy_arg:
tail = copy(&added, arg, count);
added->pos.newline = body->pos.newline;
added->pos.whitespace = body->pos.whitespace;
break;
case TOKEN_CONCAT:
if (state == Placeholder)
state = Normal;
else
state = Concat;
continue;
case TOKEN_IDENT:
added = dup_token(body, base_pos);
if (added->ident->tainted)
added->pos.noexpand = 1;
tail = &added->next;
break;
default:
added = dup_token(body, base_pos);
tail = &added->next;
break;
}
/*
* if we got to doing real concatenation, we already have
* added something into the list, so containing_token() is OK.
*/
if (state == Concat && merge(containing_token(list), added)) {
*list = added->next;
if (tail != &added->next)
list = tail;
} else {
*list = added;
list = tail;
}
state = Normal;
}
*list = &eof_token_entry;
return list;
}
static int expand(struct token **list, struct symbol *sym)
{
struct token *last;
struct token *token = *list;
struct ident *expanding = token->ident;
struct token **tail;
struct token *expansion = sym->expansion;
int nargs = sym->arglist ? sym->arglist->count.normal : 0;
struct arg args[nargs];
if (expanding->tainted) {
token->pos.noexpand = 1;
return 1;
}
if (sym->arglist) {
if (!match_op(scan_next(&token->next), '('))
return 1;
if (!collect_arguments(token->next, sym->arglist, args, token))
return 1;
expand_arguments(nargs, args);
}
if (sym->expand)
return sym->expand(token, args) ? 0 : 1;
expanding->tainted = 1;
last = token->next;
tail = substitute(list, expansion, args);
/*
* Note that it won't be eof - at least TOKEN_UNTAINT will be there.
* We still can lose the newline flag if the sucker expands to nothing,
* but the price of dealing with that is probably too high (we'd need
* to collect the flags during scan_next())
*/
(*list)->pos.newline = token->pos.newline;
(*list)->pos.whitespace = token->pos.whitespace;
*tail = last;
return 0;
}
static const char *token_name_sequence(struct token *token, int endop, struct token *start)
{
static char buffer[256];
char *ptr = buffer;
while (!eof_token(token) && !match_op(token, endop)) {
int len;
const char *val = token->string->data;
if (token_type(token) != TOKEN_STRING)
val = show_token(token);
len = strlen(val);
memcpy(ptr, val, len);
ptr += len;
token = token->next;
}
*ptr = 0;
if (endop && !match_op(token, endop))
sparse_error(start->pos, "expected '>' at end of filename");
return buffer;
}
static int already_tokenized(const char *path)
{
int stream, next;
for (stream = *hash_stream(path); stream >= 0 ; stream = next) {
struct stream *s = input_streams + stream;
next = s->next_stream;
if (s->once) {
if (strcmp(path, s->name))
continue;
return 1;
}
if (s->constant != CONSTANT_FILE_YES)
continue;
if (strcmp(path, s->name))
continue;
if (s->protect && !lookup_macro(s->protect))
continue;
return 1;
}
return 0;
}
/* Handle include of header files.
* The relevant options are made compatible with gcc. The only options that
* are not supported is -withprefix and friends.
*
* Three set of include paths are known:
* quote_includepath: Path to search when using #include "file.h"
* angle_includepath: Paths to search when using #include
* isys_includepath: Paths specified with -isystem, come before the
* built-in system include paths. Gcc would suppress
* warnings from system headers. Here we separate
* them from the angle_ ones to keep search ordering.
*
* sys_includepath: Built-in include paths.
* dirafter_includepath Paths added with -dirafter.
*
* The above is implemented as one array with pointers
* +--------------+
* quote_includepath ---> | |
* +--------------+
* | |
* +--------------+
* angle_includepath ---> | |
* +--------------+
* isys_includepath ---> | |
* +--------------+
* sys_includepath ---> | |
* +--------------+
* dirafter_includepath -> | |
* +--------------+
*
* -I dir insert dir just before isys_includepath and move the rest
* -I- makes all dirs specified with -I before to quote dirs only and
* angle_includepath is set equal to isys_includepath.
* -nostdinc removes all sys dirs by storing NULL in entry pointed
* to by * sys_includepath. Note that this will reset all dirs built-in
* and added before -nostdinc by -isystem and -idirafter.
* -isystem dir adds dir where isys_includepath points adding this dir as
* first systemdir
* -idirafter dir adds dir to the end of the list
*/
static void set_stream_include_path(struct stream *stream)
{
const char *path = stream->path;
if (!path) {
const char *p = strrchr(stream->name, '/');
path = "";
if (p) {
int len = p - stream->name + 1;
char *m = malloc(len+1);
/* This includes the final "/" */
memcpy(m, stream->name, len);
m[len] = 0;
path = m;
/* normalize this path */
while (path[0] == '.' && path[1] == '/') {
path += 2;
while (path[0] == '/')
path++;
}
}
stream->path = path;
}
includepath[0] = path;
}
static int try_include(struct position pos, const char *path, const char *filename, int flen, struct token **where, const char **next_path)
{
int fd;
int plen = strlen(path);
static char fullname[PATH_MAX];
memcpy(fullname, path, plen);
if (plen && path[plen-1] != '/') {
fullname[plen] = '/';
plen++;
}
memcpy(fullname+plen, filename, flen);
if (already_tokenized(fullname))
return 1;
fd = open(fullname, O_RDONLY);
if (fd >= 0) {
char *streamname = xmemdup(fullname, plen + flen);
*where = tokenize(&pos, streamname, fd, *where, next_path);
close(fd);
return 1;
}
return 0;
}
static int do_include_path(const char **pptr, struct token **list, struct token *token, const char *filename, int flen)
{
const char *path;
while ((path = *pptr++) != NULL) {
if (!try_include(token->pos, path, filename, flen, list, pptr))
continue;
return 1;
}
return 0;
}
static int free_preprocessor_line(struct token *token)
{
while (token_type(token) != TOKEN_EOF) {
struct token *free = token;
token = token->next;
__free_token(free);
};
return 1;
}
static int handle_include_path(struct stream *stream, struct token **list, struct token *token, int how)
{
const char *filename;
struct token *next;
const char **path;
int expect;
int flen;
next = token->next;
expect = '>';
if (!match_op(next, '<')) {
expand_list(&token->next);
expect = 0;
next = token;
if (match_op(token->next, '<')) {
next = token->next;
expect = '>';
}
}
token = next->next;
filename = token_name_sequence(token, expect, token);
flen = strlen(filename) + 1;
/* Absolute path? */
if (filename[0] == '/') {
if (try_include(token->pos, "", filename, flen, list, includepath))
return 0;
goto out;
}
switch (how) {
case 1:
path = stream->next_path;
break;
case 2:
includepath[0] = "";
path = includepath;
break;
default:
/* Dir of input file is first dir to search for quoted includes */
set_stream_include_path(stream);
path = expect ? angle_includepath : quote_includepath;
break;
}
/* Check the standard include paths.. */
if (do_include_path(path, list, token, filename, flen))
return 0;
out:
error_die(token->pos, "unable to open '%s'", filename);
}
static int handle_include(struct stream *stream, struct token **list, struct token *token)
{
return handle_include_path(stream, list, token, 0);
}
static int handle_include_next(struct stream *stream, struct token **list, struct token *token)
{
return handle_include_path(stream, list, token, 1);
}
static int handle_argv_include(struct stream *stream, struct token **list, struct token *token)
{
return handle_include_path(stream, list, token, 2);
}
static int token_different(struct token *t1, struct token *t2)
{
int different;
if (token_type(t1) != token_type(t2))
return 1;
switch (token_type(t1)) {
case TOKEN_IDENT:
different = t1->ident != t2->ident;
break;
case TOKEN_ARG_COUNT:
case TOKEN_UNTAINT:
case TOKEN_CONCAT:
case TOKEN_GNU_KLUDGE:
different = 0;
break;
case TOKEN_NUMBER:
different = strcmp(t1->number, t2->number);
break;
case TOKEN_SPECIAL:
different = t1->special != t2->special;
break;
case TOKEN_MACRO_ARGUMENT:
case TOKEN_QUOTED_ARGUMENT:
case TOKEN_STR_ARGUMENT:
different = t1->argnum != t2->argnum;
break;
case TOKEN_CHAR_EMBEDDED_0 ... TOKEN_CHAR_EMBEDDED_3:
case TOKEN_WIDE_CHAR_EMBEDDED_0 ... TOKEN_WIDE_CHAR_EMBEDDED_3:
different = memcmp(t1->embedded, t2->embedded, 4);
break;
case TOKEN_CHAR:
case TOKEN_WIDE_CHAR:
case TOKEN_STRING:
case TOKEN_WIDE_STRING: {
struct string *s1, *s2;
s1 = t1->string;
s2 = t2->string;
different = 1;
if (s1->length != s2->length)
break;
different = memcmp(s1->data, s2->data, s1->length);
break;
}
default:
different = 1;
break;
}
return different;
}
static int token_list_different(struct token *list1, struct token *list2)
{
for (;;) {
if (list1 == list2)
return 0;
if (!list1 || !list2)
return 1;
if (token_different(list1, list2))
return 1;
list1 = list1->next;
list2 = list2->next;
}
}
static inline void set_arg_count(struct token *token)
{
token_type(token) = TOKEN_ARG_COUNT;
token->count.normal = token->count.quoted =
token->count.str = token->count.vararg = 0;
}
static struct token *parse_arguments(struct token *list)
{
struct token *arg = list->next, *next = list;
struct argcount *count = &list->count;
set_arg_count(list);
if (match_op(arg, ')')) {
next = arg->next;
list->next = &eof_token_entry;
return next;
}
while (token_type(arg) == TOKEN_IDENT) {
if (arg->ident == &__VA_ARGS___ident)
goto Eva_args;
if (!++count->normal)
goto Eargs;
next = arg->next;
if (match_op(next, ',')) {
set_arg_count(next);
arg = next->next;
continue;
}
if (match_op(next, ')')) {
set_arg_count(next);
next = next->next;
arg->next->next = &eof_token_entry;
return next;
}
/* normal cases are finished here */
if (match_op(next, SPECIAL_ELLIPSIS)) {
if (match_op(next->next, ')')) {
set_arg_count(next);
next->count.vararg = 1;
next = next->next;
arg->next->next = &eof_token_entry;
return next->next;
}
arg = next;
goto Enotclosed;
}
if (eof_token(next)) {
goto Enotclosed;
} else {
arg = next;
goto Ebadstuff;
}
}
if (match_op(arg, SPECIAL_ELLIPSIS)) {
next = arg->next;
token_type(arg) = TOKEN_IDENT;
arg->ident = &__VA_ARGS___ident;
if (!match_op(next, ')'))
goto Enotclosed;
if (!++count->normal)
goto Eargs;
set_arg_count(next);
next->count.vararg = 1;
next = next->next;
arg->next->next = &eof_token_entry;
return next;
}
if (eof_token(arg)) {
arg = next;
goto Enotclosed;
}
if (match_op(arg, ','))
goto Emissing;
else
goto Ebadstuff;
Emissing:
sparse_error(arg->pos, "parameter name missing");
return NULL;
Ebadstuff:
sparse_error(arg->pos, "\"%s\" may not appear in macro parameter list",
show_token(arg));
return NULL;
Enotclosed:
sparse_error(arg->pos, "missing ')' in macro parameter list");
return NULL;
Eva_args:
sparse_error(arg->pos, "__VA_ARGS__ can only appear in the expansion of a C99 variadic macro");
return NULL;
Eargs:
sparse_error(arg->pos, "too many arguments in macro definition");
return NULL;
}
static int try_arg(struct token *token, enum token_type type, struct token *arglist)
{
struct ident *ident = token->ident;
int nr;
if (!arglist || token_type(token) != TOKEN_IDENT)
return 0;
arglist = arglist->next;
for (nr = 0; !eof_token(arglist); nr++, arglist = arglist->next->next) {
if (arglist->ident == ident) {
struct argcount *count = &arglist->next->count;
int n;
token->argnum = nr;
token_type(token) = type;
switch (type) {
case TOKEN_MACRO_ARGUMENT:
n = ++count->normal;
break;
case TOKEN_QUOTED_ARGUMENT:
n = ++count->quoted;
break;
default:
n = ++count->str;
}
if (n)
return count->vararg ? 2 : 1;
/*
* XXX - need saner handling of that
* (>= 1024 instances of argument)
*/
token_type(token) = TOKEN_ERROR;
return -1;
}
}
return 0;
}
static struct token *handle_hash(struct token **p, struct token *arglist)
{
struct token *token = *p;
if (arglist) {
struct token *next = token->next;
if (!try_arg(next, TOKEN_STR_ARGUMENT, arglist))
goto Equote;
next->pos.whitespace = token->pos.whitespace;
__free_token(token);
token = *p = next;
} else {
token->pos.noexpand = 1;
}
return token;
Equote:
sparse_error(token->pos, "'#' is not followed by a macro parameter");
return NULL;
}
/* token->next is ## */
static struct token *handle_hashhash(struct token *token, struct token *arglist)
{
struct token *last = token;
struct token *concat;
int state = match_op(token, ',');
try_arg(token, TOKEN_QUOTED_ARGUMENT, arglist);
while (1) {
struct token *t;
int is_arg;
/* eat duplicate ## */
concat = token->next;
while (match_op(t = concat->next, SPECIAL_HASHHASH)) {
token->next = t;
__free_token(concat);
concat = t;
}
token_type(concat) = TOKEN_CONCAT;
if (eof_token(t))
goto Econcat;
if (match_op(t, '#')) {
t = handle_hash(&concat->next, arglist);
if (!t)
return NULL;
}
is_arg = try_arg(t, TOKEN_QUOTED_ARGUMENT, arglist);
if (state == 1 && is_arg) {
state = is_arg;
} else {
last = t;
state = match_op(t, ',');
}
token = t;
if (!match_op(token->next, SPECIAL_HASHHASH))
break;
}
/* handle GNU ,##__VA_ARGS__ kludge, in all its weirdness */
if (state == 2)
token_type(last) = TOKEN_GNU_KLUDGE;
return token;
Econcat:
sparse_error(concat->pos, "'##' cannot appear at the ends of macro expansion");
return NULL;
}
static struct token *parse_expansion(struct token *expansion, struct token *arglist, struct ident *name)
{
struct token *token = expansion;
struct token **p;
if (match_op(token, SPECIAL_HASHHASH))
goto Econcat;
for (p = &expansion; !eof_token(token); p = &token->next, token = *p) {
if (match_op(token, '#')) {
token = handle_hash(p, arglist);
if (!token)
return NULL;
}
if (match_op(token->next, SPECIAL_HASHHASH)) {
token = handle_hashhash(token, arglist);
if (!token)
return NULL;
} else {
try_arg(token, TOKEN_MACRO_ARGUMENT, arglist);
}
switch (token_type(token)) {
case TOKEN_ERROR:
goto Earg;
case TOKEN_STRING:
case TOKEN_WIDE_STRING:
token->string->immutable = 1;
break;
}
}
token = alloc_token(&expansion->pos);
token_type(token) = TOKEN_UNTAINT;
token->ident = name;
token->next = *p;
*p = token;
return expansion;
Econcat:
sparse_error(token->pos, "'##' cannot appear at the ends of macro expansion");
return NULL;
Earg:
sparse_error(token->pos, "too many instances of argument in body");
return NULL;
}
static int do_define(struct position pos, struct token *token, struct ident *name,
struct token *arglist, struct token *expansion, int attr)
{
struct symbol *sym;
int ret = 1;
expansion = parse_expansion(expansion, arglist, name);
if (!expansion)
return 1;
sym = lookup_symbol(name, NS_MACRO | NS_UNDEF);
if (sym) {
int clean;
if (attr < sym->attr)
goto out;
clean = (attr == sym->attr && sym->namespace == NS_MACRO);
if (token_list_different(sym->expansion, expansion) ||
token_list_different(sym->arglist, arglist)) {
ret = 0;
if ((clean && attr == SYM_ATTR_NORMAL)
|| sym->used_in == file_scope) {
warning(pos, "preprocessor token %.*s redefined",
name->len, name->name);
info(sym->pos, "this was the original definition");
}
} else if (clean)
goto out;
}
if (!sym || sym->scope != file_scope) {
sym = alloc_symbol(pos, SYM_NODE);
bind_symbol(sym, name, NS_MACRO);
add_ident(¯os, name);
ret = 0;
}
if (!ret) {
sym->expansion = expansion;
sym->arglist = arglist;
if (token) /* Free the "define" token, but not the rest of the line */
__free_token(token);
}
sym->namespace = NS_MACRO;
sym->used_in = NULL;
sym->attr = attr;
out:
return ret;
}
///
// predefine a macro with a printf-formatted value
// @name: the name of the macro
// @weak: 0/1 for a normal or a weak define
// @fmt: the printf format followed by it's arguments.
//
// The type of the value is automatically infered:
// TOKEN_NUMBER if it starts by a digit, TOKEN_IDENT otherwise.
// If @fmt is null or empty, the macro is defined with an empty definition.
void predefine(const char *name, int weak, const char *fmt, ...)
{
struct ident *ident = built_in_ident(name);
struct token *value = &eof_token_entry;
int attr = weak ? SYM_ATTR_WEAK : SYM_ATTR_NORMAL;
if (fmt && fmt[0]) {
static char buf[256];
va_list ap;
va_start(ap, fmt);
vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
value = __alloc_token(0);
if (isdigit((unsigned char)buf[0])) {
token_type(value) = TOKEN_NUMBER;
value->number = xstrdup(buf);
} else {
token_type(value) = TOKEN_IDENT;
value->ident = built_in_ident(buf);
}
value->pos.whitespace = 1;
value->next = &eof_token_entry;
}
do_define(value->pos, NULL, ident, NULL, value, attr);
}
///
// like predefine() but only if one of the non-standard dialect is chosen
void predefine_nostd(const char *name)
{
if ((standard & STANDARD_GNU) || (standard == STANDARD_NONE))
predefine(name, 1, "1");
}
static void predefine_fmt(const char *fmt, int weak, va_list ap)
{
static char buf[256];
vsnprintf(buf, sizeof(buf), fmt, ap);
predefine(buf, weak, "1");
}
void predefine_strong(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
predefine_fmt(fmt, 0, ap);
va_end(ap);
}
void predefine_weak(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
predefine_fmt(fmt, 1, ap);
va_end(ap);
}
static int do_handle_define(struct stream *stream, struct token **line, struct token *token, int attr)
{
struct token *arglist, *expansion;
struct token *left = token->next;
struct ident *name;
if (token_type(left) != TOKEN_IDENT) {
sparse_error(token->pos, "expected identifier to 'define'");
return 1;
}
name = left->ident;
arglist = NULL;
expansion = left->next;
if (!expansion->pos.whitespace) {
if (match_op(expansion, '(')) {
arglist = expansion;
expansion = parse_arguments(expansion);
if (!expansion)
return 1;
} else if (!eof_token(expansion)) {
warning(expansion->pos,
"no whitespace before object-like macro body");
}
}
return do_define(left->pos, token, name, arglist, expansion, attr);
}
static int handle_define(struct stream *stream, struct token **line, struct token *token)
{
return do_handle_define(stream, line, token, SYM_ATTR_NORMAL);
}
static int handle_weak_define(struct stream *stream, struct token **line, struct token *token)
{
return do_handle_define(stream, line, token, SYM_ATTR_WEAK);
}
static int handle_strong_define(struct stream *stream, struct token **line, struct token *token)
{
return do_handle_define(stream, line, token, SYM_ATTR_STRONG);
}
static int do_handle_undef(struct stream *stream, struct token **line, struct token *token, int attr)
{
struct token *left = token->next;
struct symbol *sym;
if (token_type(left) != TOKEN_IDENT) {
sparse_error(token->pos, "expected identifier to 'undef'");
return 1;
}
sym = lookup_symbol(left->ident, NS_MACRO | NS_UNDEF);
if (sym) {
if (attr < sym->attr)
return 1;
if (attr == sym->attr && sym->namespace == NS_UNDEF)
return 1;
} else if (attr <= SYM_ATTR_NORMAL)
return 1;
if (!sym || sym->scope != file_scope) {
sym = alloc_symbol(left->pos, SYM_NODE);
bind_symbol(sym, left->ident, NS_MACRO);
}
sym->namespace = NS_UNDEF;
sym->used_in = NULL;
sym->attr = attr;
return 1;
}
static int handle_undef(struct stream *stream, struct token **line, struct token *token)
{
return do_handle_undef(stream, line, token, SYM_ATTR_NORMAL);
}
static int handle_strong_undef(struct stream *stream, struct token **line, struct token *token)
{
return do_handle_undef(stream, line, token, SYM_ATTR_STRONG);
}
static int preprocessor_if(struct stream *stream, struct token *token, int cond)
{
token_type(token) = false_nesting ? TOKEN_SKIP_GROUPS : TOKEN_IF;
free_preprocessor_line(token->next);
token->next = stream->top_if;
stream->top_if = token;
if (false_nesting || cond != 1)
false_nesting++;
return 0;
}
static int handle_ifdef(struct stream *stream, struct token **line, struct token *token)
{
struct token *next = token->next;
int arg;
if (token_type(next) == TOKEN_IDENT) {
arg = token_defined(next);
} else {
dirty_stream(stream);
if (!false_nesting)
sparse_error(token->pos, "expected preprocessor identifier");
arg = -1;
}
return preprocessor_if(stream, token, arg);
}
static int handle_ifndef(struct stream *stream, struct token **line, struct token *token)
{
struct token *next = token->next;
int arg;
if (token_type(next) == TOKEN_IDENT) {
if (!stream->dirty && !stream->ifndef) {
if (!stream->protect) {
stream->ifndef = token;
stream->protect = next->ident;
} else if (stream->protect == next->ident) {
stream->ifndef = token;
stream->dirty = 1;
}
}
arg = !token_defined(next);
} else {
dirty_stream(stream);
if (!false_nesting)
sparse_error(token->pos, "expected preprocessor identifier");
arg = -1;
}
return preprocessor_if(stream, token, arg);
}
/*
* Expression handling for #if and #elif; it differs from normal expansion
* due to special treatment of "defined".
*/
static int expression_value(struct token **where)
{
struct expression *expr;
struct token *p;
struct token **list = where, **beginning = NULL;
long long value;
int state = 0;
while (!eof_token(p = scan_next(list))) {
switch (state) {
case 0:
if (token_type(p) != TOKEN_IDENT)
break;
if (p->ident == &defined_ident) {
state = 1;
beginning = list;
break;
}
if (!expand_one_symbol(list))
continue;
if (token_type(p) != TOKEN_IDENT)
break;
token_type(p) = TOKEN_ZERO_IDENT;
break;
case 1:
if (match_op(p, '(')) {
state = 2;
} else {
state = 0;
replace_with_defined(p);
*beginning = p;
}
break;
case 2:
if (token_type(p) == TOKEN_IDENT)
state = 3;
else
state = 0;
replace_with_defined(p);
*beginning = p;
break;
case 3:
state = 0;
if (!match_op(p, ')'))
sparse_error(p->pos, "missing ')' after \"defined\"");
*list = p->next;
continue;
}
list = &p->next;
}
p = constant_expression(*where, &expr);
if (!eof_token(p))
sparse_error(p->pos, "garbage at end: %s", show_token_sequence(p, 0));
value = get_expression_value(expr);
return value != 0;
}
static int handle_if(struct stream *stream, struct token **line, struct token *token)
{
int value = 0;
if (!false_nesting)
value = expression_value(&token->next);
dirty_stream(stream);
return preprocessor_if(stream, token, value);
}
static int handle_elif(struct stream * stream, struct token **line, struct token *token)
{
struct token *top_if = stream->top_if;
end_group(stream);
if (!top_if) {
nesting_error(stream);
sparse_error(token->pos, "unmatched #elif within stream");
return 1;
}
if (token_type(top_if) == TOKEN_ELSE) {
nesting_error(stream);
sparse_error(token->pos, "#elif after #else");
if (!false_nesting)
false_nesting = 1;
return 1;
}
dirty_stream(stream);
if (token_type(top_if) != TOKEN_IF)
return 1;
if (false_nesting) {
false_nesting = 0;
if (!expression_value(&token->next))
false_nesting = 1;
} else {
false_nesting = 1;
token_type(top_if) = TOKEN_SKIP_GROUPS;
}
return 1;
}
static int handle_else(struct stream *stream, struct token **line, struct token *token)
{
struct token *top_if = stream->top_if;
end_group(stream);
if (!top_if) {
nesting_error(stream);
sparse_error(token->pos, "unmatched #else within stream");
return 1;
}
if (token_type(top_if) == TOKEN_ELSE) {
nesting_error(stream);
sparse_error(token->pos, "#else after #else");
}
if (false_nesting) {
if (token_type(top_if) == TOKEN_IF)
false_nesting = 0;
} else {
false_nesting = 1;
}
token_type(top_if) = TOKEN_ELSE;
return 1;
}
static int handle_endif(struct stream *stream, struct token **line, struct token *token)
{
struct token *top_if = stream->top_if;
end_group(stream);
if (!top_if) {
nesting_error(stream);
sparse_error(token->pos, "unmatched #endif in stream");
return 1;
}
if (false_nesting)
false_nesting--;
stream->top_if = top_if->next;
__free_token(top_if);
return 1;
}
static int handle_warning(struct stream *stream, struct token **line, struct token *token)
{
warning(token->pos, "%s", show_token_sequence(token->next, 0));
return 1;
}
static int handle_error(struct stream *stream, struct token **line, struct token *token)
{
sparse_error(token->pos, "%s", show_token_sequence(token->next, 0));
return 1;
}
static int handle_nostdinc(struct stream *stream, struct token **line, struct token *token)
{
/*
* Do we have any non-system includes?
* Clear them out if so..
*/
*sys_includepath = NULL;
return 1;
}
static inline void update_inc_ptrs(const char ***where)
{
if (*where <= dirafter_includepath) {
dirafter_includepath++;
/* If this was the entry that we prepend, don't
* rise the lower entries, even if they are at
* the same level. */
if (where == &dirafter_includepath)
return;
}
if (*where <= sys_includepath) {
sys_includepath++;
if (where == &sys_includepath)
return;
}
if (*where <= isys_includepath) {
isys_includepath++;
if (where == &isys_includepath)
return;
}
/* angle_includepath is actually never updated, since we
* don't suppport -iquote rught now. May change some day. */
if (*where <= angle_includepath) {
angle_includepath++;
if (where == &angle_includepath)
return;
}
}
/* Add a path before 'where' and update the pointers associated with the
* includepath array */
static void add_path_entry(struct token *token, const char *path,
const char ***where)
{
const char **dst;
const char *next;
/* Need one free entry.. */
if (includepath[INCLUDEPATHS-2])
error_die(token->pos, "too many include path entries");
/* check that this is not a duplicate */
dst = includepath;
while (*dst) {
if (strcmp(*dst, path) == 0)
return;
dst++;
}
next = path;
dst = *where;
update_inc_ptrs(where);
/*
* Move them all up starting at dst,
* insert the new entry..
*/
do {
const char *tmp = *dst;
*dst = next;
next = tmp;
dst++;
} while (next);
}
static int handle_add_include(struct stream *stream, struct token **line, struct token *token)
{
for (;;) {
token = token->next;
if (eof_token(token))
return 1;
if (token_type(token) != TOKEN_STRING) {
warning(token->pos, "expected path string");
return 1;
}
add_path_entry(token, token->string->data, &isys_includepath);
}
}
static int handle_add_isystem(struct stream *stream, struct token **line, struct token *token)
{
for (;;) {
token = token->next;
if (eof_token(token))
return 1;
if (token_type(token) != TOKEN_STRING) {
sparse_error(token->pos, "expected path string");
return 1;
}
add_path_entry(token, token->string->data, &sys_includepath);
}
}
static int handle_add_system(struct stream *stream, struct token **line, struct token *token)
{
for (;;) {
token = token->next;
if (eof_token(token))
return 1;
if (token_type(token) != TOKEN_STRING) {
sparse_error(token->pos, "expected path string");
return 1;
}
add_path_entry(token, token->string->data, &dirafter_includepath);
}
}
/* Add to end on includepath list - no pointer updates */
static void add_dirafter_entry(struct token *token, const char *path)
{
const char **dst = includepath;
/* Need one free entry.. */
if (includepath[INCLUDEPATHS-2])
error_die(token->pos, "too many include path entries");
/* Add to the end */
while (*dst)
dst++;
*dst = path;
dst++;
*dst = NULL;
}
static int handle_add_dirafter(struct stream *stream, struct token **line, struct token *token)
{
for (;;) {
token = token->next;
if (eof_token(token))
return 1;
if (token_type(token) != TOKEN_STRING) {
sparse_error(token->pos, "expected path string");
return 1;
}
add_dirafter_entry(token, token->string->data);
}
}
static int handle_split_include(struct stream *stream, struct token **line, struct token *token)
{
/*
* -I-
* From info gcc:
* Split the include path. Any directories specified with `-I'
* options before `-I-' are searched only for headers requested with
* `#include "FILE"'; they are not searched for `#include '.
* If additional directories are specified with `-I' options after
* the `-I-', those directories are searched for all `#include'
* directives.
* In addition, `-I-' inhibits the use of the directory of the current
* file directory as the first search directory for `#include "FILE"'.
*/
quote_includepath = includepath+1;
angle_includepath = sys_includepath;
return 1;
}
/*
* We replace "#pragma xxx" with "__pragma__" in the token
* stream. Just as an example.
*
* We'll just #define that away for now, but the theory here
* is that we can use this to insert arbitrary token sequences
* to turn the pragmas into internal front-end sequences for
* when we actually start caring about them.
*
* So eventually this will turn into some kind of extended
* __attribute__() like thing, except called __pragma__(xxx).
*/
static int handle_pragma(struct stream *stream, struct token **line, struct token *token)
{
struct token *next = *line;
if (match_ident(token->next, &once_ident) && eof_token(token->next->next)) {
stream->once = 1;
return 1;
}
token->ident = &pragma_ident;
token->pos.newline = 1;
token->pos.whitespace = 1;
token->pos.pos = 1;
*line = token;
token->next = next;
return 0;
}
/*
* We ignore #line for now.
*/
static int handle_line(struct stream *stream, struct token **line, struct token *token)
{
return 1;
}
static int handle_ident(struct stream *stream, struct token **line, struct token *token)
{
return 1;
}
static int handle_nondirective(struct stream *stream, struct token **line, struct token *token)
{
sparse_error(token->pos, "unrecognized preprocessor line '%s'", show_token_sequence(token, 0));
return 1;
}
static bool expand_has_attribute(struct token *token, struct arg *args)
{
struct token *arg = args[0].expanded;
struct symbol *sym;
if (token_type(arg) != TOKEN_IDENT) {
sparse_error(arg->pos, "identifier expected");
return false;
}
sym = lookup_symbol(arg->ident, NS_KEYWORD);
replace_with_bool(token, sym && sym->op && sym->op->attribute);
return true;
}
static bool expand_has_builtin(struct token *token, struct arg *args)
{
struct token *arg = args[0].expanded;
struct symbol *sym;
if (token_type(arg) != TOKEN_IDENT) {
sparse_error(arg->pos, "identifier expected");
return false;
}
sym = lookup_symbol(arg->ident, NS_SYMBOL);
replace_with_bool(token, sym && sym->builtin);
return true;
}
static bool expand_has_extension(struct token *token, struct arg *args)
{
struct token *arg = args[0].expanded;
struct ident *ident;
bool val = false;
if (token_type(arg) != TOKEN_IDENT) {
sparse_error(arg->pos, "identifier expected");
return false;
}
ident = arg->ident;
if (ident == &c_alignas_ident)
val = true;
else if (ident == &c_alignof_ident)
val = true;
else if (ident == &c_generic_selections_ident)
val = true;
else if (ident == &c_static_assert_ident)
val = true;
replace_with_bool(token, val);
return 1;
}
static bool expand_has_feature(struct token *token, struct arg *args)
{
struct token *arg = args[0].expanded;
struct ident *ident;
bool val = false;
if (token_type(arg) != TOKEN_IDENT) {
sparse_error(arg->pos, "identifier expected");
return false;
}
ident = arg->ident;
if (standard >= STANDARD_C11) {
if (ident == &c_alignas_ident)
val = true;
else if (ident == &c_alignof_ident)
val = true;
else if (ident == &c_generic_selections_ident)
val = true;
else if (ident == &c_static_assert_ident)
val = true;
}
replace_with_bool(token, val);
return 1;
}
static void create_arglist(struct symbol *sym, int count)
{
struct token *token;
struct token **next;
if (!count)
return;
token = __alloc_token(0);
token_type(token) = TOKEN_ARG_COUNT;
token->count.normal = count;
sym->arglist = token;
next = &token->next;
while (count--) {
struct token *id, *uses;
id = __alloc_token(0);
token_type(id) = TOKEN_IDENT;
uses = __alloc_token(0);
token_type(uses) = TOKEN_ARG_COUNT;
uses->count.normal = 1;
*next = id;
id->next = uses;
next = &uses->next;
}
*next = &eof_token_entry;
}
static void init_preprocessor(void)
{
int i;
int stream = init_stream(NULL, "preprocessor", -1, includepath);
static struct {
const char *name;
int (*handler)(struct stream *, struct token **, struct token *);
} normal[] = {
{ "define", handle_define },
{ "weak_define", handle_weak_define },
{ "strong_define", handle_strong_define },
{ "undef", handle_undef },
{ "strong_undef", handle_strong_undef },
{ "warning", handle_warning },
{ "error", handle_error },
{ "include", handle_include },
{ "include_next", handle_include_next },
{ "pragma", handle_pragma },
{ "line", handle_line },
{ "ident", handle_ident },
// our internal preprocessor tokens
{ "nostdinc", handle_nostdinc },
{ "add_include", handle_add_include },
{ "add_isystem", handle_add_isystem },
{ "add_system", handle_add_system },
{ "add_dirafter", handle_add_dirafter },
{ "split_include", handle_split_include },
{ "argv_include", handle_argv_include },
}, special[] = {
{ "ifdef", handle_ifdef },
{ "ifndef", handle_ifndef },
{ "else", handle_else },
{ "endif", handle_endif },
{ "if", handle_if },
{ "elif", handle_elif },
};
static struct {
const char *name;
void (*expand_simple)(struct token *);
bool (*expand)(struct token *, struct arg *args);
} dynamic[] = {
{ "__LINE__", expand_line },
{ "__FILE__", expand_file },
{ "__BASE_FILE__", expand_basefile },
{ "__DATE__", expand_date },
{ "__TIME__", expand_time },
{ "__COUNTER__", expand_counter },
{ "__INCLUDE_LEVEL__", expand_include_level },
{ "__has_attribute", NULL, expand_has_attribute },
{ "__has_builtin", NULL, expand_has_builtin },
{ "__has_extension", NULL, expand_has_extension },
{ "__has_feature", NULL, expand_has_feature },
};
for (i = 0; i < ARRAY_SIZE(normal); i++) {
struct symbol *sym;
sym = create_symbol(stream, normal[i].name, SYM_PREPROCESSOR, NS_PREPROCESSOR);
sym->handler = normal[i].handler;
sym->normal = 1;
}
for (i = 0; i < ARRAY_SIZE(special); i++) {
struct symbol *sym;
sym = create_symbol(stream, special[i].name, SYM_PREPROCESSOR, NS_PREPROCESSOR);
sym->handler = special[i].handler;
sym->normal = 0;
}
for (i = 0; i < ARRAY_SIZE(dynamic); i++) {
struct symbol *sym;
sym = create_symbol(stream, dynamic[i].name, SYM_NODE, NS_MACRO);
sym->expand_simple = dynamic[i].expand_simple;
if ((sym->expand = dynamic[i].expand) != NULL)
create_arglist(sym, 1);
}
counter_macro = 0;
}
static void handle_preprocessor_line(struct stream *stream, struct token **line, struct token *start)
{
int (*handler)(struct stream *, struct token **, struct token *);
struct token *token = start->next;
int is_normal = 1;
int is_cond = 0; // is one of {is,ifdef,ifndef,elif,else,endif}
if (eof_token(token))
return;
if (token_type(token) == TOKEN_IDENT) {
struct symbol *sym = lookup_symbol(token->ident, NS_PREPROCESSOR);
if (sym) {
handler = sym->handler;
is_normal = sym->normal;
is_cond = !sym->normal;
} else {
handler = handle_nondirective;
}
} else if (token_type(token) == TOKEN_NUMBER) {
handler = handle_line;
} else {
handler = handle_nondirective;
}
if (is_normal) {
dirty_stream(stream);
if (false_nesting)
goto out;
}
if (expanding) {
if (!is_cond || Wpedantic)
warning(start->pos, "directive in macro's argument list");
}
if (!handler(stream, line, token)) /* all set */
return;
out:
free_preprocessor_line(token);
}
static void preprocessor_line(struct stream *stream, struct token **line)
{
struct token *start = *line, *next;
struct token **tp = &start->next;
for (;;) {
next = *tp;
if (next->pos.newline)
break;
tp = &next->next;
}
*line = next;
*tp = &eof_token_entry;
handle_preprocessor_line(stream, line, start);
}
static void do_preprocess(struct token **list)
{
struct token *next;
while (!eof_token(next = scan_next(list))) {
struct stream *stream = input_streams + next->pos.stream;
if (next->pos.newline && match_op(next, '#')) {
if (!next->pos.noexpand) {
preprocessor_line(stream, list);
__free_token(next); /* Free the '#' token */
continue;
}
}
switch (token_type(next)) {
case TOKEN_STREAMEND:
if (stream->top_if) {
nesting_error(stream);
sparse_error(stream->top_if->pos, "unterminated preprocessor conditional");
stream->top_if = NULL;
false_nesting = 0;
}
if (!stream->dirty)
stream->constant = CONSTANT_FILE_YES;
*list = next->next;
include_level--;
continue;
case TOKEN_STREAMBEGIN:
*list = next->next;
include_level++;
continue;
default:
dirty_stream(stream);
if (false_nesting) {
*list = next->next;
__free_token(next);
continue;
}
if (token_type(next) != TOKEN_IDENT ||
expand_one_symbol(list))
list = &next->next;
}
}
}
struct token * preprocess(struct token *token)
{
preprocessing = 1;
init_preprocessor();
do_preprocess(&token);
// Drop all expressions from preprocessing, they're not used any more.
// This is not true when we have multiple files, though ;/
// clear_expression_alloc();
preprocessing = 0;
return token;
}
static int is_VA_ARGS_token(struct token *token)
{
return (token_type(token) == TOKEN_IDENT) &&
(token->ident == &__VA_ARGS___ident);
}
static void dump_macro(struct symbol *sym)
{
int nargs = sym->arglist ? sym->arglist->count.normal : 0;
struct token *args[nargs];
struct token *token;
printf("#define %s", show_ident(sym->ident));
token = sym->arglist;
if (token) {
const char *sep = "";
int narg = 0;
putchar('(');
for (; !eof_token(token); token = token->next) {
if (token_type(token) == TOKEN_ARG_COUNT)
continue;
if (is_VA_ARGS_token(token))
printf("%s...", sep);
else
printf("%s%s", sep, show_token(token));
args[narg++] = token;
sep = ",";
}
putchar(')');
}
token = sym->expansion;
while (token_type(token) != TOKEN_UNTAINT) {
struct token *next = token->next;
if (token->pos.whitespace)
putchar(' ');
switch (token_type(token)) {
case TOKEN_CONCAT:
printf("##");
break;
case TOKEN_STR_ARGUMENT:
printf("#");
/* fall-through */
case TOKEN_QUOTED_ARGUMENT:
case TOKEN_MACRO_ARGUMENT:
token = args[token->argnum];
/* fall-through */
default:
printf("%s", show_token(token));
}
token = next;
}
putchar('\n');
}
void dump_macro_definitions(void)
{
struct ident *name;
FOR_EACH_PTR(macros, name) {
struct symbol *sym = lookup_macro(name);
if (sym)
dump_macro(sym);
} END_FOR_EACH_PTR(name);
}
sparse-0.6.4/predefine.c 0000664 0000000 0000000 00000022271 14115310122 0015115 0 ustar 00root root 0000000 0000000 // SPDX-License-Identifier: MIT
// Copyright (C) 2017-2020 Luc Van Oostenryck.
#include
#include "lib.h"
#include "machine.h"
#include "symbol.h"
#define PTYPE_SIZEOF (1U << 0)
#define PTYPE_T (1U << 1)
#define PTYPE_MAX (1U << 2)
#define PTYPE_MIN (1U << 3)
#define PTYPE_WIDTH (1U << 4)
#define PTYPE_TYPE (1U << 5)
#define PTYPE_ALL (PTYPE_MAX|PTYPE_SIZEOF|PTYPE_WIDTH)
#define PTYPE_ALL_T (PTYPE_MAX|PTYPE_SIZEOF|PTYPE_WIDTH|PTYPE_T)
static void predefined_sizeof(const char *name, const char *suffix, unsigned bits)
{
char buf[32];
snprintf(buf, sizeof(buf), "__SIZEOF_%s%s__", name, suffix);
predefine(buf, 1, "%d", bits/8);
}
static void predefined_width(const char *name, struct symbol *type)
{
char buf[32];
snprintf(buf, sizeof(buf), "__%s_WIDTH__", name);
predefine(buf, 1, "%d", type->bit_size);
}
static void predefined_max(const char *name, struct symbol *type)
{
const char *suffix = builtin_type_suffix(type);
unsigned bits = type->bit_size - is_signed_type(type);
unsigned long long max = bits_mask(bits);
char buf[32];
snprintf(buf, sizeof(buf), "__%s_MAX__", name);
predefine(buf, 1, "%#llx%s", max, suffix);
}
static void predefined_min(const char *name, struct symbol *type)
{
const char *suffix = builtin_type_suffix(type);
char buf[32];
snprintf(buf, sizeof(buf), "__%s_MIN__", name);
if (is_signed_type(type))
add_pre_buffer("#weak_define %s (-__%s_MAX__ - 1)\n", buf, name);
else
predefine(buf, 1, "0%s", suffix);
}
static void predefined_type(const char *name, struct symbol *type)
{
const char *typename = builtin_typename(type);
add_pre_buffer("#weak_define __%s_TYPE__ %s\n", name, typename);
}
static void predefined_ctype(const char *name, struct symbol *type, int flags)
{
unsigned bits = type->bit_size;
if (flags & PTYPE_SIZEOF) {
const char *suffix = (flags & PTYPE_T) ? "_T" : "";
predefined_sizeof(name, suffix, bits);
}
if (flags & PTYPE_MAX)
predefined_max(name, type);
if (flags & PTYPE_MIN)
predefined_min(name, type);
if (flags & PTYPE_TYPE)
predefined_type(name, type);
if (flags & PTYPE_WIDTH)
predefined_width(name, type);
}
void predefined_macros(void)
{
predefine("__CHECKER__", 0, "1");
predefine("__GNUC__", 1, "%d", gcc_major);
predefine("__GNUC_MINOR__", 1, "%d", gcc_minor);
predefine("__GNUC_PATCHLEVEL__", 1, "%d", gcc_patchlevel);
predefine("__STDC__", 1, "1");
predefine("__STDC_HOSTED__", 0, fhosted ? "1" : "0");
switch (standard) {
default:
break;
case STANDARD_C94:
predefine("__STDC_VERSION__", 1, "199409L");
break;
case STANDARD_C99:
case STANDARD_GNU99:
predefine("__STDC_VERSION__", 1, "199901L");
break;
case STANDARD_C11:
case STANDARD_GNU11:
predefine("__STDC_VERSION__", 1, "201112L");
break;
case STANDARD_C17:
case STANDARD_GNU17:
predefine("__STDC_VERSION__", 1, "201710L");
break;
}
if (!(standard & STANDARD_GNU) && (standard != STANDARD_NONE))
predefine("__STRICT_ANSI__", 1, "1");
if (standard >= STANDARD_C11) {
predefine("__STDC_NO_ATOMICS__", 1, "1");
predefine("__STDC_NO_COMPLEX__", 1, "1");
predefine("__STDC_NO_THREADS__", 1, "1");
}
predefine("__CHAR_BIT__", 1, "%d", bits_in_char);
if (funsigned_char)
predefine("__CHAR_UNSIGNED__", 1, "1");
predefined_ctype("SHORT", &short_ctype, PTYPE_SIZEOF);
predefined_ctype("SHRT", &short_ctype, PTYPE_MAX|PTYPE_WIDTH);
predefined_ctype("SCHAR", &schar_ctype, PTYPE_MAX|PTYPE_WIDTH);
predefined_ctype("WCHAR", wchar_ctype, PTYPE_ALL_T|PTYPE_MIN|PTYPE_TYPE);
predefined_ctype("WINT", wint_ctype, PTYPE_ALL_T|PTYPE_MIN|PTYPE_TYPE);
predefined_ctype("CHAR16", &ushort_ctype, PTYPE_TYPE);
predefined_ctype("CHAR32", uint32_ctype, PTYPE_TYPE);
predefined_ctype("INT", &int_ctype, PTYPE_ALL);
predefined_ctype("LONG", &long_ctype, PTYPE_ALL);
predefined_ctype("LONG_LONG", &llong_ctype, PTYPE_ALL);
predefined_ctype("INT8", &schar_ctype, PTYPE_MAX|PTYPE_TYPE);
predefined_ctype("UINT8", &uchar_ctype, PTYPE_MAX|PTYPE_TYPE);
predefined_ctype("INT16", &short_ctype, PTYPE_MAX|PTYPE_TYPE);
predefined_ctype("UINT16", &ushort_ctype, PTYPE_MAX|PTYPE_TYPE);
predefined_ctype("INT32", int32_ctype, PTYPE_MAX|PTYPE_TYPE);
predefined_ctype("UINT32", uint32_ctype, PTYPE_MAX|PTYPE_TYPE);
predefined_ctype("INT64", int64_ctype, PTYPE_MAX|PTYPE_TYPE);
predefined_ctype("UINT64", uint64_ctype, PTYPE_MAX|PTYPE_TYPE);
predefined_ctype("INT_LEAST8", &schar_ctype, PTYPE_MAX|PTYPE_TYPE|PTYPE_WIDTH);
predefined_ctype("UINT_LEAST8", &uchar_ctype, PTYPE_MAX|PTYPE_TYPE);
predefined_ctype("INT_LEAST16", &short_ctype, PTYPE_MAX|PTYPE_TYPE|PTYPE_WIDTH);
predefined_ctype("UINT_LEAST16",&ushort_ctype, PTYPE_MAX|PTYPE_TYPE);
predefined_ctype("INT_LEAST32", int32_ctype, PTYPE_MAX|PTYPE_TYPE|PTYPE_WIDTH);
predefined_ctype("UINT_LEAST32", uint32_ctype, PTYPE_MAX|PTYPE_TYPE);
predefined_ctype("INT_LEAST64", int64_ctype, PTYPE_MAX|PTYPE_TYPE|PTYPE_WIDTH);
predefined_ctype("UINT_LEAST64", uint64_ctype, PTYPE_MAX|PTYPE_TYPE);
predefined_ctype("INT_FAST8", fast8_ctype, PTYPE_MAX|PTYPE_TYPE|PTYPE_WIDTH);
predefined_ctype("UINT_FAST8", ufast8_ctype, PTYPE_MAX|PTYPE_TYPE);
predefined_ctype("INT_FAST16", fast16_ctype, PTYPE_MAX|PTYPE_TYPE|PTYPE_WIDTH);
predefined_ctype("UINT_FAST16",ufast16_ctype, PTYPE_MAX|PTYPE_TYPE);
predefined_ctype("INT_FAST32", fast32_ctype, PTYPE_MAX|PTYPE_TYPE|PTYPE_WIDTH);
predefined_ctype("UINT_FAST32",ufast32_ctype, PTYPE_MAX|PTYPE_TYPE);
predefined_ctype("INT_FAST64", fast64_ctype, PTYPE_MAX|PTYPE_TYPE|PTYPE_WIDTH);
predefined_ctype("UINT_FAST64",ufast64_ctype, PTYPE_MAX|PTYPE_TYPE);
predefined_ctype("INTMAX", intmax_ctype, PTYPE_MAX|PTYPE_TYPE|PTYPE_WIDTH);
predefined_ctype("UINTMAX", uintmax_ctype, PTYPE_MAX|PTYPE_TYPE);
predefined_ctype("INTPTR", intptr_ctype, PTYPE_MAX|PTYPE_TYPE|PTYPE_WIDTH);
predefined_ctype("UINTPTR", uintptr_ctype, PTYPE_MAX|PTYPE_TYPE);
predefined_ctype("PTRDIFF", ptrdiff_ctype, PTYPE_ALL_T|PTYPE_TYPE);
predefined_ctype("SIZE", size_t_ctype, PTYPE_ALL_T|PTYPE_TYPE);
predefined_ctype("POINTER", &ptr_ctype, PTYPE_SIZEOF);
predefined_ctype("SIG_ATOMIC", sig_atomic_ctype, PTYPE_MAX|PTYPE_MIN|PTYPE_TYPE|PTYPE_WIDTH);
predefined_sizeof("FLOAT", "", bits_in_float);
predefined_sizeof("DOUBLE", "", bits_in_double);
predefined_sizeof("LONG_DOUBLE", "", bits_in_longdouble);
if (arch_target->has_int128)
predefined_sizeof("INT128", "", 128);
predefine("__ATOMIC_RELAXED", 0, "0");
predefine("__ATOMIC_CONSUME", 0, "1");
predefine("__ATOMIC_ACQUIRE", 0, "3");
predefine("__ATOMIC_RELEASE", 0, "4");
predefine("__ATOMIC_ACQ_REL", 0, "7");
predefine("__ATOMIC_SEQ_CST", 0, "8");
predefine("__ORDER_LITTLE_ENDIAN__", 1, "1234");
predefine("__ORDER_BIG_ENDIAN__", 1, "4321");
predefine("__ORDER_PDP_ENDIAN__", 1, "3412");
if (arch_big_endian) {
predefine("__BIG_ENDIAN__", 1, "1");
predefine("__BYTE_ORDER__", 1, "__ORDER_BIG_ENDIAN__");
} else {
predefine("__LITTLE_ENDIAN__", 1, "1");
predefine("__BYTE_ORDER__", 1, "__ORDER_LITTLE_ENDIAN__");
}
if (optimize_level)
predefine("__OPTIMIZE__", 0, "1");
if (optimize_size)
predefine("__OPTIMIZE_SIZE__", 0, "1");
predefine("__PRAGMA_REDEFINE_EXTNAME", 1, "1");
// Temporary hacks
predefine("__extension__", 0, NULL);
predefine("__pragma__", 0, NULL);
switch (arch_m64) {
case ARCH_LP32:
break;
case ARCH_X32:
predefine("__ILP32__", 1, "1");
predefine("_ILP32", 1, "1");
break;
case ARCH_LP64:
predefine("__LP64__", 1, "1");
predefine("_LP64", 1, "1");
break;
case ARCH_LLP64:
predefine("__LLP64__", 1, "1");
break;
}
if (fpic) {
predefine("__pic__", 0, "%d", fpic);
predefine("__PIC__", 0, "%d", fpic);
}
if (fpie) {
predefine("__pie__", 0, "%d", fpie);
predefine("__PIE__", 0, "%d", fpie);
}
if (arch_target->predefine)
arch_target->predefine(arch_target);
if (arch_os >= OS_UNIX && arch_os != OS_DARWIN) {
predefine("__unix__", 1, "1");
predefine("__unix", 1, "1");
predefine_nostd("unix");
}
switch (arch_os) {
case OS_CYGWIN:
predefine("__CYGWIN__", 1, "1");
if (arch_m64 == ARCH_LP32)
predefine("__CYGWIN32__", 1, "1");
add_pre_buffer("#define __cdecl __attribute__((__cdecl__))\n");
add_pre_buffer("#define __declspec(x) __attribute__((x))\n");
add_pre_buffer("#define __fastcall __attribute__((__fastcall__))\n");
add_pre_buffer("#define __stdcall __attribute__((__stdcall__))\n");
add_pre_buffer("#define __thiscall __attribute__((__thiscall__))\n");
add_pre_buffer("#define _cdecl __attribute__((__cdecl__))\n");
add_pre_buffer("#define _fastcall __attribute__((__fastcall__))\n");
add_pre_buffer("#define _stdcall __attribute__((__stdcall__))\n");
add_pre_buffer("#define _thiscall __attribute__((__thiscall__))\n");
break;
case OS_DARWIN:
predefine("__APPLE__", 1, "1");
predefine("__APPLE_CC__", 1, "1");
predefine("__MACH__", 1, "1");
break;
case OS_FREEBSD:
predefine("__FreeBSD__", 1, "1");
break;
case OS_LINUX:
predefine("__linux__", 1, "1");
predefine("__linux", 1, "1");
break;
case OS_NETBSD:
predefine("__NetBSD__", 1, "1");
break;
case OS_OPENBSD:
predefine("__OpenBSD__", 1, "1");
break;
case OS_SUNOS:
predefine("__sun__", 1, "1");
predefine("__sun", 1, "1");
predefine_nostd("sun");
predefine("__svr4__", 1, "1");
break;
}
}
sparse-0.6.4/ptrlist.c 0000664 0000000 0000000 00000026244 14115310122 0014661 0 ustar 00root root 0000000 0000000 /*
* ptrlist.c
*
* (C) Copyright Linus Torvalds 2003-2005
*/
///
// Pointer list manipulation
// -------------------------
//
// The data structure handled here is designed to hold pointers
// but two special cases need to be avoided or need special care:
// * NULL is used by {PREPARE,NEXT}_PTR_LIST() to indicate the end-of-list.
// Thus, NULL can't be stored in lists using this API but is fine to
// use with FOR_EACH_PTR() and its variants.
// * VOID is used to replace a removed pseudo 'usage'. Since phi-nodes
// (OP_PHI) use a list to store their operands, a VOID in a phi-node
// list must be ignored since it represents a removed operand. As
// consequence, VOIDs must never be used as phi-node operand.
// This is fine since phi-nodes make no sense with void values
// but VOID is also used for invalid types and in case of errors.
#include
#include
#include
#include "ptrlist.h"
#include "allocate.h"
#include "compat.h"
__DECLARE_ALLOCATOR(struct ptr_list, ptrlist);
__ALLOCATOR(struct ptr_list, "ptr list", ptrlist);
///
// get the size of a ptrlist
// @head: the head of the list
// @return: the size of the list given by @head.
int ptr_list_size(struct ptr_list *head)
{
int nr = 0;
if (head) {
struct ptr_list *list = head;
do {
nr += list->nr - list->rm;
} while ((list = list->next) != head);
}
return nr;
}
///
// test if a list is empty
// @head: the head of the list
// @return: ``true`` if the list is empty, ``false`` otherwise.
bool ptr_list_empty(const struct ptr_list *head)
{
const struct ptr_list *list = head;
if (!head)
return true;
do {
if (list->nr - list->rm)
return false;
} while ((list = list->next) != head);
return true;
}
///
// test is a list contains more than one element
// @head: the head of the list
// @return: ``true`` if the list has more than 1 element, ``false`` otherwise.
bool ptr_list_multiple(const struct ptr_list *head)
{
const struct ptr_list *list = head;
int nr = 0;
if (!head)
return false;
do {
nr += list->nr - list->rm;
if (nr > 1)
return true;
} while ((list = list->next) != head);
return false;
}
///
// get the first element of a ptrlist
// @head: the head of the list
// @return: the first element of the list or ``NULL`` if the list is empty
void *first_ptr_list(struct ptr_list *head)
{
struct ptr_list *list = head;
if (!head)
return NULL;
while (list->nr == 0) {
list = list->next;
if (list == head)
return NULL;
}
return PTR_ENTRY_NOTAG(list, 0);
}
///
// get the last element of a ptrlist
// @head: the head of the list
// @return: the last element of the list or ``NULL`` if the list is empty
void *last_ptr_list(struct ptr_list *head)
{
struct ptr_list *list;
if (!head)
return NULL;
list = head->prev;
while (list->nr == 0) {
if (list == head)
return NULL;
list = list->prev;
}
return PTR_ENTRY_NOTAG(list, list->nr-1);
}
///
// get the nth element of a ptrlist
// @head: the head of the list
// @return: the nth element of the list or ``NULL`` if the list is too short.
void *ptr_list_nth_entry(struct ptr_list *list, unsigned int idx)
{
struct ptr_list *head = list;
if (!head)
return NULL;
do {
unsigned int nr = list->nr;
if (idx < nr)
return list->list[idx];
else
idx -= nr;
} while ((list = list->next) != head);
return NULL;
}
///
// linearize the entries of a list
//
// @head: the list to be linearized
// @arr: a ``void*`` array to fill with @head's entries
// @max: the maximum number of entries to store into @arr
// @return: the number of entries in the list.
//
// Linearize the entries of a list up to a total of @max,
// and return the number of entries in the list.
//
// The array to linearize into (@arr) should really
// be ``void *x[]``, but we want to let people fill in any kind
// of pointer array, so let's just call it ``void **``.
int linearize_ptr_list(struct ptr_list *head, void **arr, int max)
{
int nr = 0;
if (head && max > 0) {
struct ptr_list *list = head;
do {
int i = list->nr;
nr += i;
if (max == 0)
continue;
if (i > max)
i = max;
memcpy(arr, list->list, i*sizeof(void *));
arr += i;
max -= i;
} while ((list = list->next) != head);
}
return nr;
}
///
// pack a ptrlist
//
// @listp: a pointer to the list to be packed.
//
// When we've walked the list and deleted entries,
// we may need to re-pack it so that we don't have
// any empty blocks left (empty blocks upset the
// walking code).
void pack_ptr_list(struct ptr_list **listp)
{
struct ptr_list *head = *listp;
if (head) {
struct ptr_list *entry = head;
do {
struct ptr_list *next;
restart:
next = entry->next;
if (!entry->nr) {
struct ptr_list *prev;
if (next == entry) {
__free_ptrlist(entry);
*listp = NULL;
return;
}
prev = entry->prev;
prev->next = next;
next->prev = prev;
__free_ptrlist(entry);
if (entry == head) {
*listp = next;
head = next;
entry = next;
goto restart;
}
}
entry = next;
} while (entry != head);
}
}
///
// split a ptrlist block
// @head: the ptrlist block to be split
//
// A new block is inserted just after @head and the entries
// at the half end of @head are moved to this new block.
// The goal being to create space inside @head for a new entry.
void split_ptr_list_head(struct ptr_list *head)
{
int old = head->nr, nr = old / 2;
struct ptr_list *newlist = __alloc_ptrlist(0);
struct ptr_list *next = head->next;
old -= nr;
head->nr = old;
newlist->next = next;
next->prev = newlist;
newlist->prev = head;
head->next = newlist;
newlist->nr = nr;
memcpy(newlist->list, head->list + old, nr * sizeof(void *));
memset(head->list + old, 0xf0, nr * sizeof(void *));
}
///
// add an entry to a ptrlist
// @listp: a pointer to the list
// @ptr: the entry to add to the list
// @return: the address where the new entry is stored.
//
// :note: code must not use this function and should use
// :func:`add_ptr_list` instead.
void **__add_ptr_list(struct ptr_list **listp, void *ptr)
{
struct ptr_list *list = *listp;
struct ptr_list *last = NULL; /* gcc complains needlessly */
void **ret;
int nr;
if (!list || (nr = (last = list->prev)->nr) >= LIST_NODE_NR) {
struct ptr_list *newlist = __alloc_ptrlist(0);
if (!list) {
newlist->next = newlist;
newlist->prev = newlist;
*listp = newlist;
} else {
newlist->prev = last;
newlist->next = list;
list->prev = newlist;
last->next = newlist;
}
last = newlist;
nr = 0;
}
ret = last->list + nr;
*ret = ptr;
nr++;
last->nr = nr;
return ret;
}
///
// add a tagged entry to a ptrlist
// @listp: a pointer to the list
// @ptr: the entry to add to the list
// @tag: the tag to add to @ptr
// @return: the address where the new entry is stored.
//
// :note: code must not use this function and should use
// :func:`add_ptr_list_tag` instead.
void **__add_ptr_list_tag(struct ptr_list **listp, void *ptr, unsigned long tag)
{
/* The low two bits are reserved for tags */
assert((3 & (unsigned long)ptr) == 0);
assert((~3 & tag) == 0);
ptr = (void *)(tag | (unsigned long)ptr);
return __add_ptr_list(listp, ptr);
}
///
// test if some entry is already present in a ptrlist
// @list: the head of the list
// @entry: the entry to test
// @return: ``true`` if the entry is already present, ``false`` otherwise.
bool lookup_ptr_list_entry(const struct ptr_list *head, const void *entry)
{
const struct ptr_list *list = head;
if (!head)
return false;
do {
int nr = list->nr;
int i;
for (i = 0; i < nr; i++)
if (list->list[i] == entry)
return true;
} while ((list = list->next) != head);
return false;
}
///
// delete an entry from a ptrlist
// @list: a pointer to the list
// @entry: the item to be deleted
// @count: the minimum number of times @entry should be deleted or 0.
int delete_ptr_list_entry(struct ptr_list **list, void *entry, int count)
{
void *ptr;
FOR_EACH_PTR(*list, ptr) {
if (ptr == entry) {
DELETE_CURRENT_PTR(ptr);
if (!--count)
goto out;
}
} END_FOR_EACH_PTR(ptr);
assert(count <= 0);
out:
pack_ptr_list(list);
return count;
}
///
// replace an entry in a ptrlist
// @list: a pointer to the list
// @old_ptr: the entry to be replaced
// @new_ptr: the new entry
// @count: the minimum number of times @entry should be deleted or 0.
int replace_ptr_list_entry(struct ptr_list **list, void *old_ptr,
void *new_ptr, int count)
{
void *ptr;
FOR_EACH_PTR(*list, ptr) {
if (ptr==old_ptr) {
REPLACE_CURRENT_PTR(ptr, new_ptr);
if (!--count)
goto out;
}
}END_FOR_EACH_PTR(ptr);
assert(count <= 0);
out:
return count;
}
///
// remove the last entry of a ptrlist
// @head: a pointer to the list
// @return: the last element of the list or NULL if the list is empty.
//
// :note: this doesn't repack the list
void * undo_ptr_list_last(struct ptr_list **head)
{
struct ptr_list *last, *first = *head;
if (!first)
return NULL;
last = first;
do {
last = last->prev;
if (last->nr) {
void *ptr;
int nr = --last->nr;
ptr = last->list[nr];
last->list[nr] = (void *)0xf1f1f1f1;
return ptr;
}
} while (last != first);
return NULL;
}
///
// remove the last entry and repack the list
// @head: a pointer to the list
// @return: the last element of the list or NULL if the list is empty.
void * delete_ptr_list_last(struct ptr_list **head)
{
void *ptr = NULL;
struct ptr_list *last, *first = *head;
if (!first)
return NULL;
last = first->prev;
if (last->nr)
ptr = last->list[--last->nr];
if (last->nr <=0) {
first->prev = last->prev;
last->prev->next = first;
if (last == first)
*head = NULL;
__free_ptrlist(last);
}
return ptr;
}
///
// concat two ptrlists
// @a: the source list
// @b: a pointer to the destination list.
// The element of @a are added at the end of @b.
void concat_ptr_list(struct ptr_list *a, struct ptr_list **b)
{
void *entry;
FOR_EACH_PTR(a, entry) {
add_ptr_list(b, entry);
} END_FOR_EACH_PTR(entry);
}
///
// copy the elements of a list at the end of another list.
// @listp: a pointer to the destination list.
// @src: the head of the source list.
void copy_ptr_list(struct ptr_list **listp, struct ptr_list *src)
{
struct ptr_list *head, *tail;
struct ptr_list *cur = src;
int idx;
if (!src)
return;
head = *listp;
if (!head) {
*listp = src;
return;
}
tail = head->prev;
idx = tail->nr;
do {
struct ptr_list *next;
int nr = cur->nr;
int i;
for (i = 0; i < nr;) {
void *ptr = cur->list[i++];
if (!ptr)
continue;
if (idx >= LIST_NODE_NR) {
struct ptr_list *prev = tail;
tail = __alloc_ptrlist(0);
prev->next = tail;
tail->prev = prev;
prev->nr = idx;
idx = 0;
}
tail->list[idx++] = ptr;
}
next = cur->next;
__free_ptrlist(cur);
cur = next;
} while (cur != src);
tail->nr = idx;
head->prev = tail;
tail->next = head;
}
///
// free a ptrlist
// @listp: a pointer to the list
// Each blocks of the list are freed (but the entries
// themselves are not freed).
//
// :note: code must not use this function and should use
// the macro :func:`free_ptr_list` instead.
void __free_ptr_list(struct ptr_list **listp)
{
struct ptr_list *tmp, *list = *listp;
if (!list)
return;
list->prev->next = NULL;
while (list) {
tmp = list;
list = list->next;
__free_ptrlist(tmp);
}
*listp = NULL;
}
sparse-0.6.4/ptrlist.h 0000664 0000000 0000000 00000023030 14115310122 0014654 0 ustar 00root root 0000000 0000000 #ifndef PTR_LIST_H
#define PTR_LIST_H
#include
#include
/*
* Generic pointer list manipulation code.
*
* (C) Copyright Linus Torvalds 2003-2005
*/
/* Silly type-safety check ;) */
#define CHECK_TYPE(head,ptr) (void)(&(ptr) == &(head)->list[0])
#define PTRLIST_TYPE(head) __typeof__((head)->list[0])
#define VRFY_PTR_LIST(head) (void)(sizeof((head)->list[0]))
#define LIST_NODE_NR (13)
#define DECLARE_PTR_LIST(listname, type) \
struct listname { \
int nr:8; \
int rm:8; \
struct listname *prev; \
struct listname *next; \
type *list[LIST_NODE_NR]; \
}
DECLARE_PTR_LIST(ptr_list, void);
void * undo_ptr_list_last(struct ptr_list **head);
void * delete_ptr_list_last(struct ptr_list **head);
int delete_ptr_list_entry(struct ptr_list **, void *, int);
int replace_ptr_list_entry(struct ptr_list **, void *old, void *new, int);
bool lookup_ptr_list_entry(const struct ptr_list *head, const void *entry);
extern void sort_list(struct ptr_list **, int (*)(const void *, const void *));
extern void concat_ptr_list(struct ptr_list *a, struct ptr_list **b);
extern void copy_ptr_list(struct ptr_list **h, struct ptr_list *t);
extern int ptr_list_size(struct ptr_list *);
extern bool ptr_list_empty(const struct ptr_list *head);
extern bool ptr_list_multiple(const struct ptr_list *head);
extern int linearize_ptr_list(struct ptr_list *, void **, int);
extern void *first_ptr_list(struct ptr_list *);
extern void *last_ptr_list(struct ptr_list *);
extern void *ptr_list_nth_entry(struct ptr_list *, unsigned int idx);
extern void pack_ptr_list(struct ptr_list **);
/*
* Hey, who said that you can't do overloading in C?
*
* You just have to be creative, and use some gcc
* extensions..
*/
extern void **__add_ptr_list(struct ptr_list **, void *);
extern void **__add_ptr_list_tag(struct ptr_list **, void *, unsigned long);
#define add_ptr_list(list, ptr) ({ \
struct ptr_list** head = (struct ptr_list**)(list); \
CHECK_TYPE(*(list),ptr); \
(__typeof__(&(ptr))) __add_ptr_list(head, ptr); \
})
#define add_ptr_list_tag(list, ptr, tag) ({ \
struct ptr_list** head = (struct ptr_list**)(list); \
CHECK_TYPE(*(list),ptr); \
(__typeof__(&(ptr))) __add_ptr_list_tag(head, ptr, tag);\
})
#define pop_ptr_list(l) ({ \
PTRLIST_TYPE(*(l)) ptr; \
ptr = delete_ptr_list_last((struct ptr_list**)(l)); \
ptr; \
})
extern void __free_ptr_list(struct ptr_list **);
#define free_ptr_list(list) do { \
VRFY_PTR_LIST(*(list)); \
__free_ptr_list((struct ptr_list **)(list)); \
} while (0)
#define ptr_list_nth(lst, nth) ({ \
struct ptr_list* head = (struct ptr_list*)(lst); \
(PTRLIST_TYPE(lst)) ptr_list_nth_entry(head, nth);\
})
#define ptr_list_to_array(list, array, size) ({ \
struct ptr_list* head = (struct ptr_list*)(list); \
CHECK_TYPE(list, *array); \
linearize_ptr_list(head, (void**)array, size); \
})
////////////////////////////////////////////////////////////////////////
// API
#define PREPARE_PTR_LIST(head, ptr) \
DO_PREPARE(head, ptr, __head##ptr, __list##ptr, __nr##ptr, PTR_ENTRY_UNTAG)
#define NEXT_PTR_LIST(ptr) \
DO_NEXT(ptr, __head##ptr, __list##ptr, __nr##ptr, PTR_ENTRY_UNTAG)
#define RESET_PTR_LIST(ptr) \
DO_RESET(ptr, __head##ptr, __list##ptr, __nr##ptr, PTR_ENTRY_UNTAG)
#define FINISH_PTR_LIST(ptr) \
DO_FINISH(ptr, __head##ptr, __list##ptr, __nr##ptr)
#define RECURSE_PTR_REVERSE(ptr, new) \
DO_REVERSE(ptr, __head##ptr, __list##ptr, __nr##ptr, __rname##new, \
new, __head##new, __list##new, __nr##new, PTR_ENTRY_UNTAG)
#define FOR_EACH_PTR(head, ptr) \
DO_FOR_EACH(head, ptr, __head##ptr, __list##ptr, __nr##ptr, __name##ptr, PTR_ENTRY_NOTAG)
#define FOR_EACH_PTR_TAG(head, ptr) \
DO_FOR_EACH(head, ptr, __head##ptr, __list##ptr, __nr##ptr, __name##ptr, PTR_ENTRY_UNTAG)
#define END_FOR_EACH_PTR(ptr) \
DO_END_FOR_EACH(ptr, __head##ptr, __list##ptr, __nr##ptr, __name##ptr)
#define FOR_EACH_PTR_REVERSE(head, ptr) \
DO_FOR_EACH_REVERSE(head, ptr, __head##ptr, __list##ptr, __nr##ptr, __rname##ptr, PTR_ENTRY_NOTAG)
#define FOR_EACH_PTR_REVERSE_TAG(head, ptr) \
DO_FOR_EACH_REVERSE(head, ptr, __head##ptr, __list##ptr, __nr##ptr, __rname##ptr, PTR_ENTRY_UNTAG)
#define END_FOR_EACH_PTR_REVERSE(ptr) \
DO_END_FOR_EACH_REVERSE(ptr, __head##ptr, __list##ptr, __nr##ptr, __rname##ptr)
#define THIS_ADDRESS(ptr) \
DO_THIS_ADDRESS(ptr, __head##ptr, __list##ptr, __nr##ptr)
#define INSERT_CURRENT(new, ptr) \
DO_INSERT_CURRENT(new, __head##ptr, __list##ptr, __nr##ptr)
#define DELETE_CURRENT_PTR(ptr) \
DO_DELETE_CURRENT(__head##ptr, __list##ptr, __nr##ptr)
#define REPLACE_CURRENT_PTR(ptr, new_ptr) \
do { *THIS_ADDRESS(ptr) = (new_ptr); } while (0)
// This replace the current element by a null-pointer.
// It's used when an element of the list must be removed
// but the address of the other elements must not be changed.
#define MARK_CURRENT_DELETED(ptr) \
DO_MARK_CURRENT_DELETED(ptr, __list##ptr)
#define PACK_PTR_LIST(x) \
pack_ptr_list((struct ptr_list **)(x))
#define CURRENT_TAG(ptr) (3 & (unsigned long)*THIS_ADDRESS(ptr))
#define TAG_CURRENT(ptr,val) update_tag(THIS_ADDRESS(ptr),val)
// backward compatibility for smatch
#define FOR_EACH_PTR_NOTAG(list, ptr) FOR_EACH_PTR(list, ptr)
#define END_FOR_EACH_PTR_NOTAG(ptr) END_FOR_EACH_PTR(ptr)
////////////////////////////////////////////////////////////////////////
// Implementation
#define PTR_UNTAG(p) ((void*)(~3UL & (unsigned long)(p)))
#define PTR_ENTRY_NOTAG(h,i) ((h)->list[i])
#define PTR_ENTRY_UNTAG(h,i) PTR_UNTAG((h)->list[i])
#define PTR_NEXT(ptr, __head, __list, __nr, PTR_ENTRY) \
do { \
if (__nr < __list->nr) { \
ptr = PTR_ENTRY(__list,__nr); \
__nr++; \
break; \
} \
ptr = NULL; \
__nr = 0; \
} while ((__list = __list->next) != __head) \
#define DO_PREPARE(head, ptr, __head, __list, __nr, PTR_ENTRY) \
do { \
__typeof__(head) __head = (head); \
__typeof__(head) __list = __head; \
int __nr = 0; \
ptr = NULL; \
if (__head) { \
PTR_NEXT(ptr, __head, __list, __nr, PTR_ENTRY); \
} \
#define DO_NEXT(ptr, __head, __list, __nr, PTR_ENTRY) \
if (ptr) { \
PTR_NEXT(ptr, __head, __list, __nr, PTR_ENTRY); \
}
#define DO_RESET(ptr, __head, __list, __nr, PTR_ENTRY) \
do { \
__nr = 0; \
__list = __head; \
if (__head) \
PTR_NEXT(ptr, __head, __list, __nr, PTR_ENTRY); \
} while (0)
#define DO_FINISH(ptr, __head, __list, __nr) \
VRFY_PTR_LIST(__head); /* Sanity-check nesting */ \
} while (0)
#define DO_FOR_EACH(head, ptr, __head, __list, __nr, __name, PTR_ENTRY) do { \
__typeof__(head) __head = (head); \
__typeof__(head) __list = __head; \
__typeof__(head) __name = __head; \
int __nr; \
if (!__head) \
break; \
do { \
for (__nr = 0; __nr < __list->nr; __nr++) { \
ptr = PTR_ENTRY(__list,__nr); \
if (__list->rm && !ptr) \
continue; \
#define DO_END_FOR_EACH(ptr, __head, __list, __nr, __name) \
} \
} while ((__list = __list->next) != __head); \
(void) __name; \
} while (0)
#define DO_FOR_EACH_REVERSE(head, ptr, __head, __list, __nr, __name, PTR_ENTRY) do { \
__typeof__(head) __head = (head); \
__typeof__(head) __list = __head; \
__typeof__(head) __name = __head; \
int __nr; \
if (!head) \
break; \
do { \
__list = __list->prev; \
__nr = __list->nr; \
while (--__nr >= 0) { \
ptr = PTR_ENTRY(__list,__nr); \
if (__list->rm && !ptr) \
continue; \
#define DO_END_FOR_EACH_REVERSE(ptr, __head, __list, __nr, __name) \
} \
} while (__list != __head); \
(void) __name; \
} while (0)
#define DO_REVERSE(ptr, __head, __list, __nr, __name, new, __newhead, \
__newlist, __newnr, PTR_ENTRY) do { \
__typeof__(__head) __newhead = __head; \
__typeof__(__head) __newlist = __list; \
__typeof__(__head) __name = __list; \
int __newnr = __nr; \
new = ptr; \
goto __inside##new; \
do { \
__newlist = __newlist->prev; \
__newnr = __newlist->nr; \
__inside##new: \
while (--__newnr >= 0) { \
new = PTR_ENTRY(__newlist,__newnr); \
#define DO_THIS_ADDRESS(ptr, __head, __list, __nr) \
(&__list->list[__nr])
extern void split_ptr_list_head(struct ptr_list *);
#define DO_INSERT_CURRENT(new, __head, __list, __nr) do { \
PTRLIST_TYPE(__head) *__this, *__last; \
if (__list->nr == LIST_NODE_NR) { \
split_ptr_list_head((struct ptr_list*)__list); \
if (__nr >= __list->nr) { \
__nr -= __list->nr; \
__list = __list->next; \
} \
} \
__this = __list->list + __nr; \
__last = __list->list + __list->nr - 1; \
while (__last >= __this) { \
__last[1] = __last[0]; \
__last--; \
} \
*__this = (new); \
__list->nr++; \
} while (0)
#define DO_DELETE_CURRENT(__head, __list, __nr) do { \
PTRLIST_TYPE(__head) *__this = __list->list + __nr; \
PTRLIST_TYPE(__head) *__last = __list->list + __list->nr - 1; \
while (__this < __last) { \
__this[0] = __this[1]; \
__this++; \
} \
*__this = (void *)0xf0f0f0f0; \
__list->nr--; __nr--; \
} while (0)
#define DO_MARK_CURRENT_DELETED(ptr, __list) do { \
REPLACE_CURRENT_PTR(ptr, NULL); \
__list->rm++; \
} while (0)
static inline void update_tag(void *p, unsigned long tag)
{
unsigned long *ptr = p;
*ptr = tag | (~3UL & *ptr);
}
static inline void *tag_ptr(void *ptr, unsigned long tag)
{
return (void *)(tag | (unsigned long)ptr);
}
#endif /* PTR_LIST_H */
sparse-0.6.4/ptrmap.c 0000664 0000000 0000000 00000005150 14115310122 0014454 0 ustar 00root root 0000000 0000000 // SPDX-License-Identifier: MIT
/*
* Stupid implementation of pointer -> pointer map.
*
* Copyright (c) 2017 Luc Van Oostenryck.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "ptrmap.h"
#include "allocate.h"
#include
#define MAP_NR 7
struct ptrpair {
void *key;
void *val;
};
struct ptrmap {
struct ptrmap *next;
int nr;
struct ptrpair pairs[MAP_NR];
};
DECLARE_ALLOCATOR(ptrmap);
ALLOCATOR(ptrmap, "ptrmap");
void __ptrmap_add(struct ptrmap **mapp, void *key, void *val)
{
struct ptrmap *head = *mapp;
struct ptrmap *newmap;
struct ptrmap *map;
struct ptrpair *pair;
int nr;
if ((map = head)) {
struct ptrmap *next = map->next;
if (next) // head is full
map = next;
if ((nr = map->nr) < MAP_NR)
goto oldmap;
}
// need a new block
newmap = __alloc_ptrmap(0);
if (!head) {
*mapp = newmap;
} else {
newmap->next = head->next;
head->next = newmap;
}
map = newmap;
nr = 0;
oldmap:
pair = &map->pairs[nr];
pair->key = key;
pair->val = val;
map->nr = ++nr;
}
void *__ptrmap_lookup(struct ptrmap *map, void *key)
{
for (; map; map = map->next) {
int i, n = map->nr;
for (i = 0; i < n; i++) {
struct ptrpair *pair = &map->pairs[i];
if (pair->key == key)
return pair->val;
}
}
return NULL;
}
void __ptrmap_update(struct ptrmap **mapp, void *key, void *val)
{
struct ptrmap *map = *mapp;
for (; map; map = map->next) {
int i, n = map->nr;
for (i = 0; i < n; i++) {
struct ptrpair *pair = &map->pairs[i];
if (pair->key == key) {
if (pair->val != val)
pair->val = val;
return;
}
}
}
__ptrmap_add(mapp, key, val);
}
sparse-0.6.4/ptrmap.h 0000664 0000000 0000000 00000001541 14115310122 0014461 0 ustar 00root root 0000000 0000000 #ifndef PTRMAP_H
#define PTRMAP_H
struct ptrmap;
#define DECLARE_PTRMAP(name, ktype, vtype) \
struct name ## _pair { ktype key; vtype val; }; \
struct name { struct name ## _pair block[1]; }; \
static inline \
void name##_add(struct name **map, ktype k, vtype v) { \
__ptrmap_add((struct ptrmap**)map, k, v); \
} \
static inline \
void name##_update(struct name **map, ktype k, vtype v) { \
__ptrmap_update((struct ptrmap**)map, k, v); \
} \
static inline \
vtype name##_lookup(struct name *map, ktype k) { \
vtype val = __ptrmap_lookup((struct ptrmap*)map, k); \
return val; \
} \
/* ptrmap.c */
void __ptrmap_add(struct ptrmap **mapp, void *key, void *val);
void __ptrmap_update(struct ptrmap **mapp, void *key, void *val);
void *__ptrmap_lookup(struct ptrmap *map, void *key);
#endif
sparse-0.6.4/scheck.c 0000664 0000000 0000000 00000024611 14115310122 0014414 0 ustar 00root root 0000000 0000000 // SPDX-License-Identifier: MIT
// Copyright (C) 2021 Luc Van Oostenryck
///
// Symbolic checker for Sparse's IR
// --------------------------------
//
// This is an example client program with a dual purpose:
// # It shows how to translate Sparse's IR into the language
// of SMT solvers (only the part concerning integers,
// floating-point and memory is ignored).
// # It's used as a simple symbolic checker for the IR.
// The idea is to create a mini-language that allows to
// express some assertions with some pre-conditions.
#include
#include
#include
#include
#include
#include
#include
#include
#include "lib.h"
#include "expression.h"
#include "linearize.h"
#include "symbol.h"
#include "builtin.h"
#define dyntype incomplete_ctype
static const struct builtin_fn builtins_scheck[] = {
{ "__assume", &void_ctype, 0, { &dyntype }, .op = &generic_int_op },
{ "__assert", &void_ctype, 0, { &bool_ctype }, .op = &generic_int_op },
{ "__assert_eq", &void_ctype, 0, { &dyntype, &dyntype }, .op = &generic_int_op },
{ "__assert_const", &void_ctype, 0, { &dyntype, &dyntype }, .op = &generic_int_op },
{}
};
static BoolectorSort get_sort(Btor *btor, struct symbol *type, struct position pos)
{
if (!is_int_type(type)) {
sparse_error(pos, "invalid type");
return NULL;
}
return boolector_bitvec_sort(btor, type->bit_size);
}
static BoolectorNode *mkvar(Btor *btor, BoolectorSort s, pseudo_t pseudo)
{
static char buff[33];
BoolectorNode *n;
switch (pseudo->type) {
case PSEUDO_VAL:
sprintf(buff, "%llx", pseudo->value);
return boolector_consth(btor, s, buff);
case PSEUDO_ARG:
case PSEUDO_REG:
if (pseudo->priv)
return pseudo->priv;
n = boolector_var(btor, s, show_pseudo(pseudo));
break;
default:
fprintf(stderr, "invalid pseudo: %s\n", show_pseudo(pseudo));
return NULL;
}
return pseudo->priv = n;
}
static BoolectorNode *mktvar(Btor *btor, struct instruction *insn, pseudo_t src)
{
BoolectorSort s = get_sort(btor, insn->type, insn->pos);
return mkvar(btor, s, src);
}
static BoolectorNode *mkivar(Btor *btor, struct instruction *insn, pseudo_t src)
{
BoolectorSort s = get_sort(btor, insn->itype, insn->pos);
return mkvar(btor, s, src);
}
static BoolectorNode *get_arg(Btor *btor, struct instruction *insn, int idx)
{
pseudo_t arg = ptr_list_nth(insn->arguments, idx);
struct symbol *type = ptr_list_nth(insn->fntypes, idx + 1);
BoolectorSort s = get_sort(btor, type, insn->pos);
return mkvar(btor, s, arg);
}
static BoolectorNode *zext(Btor *btor, struct instruction *insn, BoolectorNode *s)
{
int old = boolector_get_width(btor, s);
int new = insn->type->bit_size;
return boolector_uext(btor, s, new - old);
}
static BoolectorNode *sext(Btor *btor, struct instruction *insn, BoolectorNode *s)
{
int old = boolector_get_width(btor, s);
int new = insn->type->bit_size;
return boolector_sext(btor, s, new - old);
}
static BoolectorNode *slice(Btor *btor, struct instruction *insn, BoolectorNode *s)
{
int old = boolector_get_width(btor, s);
int new = insn->type->bit_size;
return boolector_slice(btor, s, old - new - 1, 0);
}
static void binary(Btor *btor, BoolectorSort s, struct instruction *insn)
{
BoolectorNode *t, *a, *b;
a = mkvar(btor, s, insn->src1);
b = mkvar(btor, s, insn->src2);
if (!a || !b)
return;
switch (insn->opcode) {
case OP_ADD: t = boolector_add(btor, a, b); break;
case OP_SUB: t = boolector_sub(btor, a, b); break;
case OP_MUL: t = boolector_mul(btor, a, b); break;
case OP_AND: t = boolector_and(btor, a, b); break;
case OP_OR: t = boolector_or (btor, a, b); break;
case OP_XOR: t = boolector_xor(btor, a, b); break;
case OP_SHL: t = boolector_sll(btor, a, b); break;
case OP_LSR: t = boolector_srl(btor, a, b); break;
case OP_ASR: t = boolector_sra(btor, a, b); break;
case OP_DIVS: t = boolector_sdiv(btor, a, b); break;
case OP_DIVU: t = boolector_udiv(btor, a, b); break;
case OP_MODS: t = boolector_srem(btor, a, b); break;
case OP_MODU: t = boolector_urem(btor, a, b); break;
case OP_SET_EQ: t = zext(btor, insn, boolector_eq(btor, a, b)); break;
case OP_SET_NE: t = zext(btor, insn, boolector_ne(btor, a, b)); break;
case OP_SET_LT: t = zext(btor, insn, boolector_slt(btor, a, b)); break;
case OP_SET_LE: t = zext(btor, insn, boolector_slte(btor, a, b)); break;
case OP_SET_GE: t = zext(btor, insn, boolector_sgte(btor, a, b)); break;
case OP_SET_GT: t = zext(btor, insn, boolector_sgt(btor, a, b)); break;
case OP_SET_B: t = zext(btor, insn, boolector_ult(btor, a, b)); break;
case OP_SET_BE: t = zext(btor, insn, boolector_ulte(btor, a, b)); break;
case OP_SET_AE: t = zext(btor, insn, boolector_ugte(btor, a, b)); break;
case OP_SET_A: t = zext(btor, insn, boolector_ugt(btor, a, b)); break;
default:
fprintf(stderr, "unsupported insn: %s\n", show_instruction(insn));
return;
}
insn->target->priv = t;
}
static void binop(Btor *btor, struct instruction *insn)
{
BoolectorSort s = get_sort(btor, insn->type, insn->pos);
binary(btor, s, insn);
}
static void icmp(Btor *btor, struct instruction *insn)
{
BoolectorSort s = get_sort(btor, insn->itype, insn->pos);
binary(btor, s, insn);
}
static void unop(Btor *btor, struct instruction *insn)
{
pseudo_t src = insn->src;
BoolectorNode *t;
switch (insn->opcode) {
case OP_SEXT: t = sext(btor, insn, mkivar(btor, insn, src)); break;
case OP_ZEXT: t = zext(btor, insn, mkivar(btor, insn, src)); break;
case OP_TRUNC: t = slice(btor, insn, mkivar(btor, insn, src)); break;
case OP_NEG: t = boolector_neg(btor, mktvar(btor, insn, src)); break;
case OP_NOT: t = boolector_not(btor, mktvar(btor, insn, src)); break;
default:
fprintf(stderr, "unsupported insn: %s\n", show_instruction(insn));
return;
}
insn->target->priv = t;
}
static void ternop(Btor *btor, struct instruction *insn)
{
BoolectorSort s = get_sort(btor, insn->type, insn->pos);
BoolectorNode *t, *a, *b, *c, *z, *d;
a = mkvar(btor, s, insn->src1);
b = mkvar(btor, s, insn->src2);
c = mkvar(btor, s, insn->src3);
if (!a || !b || !c)
return;
switch (insn->opcode) {
case OP_SEL:
z = boolector_zero(btor, s);
d = boolector_ne(btor, a, z);
t = boolector_cond(btor, d, b, c);
break;
default:
fprintf(stderr, "unsupported insn: %s\n", show_instruction(insn));
return;
}
insn->target->priv = t;
}
static bool add_precondition(Btor *btor, BoolectorNode **pre, struct instruction *insn)
{
BoolectorNode *a = get_arg(btor, insn, 0);
BoolectorNode *z = boolector_zero(btor, boolector_get_sort(btor, a));
BoolectorNode *n = boolector_ne(btor, a, z);
BoolectorNode *p = boolector_and(btor, *pre, n);
*pre = p;
return true;
}
static bool check_btor(Btor *btor, BoolectorNode *p, BoolectorNode *n, struct instruction *insn)
{
char model_format[] = "btor";
int res;
n = boolector_implies(btor, p, n);
boolector_assert(btor, boolector_not(btor, n));
res = boolector_sat(btor);
switch (res) {
case BOOLECTOR_UNSAT:
return 1;
case BOOLECTOR_SAT:
sparse_error(insn->pos, "assertion failed");
show_entry(insn->bb->ep);
boolector_dump_btor(btor, stdout);
boolector_print_model(btor, model_format, stdout);
break;
default:
sparse_error(insn->pos, "SMT failure");
break;
}
return 0;
}
static bool check_assert(Btor *btor, BoolectorNode *pre, struct instruction *insn)
{
BoolectorNode *a = get_arg(btor, insn, 0);
BoolectorNode *z = boolector_zero(btor, boolector_get_sort(btor, a));
BoolectorNode *n = boolector_ne(btor, a, z);
return check_btor(btor, pre, n, insn);
}
static bool check_equal(Btor *btor, BoolectorNode *pre, struct instruction *insn)
{
BoolectorNode *a = get_arg(btor, insn, 0);
BoolectorNode *b = get_arg(btor, insn, 1);
BoolectorNode *n = boolector_eq(btor, a, b);
return check_btor(btor, pre, n, insn);
}
static bool check_const(Btor *ctxt, struct instruction *insn)
{
pseudo_t src1 = ptr_list_nth(insn->arguments, 0);
pseudo_t src2 = ptr_list_nth(insn->arguments, 1);
if (src2->type != PSEUDO_VAL)
sparse_error(insn->pos, "should be a constant: %s", show_pseudo(src2));
if (src1 == src2)
return 1;
if (src1->type != PSEUDO_VAL)
sparse_error(insn->pos, "not a constant: %s", show_pseudo(src1));
else
sparse_error(insn->pos, "invalid value: %s != %s", show_pseudo(src1), show_pseudo(src2));
return 0;
}
static bool check_call(Btor *btor, BoolectorNode **pre, struct instruction *insn)
{
pseudo_t func = insn->func;
struct ident *ident = func->ident;
if (ident == &__assume_ident)
return add_precondition(btor, pre, insn);
if (ident == &__assert_ident)
return check_assert(btor, *pre, insn);
if (ident == &__assert_eq_ident)
return check_equal(btor, *pre, insn);
if (ident == &__assert_const_ident)
return check_const(btor, insn);
return 0;
}
static bool check_function(struct entrypoint *ep)
{
Btor *btor = boolector_new();
BoolectorNode *pre = boolector_true(btor);
struct basic_block *bb;
int rc = 0;
boolector_set_opt(btor, BTOR_OPT_MODEL_GEN, 1);
boolector_set_opt(btor, BTOR_OPT_INCREMENTAL, 1);
FOR_EACH_PTR(ep->bbs, bb) {
struct instruction *insn;
FOR_EACH_PTR(bb->insns, insn) {
if (!insn->bb)
continue;
switch (insn->opcode) {
case OP_ENTRY:
continue;
case OP_BINARY ... OP_BINARY_END:
binop(btor, insn);
break;
case OP_BINCMP ... OP_BINCMP_END:
icmp(btor, insn);
break;
case OP_UNOP ... OP_UNOP_END:
unop(btor, insn);
break;
case OP_SEL:
ternop(btor, insn);
break;
case OP_CALL:
rc &= check_call(btor, &pre, insn);
break;
case OP_RET:
goto out;
case OP_INLINED_CALL:
case OP_DEATHNOTE:
case OP_NOP:
case OP_CONTEXT:
continue;
default:
fprintf(stderr, "unsupported insn: %s\n", show_instruction(insn));
goto out;
}
} END_FOR_EACH_PTR(insn);
} END_FOR_EACH_PTR(bb);
fprintf(stderr, "unterminated function\n");
out:
boolector_release_all(btor);
boolector_delete(btor);
return rc;
}
static void check_functions(struct symbol_list *list)
{
struct symbol *sym;
FOR_EACH_PTR(list, sym) {
struct entrypoint *ep;
expand_symbol(sym);
ep = linearize_symbol(sym);
if (!ep || !ep->entry)
continue;
check_function(ep);
} END_FOR_EACH_PTR(sym);
}
int main(int argc, char **argv)
{
struct string_list *filelist = NULL;
char *file;
Wdecl = 0;
sparse_initialize(argc, argv, &filelist);
declare_builtins(0, builtins_scheck);
predefine_strong("__SYMBOLIC_CHECKER__");
// Expand, linearize and check.
FOR_EACH_PTR(filelist, file) {
check_functions(sparse(file));
} END_FOR_EACH_PTR(file);
return 0;
}
sparse-0.6.4/scope.c 0000664 0000000 0000000 00000010131 14115310122 0014255 0 ustar 00root root 0000000 0000000 /*
* Symbol scoping.
*
* This is pretty trivial.
*
* Copyright (C) 2003 Transmeta Corp.
* 2003-2004 Linus Torvalds
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include
#include
#include
#include "lib.h"
#include "allocate.h"
#include "symbol.h"
#include "scope.h"
static struct scope builtin_scope = { .next = &builtin_scope };
struct scope *block_scope = &builtin_scope, // regular automatic variables etc
*label_scope = NULL, // expr-stmt labels
*function_scope = &builtin_scope, // labels, arguments etc
*file_scope = &builtin_scope, // static
*global_scope = &builtin_scope; // externally visible
void set_current_scope(struct symbol *sym)
{
sym->scope = block_scope;
}
void bind_scope(struct symbol *sym, struct scope *scope)
{
sym->scope = scope;
add_symbol(&scope->symbols, sym);
}
void rebind_scope(struct symbol *sym, struct scope *new)
{
struct scope *old = sym->scope;
if (old == new)
return;
if (old)
delete_ptr_list_entry((struct ptr_list**) &old->symbols, sym, 1);
bind_scope(sym, new);
}
static void start_scope(struct scope **s)
{
struct scope *scope = __alloc_scope(0);
scope->next = *s;
*s = scope;
}
void start_file_scope(void)
{
struct scope *scope = __alloc_scope(0);
scope->next = &builtin_scope;
file_scope = scope;
/* top-level stuff defaults to file scope, "extern" etc will choose global scope */
function_scope = scope;
block_scope = scope;
}
void start_block_scope(void)
{
start_scope(&block_scope);
}
void start_function_scope(void)
{
start_scope(&block_scope);
start_scope(&label_scope);
function_scope = label_scope;
}
static void remove_symbol_scope(struct symbol *sym)
{
struct symbol **ptr = &sym->ident->symbols;
while (*ptr != sym)
ptr = &(*ptr)->next_id;
*ptr = sym->next_id;
}
static void end_scope(struct scope **s)
{
struct scope *scope = *s;
struct symbol_list *symbols = scope->symbols;
struct symbol *sym;
*s = scope->next;
scope->symbols = NULL;
FOR_EACH_PTR(symbols, sym) {
remove_symbol_scope(sym);
} END_FOR_EACH_PTR(sym);
}
void end_file_scope(void)
{
end_scope(&file_scope);
}
void new_file_scope(void)
{
if (file_scope != &builtin_scope)
end_file_scope();
start_file_scope();
}
void end_block_scope(void)
{
end_scope(&block_scope);
}
void end_function_scope(void)
{
end_scope(&block_scope);
end_label_scope();
function_scope = label_scope;
}
void start_label_scope(void)
{
start_scope(&label_scope);
}
void end_label_scope(void)
{
struct symbol *sym;
FOR_EACH_PTR(label_scope->symbols, sym) {
if (!sym->stmt || sym->used)
continue;
if (sym->label_modifiers & MOD_UNUSED)
continue;
warning(sym->pos, "unused label '%s'", show_ident(sym->ident));
} END_FOR_EACH_PTR(sym);
end_scope(&label_scope);
}
int is_outer_scope(struct scope *scope)
{
if (scope == block_scope)
return 0;
if (scope == &builtin_scope && block_scope->next == &builtin_scope)
return 0;
return 1;
}
int is_in_scope(struct scope *outer, struct scope *inner)
{
while (inner != outer) {
if (inner == function_scope)
return 0;
inner = inner->next;
}
return 1;
}
sparse-0.6.4/scope.h 0000664 0000000 0000000 00000004176 14115310122 0014276 0 ustar 00root root 0000000 0000000 #ifndef SCOPE_H
#define SCOPE_H
/*
* Symbol scoping is pretty simple.
*
* Copyright (C) 2003 Transmeta Corp.
* 2003 Linus Torvalds
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
struct symbol;
struct scope {
struct symbol_list *symbols; /* List of symbols in this scope */
struct scope *next;
};
extern struct scope
*block_scope,
*label_scope,
*function_scope,
*file_scope,
*global_scope;
static inline int toplevel(struct scope *scope)
{
return scope == file_scope || scope == global_scope;
}
extern void start_file_scope(void);
extern void end_file_scope(void);
extern void new_file_scope(void);
extern void start_block_scope(void);
extern void end_block_scope(void);
extern void start_function_scope(void);
extern void end_function_scope(void);
extern void start_label_scope(void);
extern void end_label_scope(void);
extern void set_current_scope(struct symbol *);
extern void bind_scope(struct symbol *, struct scope *);
extern void rebind_scope(struct symbol *, struct scope *);
extern int is_outer_scope(struct scope *);
extern int is_in_scope(struct scope *outer, struct scope *inner);
#endif
sparse-0.6.4/semind.1 0000664 0000000 0000000 00000005656 14115310122 0014361 0 ustar 00root root 0000000 0000000 .\" Sindex manpage by Alexey Gladkov
.TH semind "1"
.
.SH NAME
semind \- Semantic Indexer for C
.
.SH SYNOPSIS
.B semind
[\fIoptions\fR]
.br
.B semind
[\fIoptions\fR] \fIadd\fR [\fIcommand options\fR] [\fI--\fR] [\fIcompiler options\fR] [\fIfiles...\fR]
.br
.B semind
[\fIoptions\fR] \fIrm\fR [\fIcommand options\fR] \fIpattern\fR
.br
.B semind
[\fIoptions\fR] \fIsearch\fR [\fIcommand options\fR] [\fIpattern\fR]
.br
.B semind [\fIoptions\fR] \fIsearch\fR [\fIcommand options\fR] (\fI-e\fR|\fI-l\fR) \fIfilename\fR:\fIlinenr\fR:\fIcolumn\fR
.br
.SH DESCRIPTION
.P
semind is the simple to use cscope-like tool based on sparse/dissect. Unlike
cscope it runs after pre-processor and thus it can't index the code filtered out
by ifdef's, but otoh it understands how the symbol is used and it can track the
usage of struct members.
.
.SH SUBCOMMANDS
.TP
\fBadd\fR
generates or updates semantic index file.
.TP
\fBrm\fR
removes files from the index by \fIpattern\fR. The \fIpattern\fR is a
.BR glob (7)
wildcard pattern.
.TP
\fBsearch\fR
queries information about symbol by \fIpattern\fR. The \fIpattern\fR is a
.BR glob (7)
wildcard pattern.
.
.SH COMMON OPTIONS
.TP
\fB-D\fR, \fB--database=FILE\fR
specify database file (default: ./semind.sqlite).
.TP
\fB-v\fR, \fB--verbose\fR
show information about what is being done.
.TP
\fB-h\fR, \fB--help\fR
show this text and exit.
.
.SH ADD OPTIONS
.TP
\fB--include-local-syms\fR
include into the index local symbols.
.
.SH SEARCH OPTIONS
.TP
\fB-f\fR, \fB--format=STRING\fR
specify an output format. Default: '(%m) %f\\t%l\\t%c\\t%C\\t%s' (see
.BR FORMAT
below).
.TP
\fB-p\fR, \fB--path=PATTERN\fR
search symbols only in specified directories.
.TP
\fB-m\fR, \fB--mode=MODE\fR
search only the specified type of access (see
.BR MODE
below).
.TP
\fB-k\fR, \fB--kind=KIND\fR
specify a kind of symbol (see
.BR KIND
below).
.TP
\fB-e\fR, \fB--explain\fR
Show what happens in the specified file position;
.TP
\fB-l\fR, \fB--location\fR
Show usage of symbols from a specific file position;
.TP
\fB-v\fR, \fB--verbose\fR
show information about what is being done;
.TP
\fB-h\fR, \fB--help\fR
show this text and exit.
.
.SH FORMAT
.TP
\fB%m\fR
access mode in human readable form (see
.BR MODE
below).
.TP
\fB%f\fR
file name.
.TP
\fB%l\fR
line number.
.TP
\fB%c\fR
column number.
.TP
\fB%C\fR
the name of the function in which the symbol occurs.
.TP
\fB%n\fR
symbol name.
.TP
\fB%s\fR
source code line. Indexer does not save source code lines. They are read from
the file during the search.
.
.SH KIND
.TP
\fBf\fR
function
.TP
\fBs\fR
strict
.TP
\fBm\fR
struct member
.
.SH MODE
The \fBMODE\fR is dumped as a 3-letter string. The first letter denotes address
of part, 2-nd - access by value, 3-rd - access by pointer. A special
value '\fIdef\fR' means a symbol definition.
.TP
\fBr\fR
read
.TP
\fBw\fR
write
.TP
\fBm\fR
read and write
.
.SH SEE ALSO
.BR sparse (1)
.
.SH HOMEPAGE
https://sparse.docs.kernel.org
.
.SH MAILING LIST
linux-sparse@vger.kernel.org
.
sparse-0.6.4/semind.c 0000664 0000000 0000000 00000070235 14115310122 0014436 0 ustar 00root root 0000000 0000000 /*
* semind - semantic indexer for C.
*
* Copyright (C) 2020 Alexey Gladkov
*/
#define _GNU_SOURCE
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "dissect.h"
#define U_DEF (0x100 << U_SHIFT)
#define SINDEX_DATABASE_VERSION 1
#define message(fmt, ...) semind_error(0, 0, (fmt), ##__VA_ARGS__)
static const char *progname;
static const char *semind_command = NULL;
// common options
static const char *semind_dbfile = "semind.sqlite";
static int semind_verbose = 0;
static char cwd[PATH_MAX];
static size_t n_cwd;
// 'add' command options
static struct string_list *semind_filelist = NULL;
static int semind_include_local_syms = 0;
struct semind_streams {
sqlite3_int64 id;
};
static struct semind_streams *semind_streams = NULL;
static int semind_streams_nr = 0;
// 'search' command options
static int semind_search_modmask;
static int semind_search_modmask_defined = 0;
static int semind_search_kind = 0;
static char *semind_search_path = NULL;
static char *semind_search_symbol = NULL;
static const char *semind_search_format = "(%m) %f\t%l\t%c\t%C\t%s";
#define EXPLAIN_LOCATION 1
#define USAGE_BY_LOCATION 2
static int semind_search_by_location;
static char *semind_search_filename;
static int semind_search_line;
static int semind_search_column;
static sqlite3 *semind_db = NULL;
static sqlite3_stmt *lock_stmt = NULL;
static sqlite3_stmt *unlock_stmt = NULL;
static sqlite3_stmt *insert_rec_stmt = NULL;
static sqlite3_stmt *select_file_stmt = NULL;
static sqlite3_stmt *insert_file_stmt = NULL;
static sqlite3_stmt *delete_file_stmt = NULL;
struct command {
const char *name;
int dbflags;
void (*parse_cmdline)(int argc, char **argv);
void (*handler)(int argc, char **argv);
};
static void show_usage(void)
{
if (semind_command)
printf("Try '%s %s --help' for more information.\n",
progname, semind_command);
else
printf("Try '%s --help' for more information.\n",
progname);
exit(1);
}
static void show_help(int ret)
{
printf(
"Usage: %1$s [options]\n"
" or: %1$s [options] add [command options] [--] [compiler options] [files...]\n"
" or: %1$s [options] rm [command options] pattern\n"
" or: %1$s [options] search [command options] pattern\n"
"\n"
"These are common %1$s commands used in various situations:\n"
" add Generate or updates semantic index file for c-source code;\n"
" rm Remove files from the index by pattern;\n"
" search Make index queries.\n"
"\n"
"Options:\n"
" -D, --database=FILE Specify database file (default: %2$s);\n"
" -B, --basedir=DIR Define project top directory (default is the current directory);\n"
" -v, --verbose Show information about what is being done;\n"
" -h, --help Show this text and exit.\n"
"\n"
"Environment:\n"
" SINDEX_DATABASE Database file location.\n"
" SINDEX_BASEDIR Project top directory.\n"
"\n"
"Report bugs to authors.\n"
"\n",
progname, semind_dbfile);
exit(ret);
}
static void show_help_add(int ret)
{
printf(
"Usage: %1$s add [options] [--] [compiler options] files...\n"
"\n"
"Utility creates or updates a symbol index.\n"
"\n"
"Options:\n"
" --include-local-syms Include into the index local symbols;\n"
" -v, --verbose Show information about what is being done;\n"
" -h, --help Show this text and exit.\n"
"\n"
"Report bugs to authors.\n"
"\n",
progname);
exit(ret);
}
static void show_help_rm(int ret)
{
printf(
"Usage: %1$s rm [options] pattern\n"
"\n"
"Utility removes source files from the index.\n"
"The pattern is a glob(7) wildcard pattern.\n"
"\n"
"Options:\n"
" -v, --verbose Show information about what is being done;\n"
" -h, --help Show this text and exit.\n"
"\n"
"Report bugs to authors.\n"
"\n",
progname);
exit(ret);
}
static void show_help_search(int ret)
{
printf(
"Usage: %1$s search [options] [pattern]\n"
" or: %1$s search [options] (-e|-l) filename[:linenr[:column]]\n"
"\n"
"Utility searches information about symbol by pattern.\n"
"The pattern is a glob(7) wildcard pattern.\n"
"\n"
"Options:\n"
" -f, --format=STRING Specify an output format;\n"
" -p, --path=PATTERN Search symbols only in specified directories;\n"
" -m, --mode=MODE Search only the specified type of access;\n"
" -k, --kind=KIND Specify a kind of symbol;\n"
" -e, --explain Show what happens in the specified file position;\n"
" -l, --location Show usage of symbols from a specific file position;\n"
" -v, --verbose Show information about what is being done;\n"
" -h, --help Show this text and exit.\n"
"\n"
"The KIND can be one of the following: `s', `f', `v', `m'.\n"
"\n"
"Report bugs to authors.\n"
"\n",
progname);
exit(ret);
}
static void semind_print_progname(void)
{
fprintf(stderr, "%s: ", progname);
if (semind_command)
fprintf(stderr, "%s: ", semind_command);
}
static void semind_error(int status, int errnum, const char *fmt, ...)
{
va_list ap;
semind_print_progname();
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
if (errnum > 0)
fprintf(stderr, ": %s", strerror(errnum));
fprintf(stderr, "\n");
if (status)
exit(status);
}
static void set_search_modmask(const char *v)
{
size_t n = strlen(v);
if (n != 1 && n != 3)
semind_error(1, 0, "the length of mode value must be 1 or 3: %s", v);
semind_search_modmask_defined = 1;
semind_search_modmask = 0;
if (n == 1) {
switch (v[0]) {
case 'r': v = "rrr"; break;
case 'w': v = "ww-"; break;
case 'm': v = "mmm"; break;
case '-': v = "---"; break;
default: semind_error(1, 0, "unknown modificator: %s", v);
}
} else if (!strcmp(v, "def")) {
semind_search_modmask = U_DEF;
return;
}
static const int modes[] = {
U_R_AOF, U_W_AOF, U_R_AOF | U_W_AOF,
U_R_VAL, U_W_VAL, U_R_VAL | U_W_VAL,
U_R_PTR, U_W_PTR, U_R_PTR | U_W_PTR,
};
for (int i = 0; i < 3; i++) {
switch (v[i]) {
case 'r': semind_search_modmask |= modes[i * 3]; break;
case 'w': semind_search_modmask |= modes[i * 3 + 1]; break;
case 'm': semind_search_modmask |= modes[i * 3 + 2]; break;
case '-': break;
default: semind_error(1, 0,
"unknown modificator in the mode value"
" (`r', `w', `m' or `-' expected): %c", v[i]);
}
}
}
static void parse_cmdline(int argc, char **argv)
{
static const struct option long_options[] = {
{ "database", required_argument, NULL, 'D' },
{ "basedir", required_argument, NULL, 'B' },
{ "verbose", no_argument, NULL, 'v' },
{ "help", no_argument, NULL, 'h' },
{ NULL }
};
int c;
char *basedir = getenv("SINDEX_BASEDIR");
char *env;
if ((env = getenv("SINDEX_DATABASE")) != NULL)
semind_dbfile = env;
while ((c = getopt_long(argc, argv, "+B:D:vh", long_options, NULL)) != -1) {
switch (c) {
case 'D':
semind_dbfile = optarg;
break;
case 'B':
basedir = optarg;
break;
case 'v':
semind_verbose++;
break;
case 'h':
show_help(0);
}
}
if (optind == argc) {
message("command required");
show_usage();
}
if (basedir) {
if (!realpath(basedir, cwd))
semind_error(1, errno, "unable to get project base directory");
n_cwd = strlen(cwd);
}
}
static void parse_cmdline_add(int argc, char **argv)
{
static const struct option long_options[] = {
{ "include-local-syms", no_argument, NULL, 1 },
{ "verbose", no_argument, NULL, 'v' },
{ "help", no_argument, NULL, 'h' },
{ NULL }
};
int c;
opterr = 0;
while ((c = getopt_long(argc, argv, "+vh", long_options, NULL)) != -1) {
switch (c) {
case 1:
semind_include_local_syms = 1;
break;
case 'v':
semind_verbose++;
break;
case 'h':
show_help_add(0);
case '?':
goto done;
}
}
done:
if (optind == argc) {
message("more arguments required");
show_usage();
}
// enforce tabstop
tabstop = 1;
// step back since sparse_initialize will ignore argv[0].
optind--;
sparse_initialize(argc - optind, argv + optind, &semind_filelist);
}
static void parse_cmdline_rm(int argc, char **argv)
{
static const struct option long_options[] = {
{ "verbose", no_argument, NULL, 'v' },
{ "help", no_argument, NULL, 'h' },
{ NULL }
};
int c;
while ((c = getopt_long(argc, argv, "+vh", long_options, NULL)) != -1) {
switch (c) {
case 'v':
semind_verbose++;
break;
case 'h':
show_help_rm(0);
}
}
if (optind == argc) {
message("more arguments required");
show_usage();
}
}
static void parse_cmdline_search(int argc, char **argv)
{
static const struct option long_options[] = {
{ "explain", no_argument, NULL, 'e' },
{ "format", required_argument, NULL, 'f' },
{ "path", required_argument, NULL, 'p' },
{ "location", no_argument, NULL, 'l' },
{ "mode", required_argument, NULL, 'm' },
{ "kind", required_argument, NULL, 'k' },
{ "verbose", no_argument, NULL, 'v' },
{ "help", no_argument, NULL, 'h' },
{ NULL }
};
int c;
while ((c = getopt_long(argc, argv, "+ef:m:k:p:lvh", long_options, NULL)) != -1) {
switch (c) {
case 'e':
semind_search_by_location = EXPLAIN_LOCATION;
break;
case 'l':
semind_search_by_location = USAGE_BY_LOCATION;
break;
case 'f':
semind_search_format = optarg;
break;
case 'm':
set_search_modmask(optarg);
break;
case 'k':
semind_search_kind = tolower(optarg[0]);
break;
case 'p':
semind_search_path = optarg;
break;
case 'v':
semind_verbose++;
break;
case 'h':
show_help_search(0);
}
}
if (semind_search_by_location) {
char *str;
if (optind == argc)
semind_error(1, 0, "one argument required");
str = argv[optind];
while (str) {
char *ptr;
if ((ptr = strchr(str, ':')) != NULL)
*ptr++ = '\0';
if (*str != '\0') {
if (!semind_search_filename) {
semind_search_filename = str;
} else if (!semind_search_line) {
semind_search_line = atoi(str);
} else if (!semind_search_column) {
semind_search_column = atoi(str);
}
}
str = ptr;
}
} else if (optind < argc)
semind_search_symbol = argv[optind++];
}
static int query_appendf(sqlite3_str *query, const char *fmt, ...)
{
int status;
va_list args;
va_start(args, fmt);
sqlite3_str_vappendf(query, fmt, args);
va_end(args);
if ((status = sqlite3_str_errcode(query)) == SQLITE_OK)
return 0;
if (status == SQLITE_NOMEM)
message("not enough memory");
if (status == SQLITE_TOOBIG)
message("string too big");
return -1;
}
static inline void sqlite_bind_text(sqlite3_stmt *stmt, const char *field, const char *var, int len)
{
if (sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, field), var, len, SQLITE_STATIC) != SQLITE_OK)
semind_error(1, 0, "unable to bind value for %s: %s", field, sqlite3_errmsg(semind_db));
}
static inline void sqlite_bind_int64(sqlite3_stmt *stmt, const char *field, long long var)
{
if (sqlite3_bind_int64(stmt, sqlite3_bind_parameter_index(stmt, field), var) != SQLITE_OK)
semind_error(1, 0, "unable to bind value for %s: %s", field, sqlite3_errmsg(semind_db));
}
static inline void sqlite_prepare(const char *sql, sqlite3_stmt **stmt)
{
int ret;
do {
ret = sqlite3_prepare_v2(semind_db, sql, -1, stmt, NULL);
if (ret != SQLITE_OK && ret != SQLITE_BUSY)
semind_error(1, 0, "unable to prepare query: %s: %s", sqlite3_errmsg(semind_db), sql);
} while (ret == SQLITE_BUSY);
}
static inline void sqlite_prepare_persistent(const char *sql, sqlite3_stmt **stmt)
{
int ret;
do {
ret = sqlite3_prepare_v3(semind_db, sql, -1, SQLITE_PREPARE_PERSISTENT, stmt, NULL);
if (ret != SQLITE_OK && ret != SQLITE_BUSY)
semind_error(1, 0, "unable to prepare query: %s: %s", sqlite3_errmsg(semind_db), sql);
} while (ret == SQLITE_BUSY);
}
static inline void sqlite_reset_stmt(sqlite3_stmt *stmt)
{
// Contrary to the intuition of many, sqlite3_reset() does not reset the
// bindings on a prepared statement. Use this routine to reset all host
// parameters to NULL.
sqlite3_clear_bindings(stmt);
sqlite3_reset(stmt);
}
static int sqlite_run(sqlite3_stmt *stmt)
{
int ret = sqlite3_step(stmt);
if (ret != SQLITE_DONE && ret != SQLITE_ROW)
semind_error(1, 0, "unable to process query: %s: %s", sqlite3_errmsg(semind_db), sqlite3_sql(stmt));
return ret;
}
static void sqlite_command(const char *sql)
{
sqlite3_stmt *stmt;
sqlite_prepare(sql, &stmt);
sqlite_run(stmt);
sqlite3_finalize(stmt);
}
static sqlite3_int64 get_db_version(void)
{
sqlite3_stmt *stmt;
sqlite3_int64 dbversion;
sqlite_prepare("PRAGMA user_version", &stmt);
sqlite_run(stmt);
dbversion = sqlite3_column_int64(stmt, 0);
sqlite3_finalize(stmt);
return dbversion;
}
static void set_db_version(void)
{
char *sql;
sqlite3_str *query = sqlite3_str_new(semind_db);
if (query_appendf(query, "PRAGMA user_version = %d", SINDEX_DATABASE_VERSION) < 0)
exit(1);
sql = sqlite3_str_finish(query);
sqlite_command(sql);
sqlite3_free(sql);
}
static void open_temp_database(void)
{
static const char *database_schema[] = {
"ATTACH ':memory:' AS tempdb",
"CREATE TABLE tempdb.semind ("
" file INTEGER NOT NULL,"
" line INTEGER NOT NULL,"
" column INTEGER NOT NULL,"
" symbol TEXT NOT NULL,"
" kind INTEGER NOT NULL,"
" context TEXT,"
" mode INTEGER NOT NULL"
")",
NULL,
};
for (int i = 0; database_schema[i]; i++)
sqlite_command(database_schema[i]);
}
static void open_database(const char *filename, int flags)
{
static const char *database_schema[] = {
"CREATE TABLE file ("
" id INTEGER PRIMARY KEY AUTOINCREMENT,"
" name TEXT UNIQUE NOT NULL,"
" mtime INTEGER NOT NULL"
")",
"CREATE TABLE semind ("
" file INTEGER NOT NULL REFERENCES file(id) ON DELETE CASCADE,"
" line INTEGER NOT NULL,"
" column INTEGER NOT NULL,"
" symbol TEXT NOT NULL,"
" kind INTEGER NOT NULL,"
" context TEXT,"
" mode INTEGER NOT NULL"
")",
"CREATE UNIQUE INDEX semind_0 ON semind (symbol, kind, mode, file, line, column)",
"CREATE INDEX semind_1 ON semind (file)",
NULL,
};
int exists = !access(filename, R_OK);
if (sqlite3_open_v2(filename, &semind_db, flags, NULL) != SQLITE_OK)
semind_error(1, 0, "unable to open database: %s: %s", filename, sqlite3_errmsg(semind_db));
sqlite_command("PRAGMA journal_mode = WAL");
sqlite_command("PRAGMA synchronous = OFF");
sqlite_command("PRAGMA secure_delete = FAST");
sqlite_command("PRAGMA busy_timeout = 2147483647");
sqlite_command("PRAGMA foreign_keys = ON");
if (exists) {
if (get_db_version() < SINDEX_DATABASE_VERSION)
semind_error(1, 0, "%s: Database too old. Please rebuild it.", filename);
return;
}
set_db_version();
for (int i = 0; database_schema[i]; i++)
sqlite_command(database_schema[i]);
}
struct index_record {
const char *context;
int ctx_len;
const char *symbol;
int sym_len;
int kind;
unsigned int mode;
long long mtime;
sqlite3_int64 file;
int line;
int col;
};
static void insert_record(struct index_record *rec)
{
sqlite_bind_text(insert_rec_stmt, "@context", rec->context, rec->ctx_len);
sqlite_bind_text(insert_rec_stmt, "@symbol", rec->symbol, rec->sym_len);
sqlite_bind_int64(insert_rec_stmt, "@kind", rec->kind);
sqlite_bind_int64(insert_rec_stmt, "@mode", rec->mode);
sqlite_bind_int64(insert_rec_stmt, "@file", rec->file);
sqlite_bind_int64(insert_rec_stmt, "@line", rec->line);
sqlite_bind_int64(insert_rec_stmt, "@column", rec->col);
sqlite_run(insert_rec_stmt);
sqlite_reset_stmt(insert_rec_stmt);
}
static void update_stream(void)
{
if (semind_streams_nr >= input_stream_nr)
return;
semind_streams = realloc(semind_streams, input_stream_nr * sizeof(struct semind_streams));
if (!semind_streams)
semind_error(1, errno, "realloc");
sqlite_run(lock_stmt);
for (int i = semind_streams_nr; i < input_stream_nr; i++) {
struct stat st;
const char *filename;
char fullname[PATH_MAX];
sqlite3_int64 cur_mtime = 0;
if (input_streams[i].fd != -1) {
/*
* FIXME: Files in the input_streams may be duplicated.
*/
if (stat(input_streams[i].name, &st) < 0)
semind_error(1, errno, "stat: %s", input_streams[i].name);
cur_mtime = st.st_mtime;
if (!realpath(input_streams[i].name, fullname))
semind_error(1, errno, "realpath: %s", input_streams[i].name);
if (!strncmp(fullname, cwd, n_cwd) && fullname[n_cwd] == '/') {
filename = fullname + n_cwd + 1;
semind_streams[i].id = 0;
} else {
semind_streams[i].id = -1;
continue;
}
} else {
semind_streams[i].id = -1;
continue;
}
if (semind_verbose > 1)
message("filename: %s", filename);
sqlite_bind_text(select_file_stmt, "@name", filename, -1);
if (sqlite_run(select_file_stmt) == SQLITE_ROW) {
sqlite3_int64 old_mtime;
semind_streams[i].id = sqlite3_column_int64(select_file_stmt, 0);
old_mtime = sqlite3_column_int64(select_file_stmt, 1);
sqlite_reset_stmt(select_file_stmt);
if (cur_mtime == old_mtime)
continue;
sqlite_bind_text(delete_file_stmt, "@name", filename, -1);
sqlite_run(delete_file_stmt);
sqlite_reset_stmt(delete_file_stmt);
}
sqlite_reset_stmt(select_file_stmt);
sqlite_bind_text(insert_file_stmt, "@name", filename, -1);
sqlite_bind_int64(insert_file_stmt, "@mtime", cur_mtime);
sqlite_run(insert_file_stmt);
sqlite_reset_stmt(insert_file_stmt);
semind_streams[i].id = sqlite3_last_insert_rowid(semind_db);
}
sqlite_run(unlock_stmt);
semind_streams_nr = input_stream_nr;
}
static void r_symbol(unsigned mode, struct position *pos, struct symbol *sym)
{
static struct ident null;
struct ident *ctx = &null;
struct index_record rec;
update_stream();
if (semind_streams[pos->stream].id == -1)
return;
if (!semind_include_local_syms && sym_is_local(sym))
return;
if (!sym->ident) {
warning(*pos, "empty ident");
return;
}
if (dissect_ctx)
ctx = dissect_ctx->ident;
rec.context = ctx->name;
rec.ctx_len = ctx->len;
rec.symbol = sym->ident->name;
rec.sym_len = sym->ident->len;
rec.kind = sym->kind;
rec.mode = mode;
rec.file = semind_streams[pos->stream].id;
rec.line = pos->line;
rec.col = pos->pos;
insert_record(&rec);
}
static void r_member(unsigned mode, struct position *pos, struct symbol *sym, struct symbol *mem)
{
static struct ident null;
static char memname[1024];
struct ident *ni, *si, *mi;
struct ident *ctx = &null;
struct index_record rec;
update_stream();
if (semind_streams[pos->stream].id == -1)
return;
if (!semind_include_local_syms && sym_is_local(sym))
return;
ni = built_in_ident("?");
si = sym->ident ?: ni;
/* mem == NULL means entire struct accessed */
mi = mem ? (mem->ident ?: ni) : built_in_ident("*");
if (dissect_ctx)
ctx = dissect_ctx->ident;
snprintf(memname, sizeof(memname), "%.*s.%.*s", si->len, si->name, mi->len, mi->name);
rec.context = ctx->name;
rec.ctx_len = ctx->len;
rec.symbol = memname;
rec.sym_len = si->len + mi->len + 1;
rec.kind = 'm';
rec.mode = mode;
rec.file = semind_streams[pos->stream].id;
rec.line = pos->line;
rec.col = pos->pos;
insert_record(&rec);
}
static void r_symdef(struct symbol *sym)
{
r_symbol(U_DEF, &sym->pos, sym);
}
static void r_memdef(struct symbol *sym, struct symbol *mem)
{
r_member(U_DEF, &mem->pos, sym, mem);
}
static void command_add(int argc, char **argv)
{
static struct reporter reporter = {
.r_symdef = r_symdef,
.r_symbol = r_symbol,
.r_memdef = r_memdef,
.r_member = r_member,
};
open_temp_database();
sqlite_prepare_persistent(
"BEGIN IMMEDIATE",
&lock_stmt);
sqlite_prepare_persistent(
"COMMIT",
&unlock_stmt);
sqlite_prepare_persistent(
"INSERT OR IGNORE INTO tempdb.semind "
"(context, symbol, kind, mode, file, line, column) "
"VALUES (@context, @symbol, @kind, @mode, @file, @line, @column)",
&insert_rec_stmt);
sqlite_prepare_persistent(
"SELECT id, mtime FROM file WHERE name == @name",
&select_file_stmt);
sqlite_prepare_persistent(
"INSERT INTO file (name, mtime) VALUES (@name, @mtime)",
&insert_file_stmt);
sqlite_prepare_persistent(
"DELETE FROM file WHERE name == @name",
&delete_file_stmt);
dissect(&reporter, semind_filelist);
sqlite_run(lock_stmt);
sqlite_command("INSERT OR IGNORE INTO semind SELECT * FROM tempdb.semind");
sqlite_run(unlock_stmt);
sqlite3_finalize(insert_rec_stmt);
sqlite3_finalize(select_file_stmt);
sqlite3_finalize(insert_file_stmt);
sqlite3_finalize(delete_file_stmt);
sqlite3_finalize(lock_stmt);
sqlite3_finalize(unlock_stmt);
free(semind_streams);
}
static void command_rm(int argc, char **argv)
{
sqlite3_stmt *stmt;
sqlite_command("BEGIN IMMEDIATE");
sqlite_prepare("DELETE FROM file WHERE name GLOB @file", &stmt);
if (semind_verbose > 1)
message("SQL: %s", sqlite3_sql(stmt));
for (int i = 0; i < argc; i++) {
sqlite_bind_text(stmt, "@file", argv[i], -1);
sqlite_run(stmt);
sqlite_reset_stmt(stmt);
}
sqlite3_finalize(stmt);
sqlite_command("COMMIT");
}
static inline void print_mode(char *value)
{
char str[3];
int v = atoi(value);
if (v == U_DEF) {
printf("def");
return;
}
#define U(m) "-rwm"[(v / m) & 3]
str[0] = U(U_R_AOF);
str[1] = U(U_R_VAL);
str[2] = U(U_R_PTR);
printf("%.3s", str);
#undef U
}
static char *semind_file_name;
static FILE *semind_file_fd;
static int semind_file_lnum;
static char *semind_line;
static size_t semind_line_buflen;
static int semind_line_len;
static void print_file_line(const char *name, int lnum)
{
/*
* All files are sorted by name and line number. So, we can reopen
* the file and read it line by line.
*/
if (!semind_file_name || strcmp(semind_file_name, name)) {
if (semind_file_fd) {
fclose(semind_file_fd);
free(semind_file_name);
}
semind_file_name = strdup(name);
if (!semind_file_name)
semind_error(1, errno, "strdup");
semind_file_fd = fopen(name, "r");
if (!semind_file_fd)
semind_error(1, errno, "fopen: %s", name);
semind_file_lnum = 0;
}
do {
if (semind_file_lnum == lnum) {
if (semind_line[semind_line_len-1] == '\n')
semind_line_len--;
printf("%.*s", semind_line_len, semind_line);
break;
}
semind_file_lnum++;
errno = 0;
} while((semind_line_len = getline(&semind_line, &semind_line_buflen, semind_file_fd)) != -1);
if (errno && errno != EOF)
semind_error(1, errno, "getline");
}
static int search_query_callback(void *data, int argc, char **argv, char **colname)
{
char *fmt = (char *) semind_search_format;
char buf[32];
int quote = 0;
int n = 0;
while (*fmt != '\0') {
char c = *fmt;
if (quote) {
quote = 0;
switch (c) {
case 't': c = '\t'; break;
case 'r': c = '\r'; break;
case 'n': c = '\n'; break;
}
} else if (c == '%') {
int colnum = 0;
char *pos = ++fmt;
c = *fmt;
if (c == '\0')
semind_error(1, 0, "unexpected end of format string");
switch (c) {
case 'f': colnum = 0; goto print_string;
case 'l': colnum = 1; goto print_string;
case 'c': colnum = 2; goto print_string;
case 'C': colnum = 3; goto print_string;
case 'n': colnum = 4; goto print_string;
case 'm':
if (n) {
printf("%.*s", n, buf);
n = 0;
}
print_mode(argv[5]);
fmt++;
break;
case 'k':
if (n) {
printf("%.*s", n, buf);
n = 0;
}
printf("%c", atoi(argv[6]));
fmt++;
break;
case 's':
if (n) {
printf("%.*s", n, buf);
n = 0;
}
print_file_line(argv[0], atoi(argv[1]));
fmt++;
break;
print_string:
if (n) {
printf("%.*s", n, buf);
n = 0;
}
printf("%s", argv[colnum]);
fmt++;
break;
default:
break;
}
if (pos == fmt)
semind_error(1, 0, "invalid format specification: %%%c", c);
continue;
} else if (c == '\\') {
quote = 1;
fmt++;
continue;
}
if (n == sizeof(buf)) {
printf("%.*s", n, buf);
n = 0;
}
buf[n++] = c;
fmt++;
}
if (n)
printf("%.*s", n, buf);
printf("\n");
return 0;
}
static void command_search(int argc, char **argv)
{
char *sql;
char *dberr = NULL;
sqlite3_str *query = sqlite3_str_new(semind_db);
if (chdir(cwd) < 0)
semind_error(1, errno, "unable to change directory: %s", cwd);
if (query_appendf(query,
"SELECT"
" file.name,"
" semind.line,"
" semind.column,"
" semind.context,"
" semind.symbol,"
" semind.mode,"
" semind.kind "
"FROM semind, file "
"WHERE semind.file == file.id") < 0)
goto fail;
if (semind_search_kind) {
if (query_appendf(query, " AND semind.kind == %d", semind_search_kind) < 0)
goto fail;
}
if (semind_search_symbol) {
int ret;
if (query_appendf(query, " AND ") < 0)
goto fail;
if (strpbrk(semind_search_symbol, "*?[]"))
ret = query_appendf(query, "semind.symbol GLOB %Q", semind_search_symbol);
else
ret = query_appendf(query, "semind.symbol == %Q", semind_search_symbol);
if (ret < 0)
goto fail;
}
if (semind_search_modmask_defined) {
if (!semind_search_modmask) {
if (query_appendf(query, " AND semind.mode == %d", semind_search_modmask) < 0)
goto fail;
} else if (query_appendf(query, " AND (semind.mode & %d) != 0", semind_search_modmask) < 0)
goto fail;
}
if (semind_search_path) {
if (query_appendf(query, " AND file.name GLOB %Q", semind_search_path) < 0)
goto fail;
}
if (semind_search_by_location == EXPLAIN_LOCATION) {
if (query_appendf(query, " AND file.name == %Q", semind_search_filename) < 0)
goto fail;
if (semind_search_line &&
query_appendf(query, " AND semind.line == %d", semind_search_line) < 0)
goto fail;
if (semind_search_column &&
query_appendf(query, " AND semind.column == %d", semind_search_column) < 0)
goto fail;
} else if (semind_search_by_location == USAGE_BY_LOCATION) {
if (query_appendf(query, " AND semind.symbol IN (") < 0)
goto fail;
if (query_appendf(query,
"SELECT semind.symbol FROM semind, file WHERE"
" semind.file == file.id AND"
" file.name == %Q", semind_search_filename) < 0)
goto fail;
if (semind_search_line &&
query_appendf(query, " AND semind.line == %d", semind_search_line) < 0)
goto fail;
if (semind_search_column &&
query_appendf(query, " AND semind.column == %d", semind_search_column) < 0)
goto fail;
if (query_appendf(query, ")") < 0)
goto fail;
}
if (query_appendf(query, " ORDER BY file.name, semind.line, semind.column ASC", semind_search_path) < 0)
goto fail;
sql = sqlite3_str_value(query);
if (semind_verbose > 1)
message("SQL: %s", sql);
sqlite3_exec(semind_db, sql, search_query_callback, NULL, &dberr);
if (dberr)
semind_error(1, 0, "sql query failed: %s", dberr);
fail:
sql = sqlite3_str_finish(query);
sqlite3_free(sql);
if (semind_file_fd) {
fclose(semind_file_fd);
free(semind_file_name);
}
free(semind_line);
}
int main(int argc, char **argv)
{
static const struct command commands[] = {
{
.name = "add",
.dbflags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
.parse_cmdline = parse_cmdline_add,
.handler = command_add
},
{
.name = "rm",
.dbflags = SQLITE_OPEN_READWRITE,
.parse_cmdline = parse_cmdline_rm,
.handler = command_rm
},
{
.name = "search",
.dbflags = SQLITE_OPEN_READONLY,
.parse_cmdline = parse_cmdline_search,
.handler = command_search
},
{ .name = NULL },
};
const struct command *cmd;
if (!(progname = rindex(argv[0], '/')))
progname = argv[0];
else
progname++;
if (!realpath(".", cwd))
semind_error(1, errno, "unable to get current directory");
n_cwd = strlen(cwd);
parse_cmdline(argc, argv);
for (cmd = commands; cmd->name && strcmp(argv[optind], cmd->name); cmd++);
if (!cmd->name)
semind_error(1, 0, "unknown command: %s", argv[optind]);
optind++;
semind_command = cmd->name;
if (cmd->parse_cmdline)
cmd->parse_cmdline(argc, argv);
open_database(semind_dbfile, cmd->dbflags);
cmd->handler(argc - optind, argv + optind);
sqlite3_close(semind_db);
return 0;
}
sparse-0.6.4/show-parse.c 0000664 0000000 0000000 00000070231 14115310122 0015243 0 ustar 00root root 0000000 0000000 /*
* 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