|
|||||||||
PREV PACKAGE NEXT PACKAGE | FRAMES NO FRAMES |
CFG
representation of a program.
See:
Description
Class Summary | |
---|---|
Assembler | This class is the base class for classes that translate instructions into assembly language. |
BBIS | This class provides basic block instruction scheduling. |
Branch | This is the abstract class for all machine branch instructions. |
CommentMarker | This class is used to associate comments with instructions and is used for debugging backend code generators. |
DiffDisplacement | This class represents a displacement field in an instruction that is the difference between two displacements. |
Displacement | This class represents a displacement field in an instruction. |
DominanceFrontier | This class computes and manages dominance frontiers for a graph. |
Domination | This class computes the dominators and post dominators of nodes in a graph. |
FloatDisplacement | This is a simple displacement where the displacement is a known floating point value. |
Generator | This class is the base class for code generators. |
ICEstimator | This class is the base class for code size estimators. |
Instruction | This is the abstract class for all machine instructions including Markers. |
IntegerDisplacement | This is a simple displacement where the displacement value is known. |
Intrinsics | This class represents a target independent implementation for compiler intrinsics. |
Label | This class marks the position of a point branched to. |
LabelDisplacement | This class represents a displacement field in an instruction that represents a label. |
LineMarker | This class is used to associate source line numbers with instructions. |
Marker | Instances of this class are used to mark places in the instruction stream. |
Node | This is an abstract class which represents a node in a graph. |
OffsetDisplacement | This class represents a displacement field in an instruction that is offset from another displacement. |
QDRA | This class implements a quick and dirty register allocator. |
RegisterAllocator | This is the base class for all register allocators. |
RegisterSet | This is the base class for describing the register set of the machine. |
SpaceAllocation | This class represents statically allocated memory. |
Stabs | This class represents "stabs" debugging information. |
StackDisplacement | This class represents a displacement field in an instruction when the displacement refers to an offset on the stack. |
SymbolDisplacement | This class represents a displacement field in an instruction when the displacement refers to an offset that must be relocated by the loader. |
Enum Summary | |
---|---|
ResultMode | This enum specifies where and what the result is from compiling an expression. |
Generates assembly language output from the CFG
representation of a program.
The Scale compiler uses object-oriented programming techniques to make the implementation of code generators for different ISAs straight forward. Thus far, code generators for Sparc, Alpha, PowerPC, and Mips processors have been implemented.
A base class for the code generator contains the basic logic to
transform Scribble
CFG nodes into
instructions. Each derived code generator class implements virtual
methods that the base class uses to perform specific actions such as
transforming a real value to an integer value or loading a register
from memory. As the Scale compiler generates assembly code and uses
the native assembler to generate object modules, there is a base class
for generating the assembly code from which specific assembly code
generators must be derived.
Each ISA has different instructions. Each ISA-specific instruction
format is represented by a different class that is derived from the
same instruction base class
. So
that the base code generator class can manage the representation of
branch points, all branch instructions
are derived from the same class that is also derived from the base
instruction class. For example, the following is a simplified example
class hierarchy for the Alpha instructions:
Instruction <- MemoryInstruction <- LoadInstruction <- StoreInstruction <- LoadAddressInstruction <- BarriorInstruction <- FetchInstruction <- IntOpInstruction <- IntOpLitInstruction <- FltOpInstruction <- FltCvtInstruction <- Branch <- BranchInstruction <- JmpInstructionFor the Sparc V8 processor a more simplified example hierarchy is:
Instruction <- SparcInstruction <- LoadInstruction <- LoadLitInstruction <- StoreInstruction <- StoreLitInstruction <- SethiInstruction <- IntOpInstruction <- IntOpLitInstruction <- FltOpInstruction <- FltOp2Instruction <- FltCmpInstruction <- Branch <- SparcBranch <- BranchCCInstruction <- BranchInstruction <- CallInstruction <- JmplInstruction <- JmplLitInstructionVirtual methods are used to implement the methods required from the instruction class by the other parts of the backend. Register allocation, described below, is a good example of how these virtual methods are used.
Some numbers will illustrate how the object-oriented approach has worked. The generic base classes comprise some 3600 non-blank, non-comment lines of Java code. The Alpha specific code is 6600 non-blank, non-comment lines of Java code. The Sparc specific code is 10,800 non-blank, non-comment lines of Java code. The Mips specific code is 5900 non-blank, non-comment lines of Java code. The generic classes and the Alpha specific classes required approximately two man-months to implement. The Sparc specific classes also took two man-months. The Mips classes where written by an undergraduate in less than two months. The major complication of the Sparc implementation was designing and implementing multi-register values for the Sparc V8 processor and the large number of different instruction formats; the register-window design of the Sparc processor required only four lines of Java code.
Register Allocation Example
The first is the concept of a register set and is similar to the ideas
used in the Gnu C compiler. In Scale, registers are represented by
integers. The register set base
class
specifies operations on those integers. There is a different
derived class, that specifies particulars of the register set, defined
for each ISA. Registers are either real or virtual with the first n
registers representing the real registers of the ISA.
The second base class
represents the basics of register allocation. It includes the methods
needed to describe the register usage for a program and the live
register analysis required by every register allocation algorithm.
The Scale compiler uses the concept of an infinite register set while
generating the instructions. The register allocator derived classes
implement the algorithms needed to map these virtual registers to the
real registers of a specific ISA. These register allocator classes
are ISA independent.
The integer value representing a register is used as an index into an array where each array element describes the corresponding register. Each register has attributes that describe what type of values that register may hold. For virtual registers these attributes are used to specify if this register is part of a multi-register set. This can be used to specify that the value spans two or more contiguous registers (e.g., 64-bit integers for a 32-bit ISA) or that the value is made up of two separable values (e.g., complex values).
During the live analysis, instructions are linearized so that
each instruction can be represented, and accessed, by an integer
value. Each instruction is visited to generate the needed register
usage information. Each instruction class implements a method (specifyRegisterUsage
)
that the register allocator calls, passing itself in as an argument to
the method. Special, non-code-generating instructions (markers
) are inserted by the code generators to
specify register usage over subroutine calls.
The register allocator base class implements three methods that the instruction class's specifyRegisterUsage method calls. The first method is used to specify, by integer value, the real or virtual registers whose values the instruction requires. The second method specifies the registers whose values are generated by the instruction. The third method specifies the registers that are modified (clobbered) by an instruction. The result of this is a set of three bit-arrays indexed by instruction index and register index. Liveness is computed from this and the result array is transposed to produce an array of bit vectors indexed by the register's integer representation.
Register spilling is also ISA independent. Each machine dependent code generator implements three methods. The first method is used by the register allocator to obtain an opaque location specifier (usually on the stack) into which a register may be spilled. The second method is used by the register allocator to cause the code generator to insert a store of a register to a spill location and the third method is used to reload the register. The second and third methods are passed: the opaque location specifier, the register, and the place in the instruction stream to insert the operation. If any registers are spilled, the current allocation of virtual registers to real registers is discarded and register allocation is performed again.
-arch
command line parameter is used to specify the target architecture.
System.getProperty("os.arch")
is used.
scale.backend.arch.ArchGenerator.class
where arch
is the name of the architecture obtained as specified above. (Note
that Java class names must start with a capital letter but
architectures are specified in all lower-case letters.) For example,
on an Alpha®, the class scale.backend.alpha.AlphaGenerator
class
is loaded.
Scale must also be able to find a class name
scale.backend.arch.ArchMachine. This class specifies attributes
of the architecture, such as word size, that are needed prior to code
generation. For example, on an Alpha®, the
is loaded.
scale.backend.alpha.AlphaMachine
class
To add these two classes, and any others required, add a new directory
to the scale/backend directory whose name is the architecture name and
place the classes in it. (You can use the xyz
directory
to obtain template classes for the four required classes.)
The code generator class must be derived from the scale.backend.Generator
class and the machine
class must be derived from scale.common.Machine
class. Use the other code generators as a guide in
building a new one. Keep in mind that the design for the code
generators assumed a register load/operate/store (RISC) model;
building a code generator for a CISC-type architecture may prove to be
much more difficult.
If there are multiple variants on the same architecture (e.g., SparcV8
& SparcV9), modifications may be required to the scale.common.Machine setup()
method. You
may also add extensions as in -arch alpha/bwx
. These
extensions are passed to the scale.common.Machine
determineArchitecture()
method.
The classes defined for the architecture's instructions must be
derived from the scale.backend
Instruction
class. Any branch instruction must be derived from the
scale.backend Branch
class.
|
|||||||||
PREV PACKAGE NEXT PACKAGE | FRAMES NO FRAMES |