Added documentation about the back-end internal structure.
Creation of st/cli branch.
CLI is a framework that defines a platform independent format for executables and a run-time environment for the execution of applications. The framework has been been standardized by the European Computer Manufacturers Association (ECMA-335) and by the International Organization for Standardization (ISO/IEC 23271:2006). CLI executables are encoded in the Common Intermediate Language (CIL), a stack-based bytecode language. CLI framework is designed to support several programming languages with different abstraction levels, from object-oriented managed languages to low-level languages with no managed execution at all.
The purpose of this project is to develop a GCC back-end that produces CLI-compliant binaries. The initial focus is on C language (more precisely, C99); C++ is likely to be considered in the future, as well as any other language for which there is an interest for a CLI back-end.
The implementation currently resides in the st/cli branch.
Check out st/cli
branch following the instructions found in the
SVN documentation.
Being this a branch, the usual maintainer rules do not apply. The branch is being maintained by Roberto Costa. Checking-in into the branch is free, provided that the action was coordinated with the branch maintainer and that the usual contribution and testing rules are followed. The branch is still in heavy development and check ins into the mainline are not planned yet.
Unlike a typical GCC back-end, CLI back-end stops the compilation flow at the end of the middle-end passes and, without going through any RTL pass, it emits CIL bytecode from GIMPLE representation. As a matter of fact, RTL is not a convenient representation to emit CLI code, while GIMPLE is much more suited for this purpose.
CIL bytecode is much more high-level than a processor machine code. For instance, there is no such a concept of registers or of frame stack; instructions operate on an unbound set of local variables (which closely match the concept of local variables) and on elements on top of an evaluation stack. In addition, CIL bytecode is strongly typed and it requires high-level data type information that is not preserved across RTL.
Like existing GCC back-ends, CLI is truly seen as a target machine and, as such, it follows GCC policy about the organization of the back-end specific files.
Unfortunately, it is not feasible to define a single CLI target
machine. The reason is that, in dealing with languages with
unmanaged datas like C and C++, the size of pointers of the target
machine must be known at compile time.
Therefore, separate 32-bit and 64-bit CLI targets are defined,
namely cil32
and cil64
.
CLI binaries compiled for cil32
are not guaranteed to
work on 64-bit machines and vice-versa.
Current work is focusing on cil32
target, but the differences between the two are minimal.
Being cil32
the target machine, the machine model
description is located in files config/cil32/cil32.*
.
This is an overview of such a description:
cil32
target, it would similarly set to 64 for cil64
).
Natural modes for computations go up to 64 bits.packed
attribute).Though most GIMPLE tree codes closely match what is representable in CIL, some simply do not. Those codes could still be expressed in CIL bytecodes by a CIL-emission pass; however, it would be much more difficult and complicated to perform the required transformations at CIL emission time (i.e.: those that involve generating new local temporary variables, modifications in the control-flow graph or in types...), than directly on GIMPLE expressions.
Pass simpcil
(file
config/cil32/tree-simp-cil.c
) is in charge of performing
such transformations.
The input is any code in GIMPLE form; the outcome is still valid
GIMPLE, it just contains only constructs for which CIL emission is
straightforward.
Such a constrained GIMPLE format is referred as "CIL simplified"
GIMPLE throughout this documentation.
The pass is currently performed just once, after leaving SSA form and
immediately before the CIL emission.
This is not a constraint; the only requirement is that the
CIL emission is immediately preceded by a run of simpcil
.
simpcil
pass is designed to be idempotent and it is perfectly
fine to insert additional previous runs in the compilation flow.
Given its current position in the list of passes,
simpcil
does not yet support SSA form (though planned).
This is a non-exhaustive list of simpcil
transformations:
RESULT_DECL
nodes.
CIL doesn't treat the value
returned by a function in any special way: if it has to be
temporarily stored, this must happen in a local.
A new local variable is generated and each RESULT_DECL
node is transformed into a VAR_DECL
of that variable.LROTATE_EXPR
and
RROTATE_EXPR
nodes.
In CIL there no are opcodes for rotation and they have
to be emulated through shifts and bit operations.
A previous expansion may generate better code (i.e.:
it may fold constants) or trigger further optimizations.ABS_EXPR
nodes (in case of
-mexpand-abs
option), of MAX_EXPR
and
MIN_EXPR
nodes (in case of -mexpand-minmax
option) and of COND_EXPR
nodes used as expressions
(not statements).
The expansion requires changes to the control-flow graph.LTGT_EXPR
, UNEQ_EXPR
,
UNLE_EXPR
and UNGE_EXPR
nodes.
CIL instruction set has some support for comparisons,
but it is not orthogonal. Whenever a comparison is difficult to be
translated in CIL, it is expanded.SWITCH_EXPR
, when it is not profitable
to have a switch table (heuristic decision is based on case density).
CIL emission pass always emits a SWITCH_EXPR
to a
CIL switch opcode. When a low case density makes compare trees
preferable, the SWITCH_EXPR
is expanded; otherwise the
SWITCH_EXPR
is not modified.
The expansion requires changes to the control-flow graph.COMPONENT_REF
nodes operating on
bit-fields and of BIT_FIELD_REF
nodes.
CIL has no direct support for bit-field access; hence,
equivalent code that extracts the bit pattern and applies the
appropriate bit mask is generated.
Memory access is performed by using INDIRECT_REF
nodes.
Beware that such nodes on the left-hand side of an
assignment also requires a load from memory; from the memory
access point of view, the operation cannot be made atomic.TARGET_MEM_REF nodes
.
Emission of such nodes is not difficult;
however, a previous expansion may trigger further optimizations
(since there is no similar construct in CIL bytecodes).ARRAY_REF
nodes with non-zero indexes
into ARRAY_REF
with zero indexes.
CIL emission of such nodes is not difficult;
however, a previous expansion may generate better code (i.e.:
it may fold constants) or trigger further optimizations
(CIL arrays cannot be used for C-style arrays).
Remark that such a simplification must keep ARRAY_REF
s,
they cannot be replaced by INDIRECT_REF
nodes in order
not to break strict aliasing.CONSTRUCTOR
nodes used as right-hand
sides of INIT_EXPR
and MODIFY_EXPR
nodes.
Such CONSTRUCTOR
nodes must be implemented in CIL
bytecode through a sequence of finer grain initializations.
Hence, initializer statements containing CONSTRUCTOR
nodes
are expanded into an equivalent list of initializer statements,
with no more CONSTRUCTOR
nodes.
Pass cil
(file config/cil32/gen-cil.c
)
receives a CIL-simplified GIMPLE form as input and it produces
a CLI assembly file as output.
It is the final pass of the compilation flow.
Before the proper emission, cil
currently merges GIMPLE
expressions in the attempt to eliminate local variables.
The elimination of such variables has positive effects on the
generated code, both on performance and code size (each of such an
useless local variable ends up in an avoidable pair of
stloc
and ldloc
CIL opcodes).
The resulting code is no longer in valid GIMPLE form; this is fine
because the code stays in this form only within the pass.
This is conceptually (perhaps not only conceptually) similar to what
done by the out-of-ssa
pass; out-of-ssa
may
even be more powerful in doing this, since it operates in SSA form.
It may be interesting to move simpcil
pass before
out-of-ssa
and to avoid any variable elimination in
cil
.
To be evaluated.
Here is an overview of how cil
pass handles some of
GIMPLE constructs. Many of them are omitted, for which the emission is
straightforward.
<Module>
.VAR_DECL
nodes are emitted as CIL
locals, global-scope VAR_DECL
nodes as static fields of
<Module>
.INTEGER_TYPE
s and REAL_TYPE
s are
translated into their obvious equivalent CIL scalar types.
BOOLEAN_TYPE
s are translated as CIL
int8
.
POINTER_TYPE
s are translated as CIL native
int
.RECORD_TYPE
,
UNION_TYPE
, ARRAY_TYPE
and
ENUMERAL_TYPE
are emitted as valuetypes with explicit
layout.
Remark that GIMPLE ARRAY_TYPE
nodes cannot be emitted
as CIL arrays (which are managed arrays, a specific kind of objects).
Explicit layout is necessary because layout of structures and unions
is already done when code is in GIMPLE form; CIL declarations have to
match the size of such data structures.INDIRECT_REF
and
ARRAY_REF
nodes are emitted as indirect memory
accesses.
Remark that CIL-simplified GIMPLE only allows ARRAY_REF
nodes with zero offset.COMPONENT_REF
nodes are emitted as field accesses.Please send FSF & GNU inquiries & questions to gnu@gnu.org. There are also other ways to contact the FSF.
These pages are maintained by the GCC team.
For questions related to the use of GCC, please consult these web pages and the GCC manuals. If that fails, the gcc-help@gcc.gnu.org mailing list might help.Copyright (C) Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA.
Verbatim copying and distribution of this entire article is permitted in any medium, provided this notice is preserved.
Last modified 2007-01-10 |
|