|
UVa CS |
| Intro | RTL Creation Interface | Assembly Language Interface | VPO Code Generation Interface |
The VPO input is a sequence of function definitions which correspond to the function definitions in the source language compilation unit. The input for VPO is organized very much like an assembly-language program, consisting of machine instructions interspersed with informational directives and pseudo-operations. Some directives and pseudo-operations are relevant to VPO, while others are relevant to the target assembler. The instructions are represented as Register Transfer Lists (RTLs). VPO performs transformations on the RTLs to improve the code. The transformed RTLs are converted to target assembly code and written to the target assembly file.
To generate input for VPO, a code expander must use three interfaces. The executable instructions are built using the RTL Creation Interface. [In many cases, a compiler writer can call automatically generated procedures to create suitable RTLs. Such procedures, when available, should be documented in the Processor Supplement.] The RTLs are formatted and written to the VPO input file by the VPO Input Generation Interface (VPOi). The VPOi interface is also used for generating informational directives needed by VPO. Target assembly-language directives are generated using the Assembly Language Generation Interface.
The interface defines types, values, and functions. Tables [->] and [->] summarize the types and functions.
<vpoi.h>= <type and value definitions> <prototypes of interface functions>
| Type | Abbreviation Meaning | |
Rtl_ty_rtl | rtl | A register transfer list (RTL), or effect. |
Rtl_ty_loc | loc | A location node in an RTL. |
Rtl_ty_storage | storage | A character representing a storage space (registers, memory, etc.). |
VPOi_ty_locSet | locSet | A set of locations. |
| Miscellaneous | |
VPOi_locSetBuild(loc loc1, ...) | Create a set of locations. |
VPOi_locSetAdd(locSet locSet, loc loc) | Add to a set of locations. |
VPOi_begin(const char *target, int argc, char **argv) | Initialize VPO. |
VPOi_end(void) | Finalize VPO. |
VPOi_parameterListSize(int bytes) | Reserve space for argument-build area. |
VPOi_sourceFile(char *filename) | Show source file. |
VPOi_sourceCoordinate(int line, int col) | Show source location. |
| Function definition | |
VPOi_functionDefine(AsmSymbol sym, locSet save) | Start defining a new function. |
VPOi_functionEnd(void) | Finish the definition of the current function. |
VPOi_globalDeclare(AsmSymbol sym, storage regspace) | Declare a global variable. |
VPOi_localVariableDeclare(AsmSymbol sym, storage regspace, int size, int volat, int blockNumber) | Declare a local variable. |
VPOi_parameterDeclare(AsmSymbol sym, storage regspace, int size, int volat, loc loc) | Declare a parameter. |
VPOi_rtl(rtl rtl, locSet dead) | Emit an instruction. |
VPOi_functionCall(rtl rtl, locSet dead, locSet used, locSet defined, locSet callerSave, locSet killed) | Emit a call instruction. |
VPOi_functionLeave(rtl rtl, locSet live) | Emit a return or tail-call instruction. |
Functions defined in this interface [*]
Locations have type Rtl_ty_loc, as defined in the
RTL Creation Interface.
A location is specified using
the function Rtl_location(space, addr) where space identifies
a storage space and addr identifies a location in that space.
The space argument is a character whose
legal values are target-dependent; the Processor Supplement enumerates
the legal spaces.
The addr argument is an expression node.
The permissible values of addr are determined by the target
machine, but typical targets permit integer constants in register
spaces and simple addressing expressions in the memory space.
The Processor Supplement may identify storage spaces for temporaries (VPO pseudo-registers), which VPO's register allocator maps to true hardware locations. Temporary spaces and the number of locations therein are target-dependent, but typical temporary spaces offer about 500 locations. Wherever possible, temporaries should be used instead of actual hardware registers. A hardware register should only appear in a RTL that requires that particular hardware register. For example, the calling convention on the target may specify a particular hardware register to hold the function return value.
This table shows some example code used to create locations on the SPARC. Such code is target-dependent and may not be meaningful on all targets.
| Hardware register 8 | Rtl_constLoc('r', 8) |
| Temporary register 0 | Rtl_constLoc('t', 0) |
| Stack memory | Rtl_location ('m', Rtl_fetch (Rtl_constLoc ('r', 14), 32)) |
Note that to use register 14 as an address, we must fetch its contents.
VPO defines three standard special locations.
<type and value definitions>= (<-U) [D->] extern Rtl_ty_loc VPOi_loc_PC; extern Rtl_ty_loc VPOi_loc_ST; extern Rtl_ty_loc VPOi_loc_RT;
DefinesVPOi_loc_PC,VPOi_loc_RT,VPOi_loc_ST(links are to index).
Additional target-dependent special locations of type
Rtl_ty_loc may be defined for different targets.
For example, on the SPARC, VPOi_loc_IC represents the integer
condition codes and is used to hold the
result of comparing two integers.
VPOi_ty_locSet.
<type and value definitions>+= (<-U) [<-D->] typedef struct VPOi_st_locSet *VPOi_ty_locSet;
DefinesVPOi_ty_locSet(links are to index).
The same RTL location node may appear in multiple location sets. The following functions are provided for building location sets.
<prototypes of interface functions>= (<-U) [D->] VPOi_ty_locSet VPOi_locSetBuild(Rtl_ty_loc loc1, ...);
DefinesVPOi_locSetBuild(links are to index).
Builds a location set consisting of locations in the parameter
list. All parameters must be of type Rtl_ty_loc and the
parameter list must be null terminated.
<prototypes of interface functions>+= (<-U) [<-D->]
VPOi_ty_locSet VPOi_locSetAdd(VPOi_ty_locSet locSet,
Rtl_ty_loc loc);
DefinesVPOi_locSetAdd(links are to index).
Creates a new set which is the result of adding location loc to
location set locSet.
If locSet is null, the result is a
location set with loc as the only member.
In the RTL interface, relocatable addresses are type
Rtl_ty_relAddr, which is a pointer to a struct defined in the
Assembly Language Interface. (In the Assembly Language Interface,
a relocatable address is type AsmRelAddr, which is the
same type as Rtl_ty_relAddr.)
The Assembly Language Interface provides functions for declaring
symbols in the target assembly language. The VPOi interface has
functions for making the assembly language symbols understandable
to VPO. Before using a relocatable address in a RTL, the
relocatable address must be declared in the Assembly Language
Interface using the import(), export(), local(), or
common() functions. If the relocatable address is not a
label, it must also be declared in the VPOi interface
with VPOi_globalDeclare(), or VPOi_localVariableDeclare(),
or VPOi_parameterDeclare(), which are discussed below.
Rtl_ty_rtl using the
RTL Creation Interface.
For example, the following constructs a RTL that assigns the
constant zero to register type 't', number zero:
Rtl_assign (Rtl_constLoc ('t', 0), 32, Rtl_int (0));
See the RTL interface for details.
Currently the VPOi interface converts RTL trees to strings that VPO processes. In a future release, VPO will process the trees directly. This change should not introduce any incompatibility into this interface.
<type and value definitions>+= (<-U) [<-D] extern Assembler VPOi_asm;
DefinesVPOi_asm(links are to index).
VPOi_asm is a pointer to a struct whose members
are pointers to functions that emit various fragments of
target assembly code. For example, the call
VPOi_asm -> section ("text") emits target assembly code
to switch to the ``text'' segment. See the Assembly Language
Interface for details.
VPOi_begin()
and the last call must be to VPOi_end().
These functions perform initialization and cleanup.
The target parameter to VPOi_begin() identifies the target
machine and operating system.
The identifiers are documented in the Processor Supplement.
<prototypes of interface functions>+= (<-U) [<-D->] void VPOi_begin(const char *target, int argc, const char **argv); void VPOi_end(void);
DefinesVPOi_begin,VPOi_end(links are to index).
The argc and argv parameters provide a
mechanism for passing configuration arguments to the interface
(currently unused).
VPOi_begin initializes VPOi_asm and calls VPOi_asm->progbeg.
VPOi_end calls VPOi_asm->progend.
<prototypes of interface functions>+= (<-U) [<-D->] void VPOi_rtl(Rtl_ty_rtl rtl, VPOi_ty_locSet dead);
DefinesVPOi_rtl(links are to index).
The rtl argument is constructed using the functions
in the RTL Creation Interface. The dead argument is a
set of ``dead'' locations constructed using VPOi_locSetBuild()
or VPOi_locSetAdd().
It is a checked runtime error to attempt to emit an instruction using
VPOi_asm->emit_instruction.
Putting a location in the dead set is tantamount to asserting that
every path from this point to a use of that location is cut by a
definition of that location.
For best performance,
a location should be declared ``dead'' if a definition of
the location reaches this point, but
the value in the location
is no longer needed by the (unimproved) input to VPO.
VPO generates correct code even when ``dead'' registers are omitted
(i.e., by using the empty set as dead), but omitting such
registers may make VPO run more
slowly and generate code of poorer quality.
It is an unchecked run-time error to put a location in dead
if the location is not in fact dead, i.e., if a definition at this
location could reach a use.
Such an error could cause VPO to generate incorrect code.
VPOi_asm -> import(), VPOi_asm -> export(),
VPOi_asm -> common(), VPOi_asm -> local(), or
VPOi_asm -> offset(). All of these return an object of type
AsmSymbol. The second step is to introduce the symbol to VPO using the
functions described in this section.
The AsmSymbol object returned by the Assembly Language
interface functions contains a member named relAddr
of type AsmRelAddr, which is the same type as
Rtl_ty_relAddr. This is a relocatable address, which serves
as a ``handle'' for the object being declared.
In the RTL creation interface, the function Rtl_relAddr()
accepts a parameter of type Rtl_ty_relAddr to build
an expression that denotes the address of a
declared object.
It is a unchecked run-time error to refer to
a relocatable address obtained from the Assembly Language
Interface that has not also been declared to VPO
using the functions that follow.
As an example, consider to declare printf and branch to it, one
might execute
<example for branching to printf>=
{ AsmSymbol sym = VPOi_asm->import("printf");
VPOi_globalDeclare (sym, 'r');
VPOi_rtl(Rtl_assign(VPOi_loc_PC, 32, Rtl_relAddr(sym->relAddr)), NULL);
}
A real compiler would, of course, call printf trather than
branching to it.
<prototypes of interface functions>+= (<-U) [<-D->] void VPOi_globalDeclare(AsmSymbol symbol, Rtl_ty_storage regspace);
DefinesVPOi_globalDeclare(links are to index).
The symbol argument is an AsmSymbol obtained from
the Assembly Language Interface.
If the symbol being declared is a function or
a global variable that is an aggregate (array, struct, etc.),
then regspace is
the type of register needed to store the address of the symbol.
If the symbol is a global variable that is not an aggregate
(simple type or pointer type),
then regspace is the type of register needed to store
the value of the variable.
After execution of VPOi_globalDeclare(sym, s), sym->relAddr
stands for the address in memory at which global variable sym is
located.
Currently VPO supports at most 4096 global symbols in a compilation unit. Exceeding this limit results in a checked run-time error.
<prototypes of interface functions>+= (<-U) [<-D->]
void VPOi_localVariableDeclare(AsmSymbol symbol,
Rtl_ty_storage regspace, int size, int volat,
int blockNumber);
DefinesVPOi_localVariableDeclare(links are to index).
The symbol argument is the AsmSymbol obtained from
the Assembly Language Interface.
The regspace argument indicates what kind of register
should be used for the variable or its address, as in
VPOi_globalVariableDeclare().
The size argument is the size of the variable in bytes.
If volat is non-zero, it means that the variable is
volatile and that VPO should not put it in a register.
Implications for CSE?
As explained below, the blockNumber parameter uniquely identifies
the innermost block
containing the variable's declaration.
This number helps VPO flatten nested scopes without causing name collisions.
After execution of VPOi_localVariableDeclare(sym, ...), sym->relAddr
stands for the offset of the variable from the pointer VPOi_loc_ST.
For example, on the SPARC, to declare a local variable i and load
its value into
$t[0], one might execute the following code.
<local-variable example>=
{ AsmSymbol isym = VPOi_asm->local(".1_i");
VPOi_localVariableDeclare (isym, 'r', 4, 0, 2);
VPOi_rtl(Rtl_assign(
Rtl_constLoc('t', 0), 32,
Rtl_fetch(Rtl_location('m',
Rtl_binary (Rtl_op_add, Rtl_fetch(VPOi_loc_ST, 32),
Rtl_relAddr(isym->relAddr))))));
}
Note that this example is valid only if the offset in
isym->relAddr is small enough to fit in the addressing mode.
Safer code would load the offset into another temporary and do the
address arithmetic into a third temporary, letting VPO find the more
efficient code if possible.
Both function parameters and local variables have block numbers.
VPO reserves block 1 for function parameters.
Block 2 is reserved for
the outermost block of a function definition, and you should use
block 2 for the blockNumber of local variables declared at the
outermost scope of a function.
Any source language block that contains local variable declarations and is enclosed directly or indirectly
by block 2 should be assigned a block number greater than 2.
Block number is not nesting depth. Block numbers within a function must be unique; in particular, different blocks at the same nesting depth must have different block numbers. We recommend using a depth-first numbering of blocks.
<prototypes of interface functions>+= (<-U) [<-D->]
void VPOi_parameterDeclare(AsmSymbol symbol,
Rtl_ty_storage regspace, int size, int volat, Rtl_ty_loc loc);
DefinesVPOi_parameterDeclare(links are to index).
The symbol, regspace, size, and volat arguments
are the same as for the function VPOi_localVariableDeclare().
The loc argument is a RTL location node that specifies
the location of the parameter in memory.
For now this must be a memory location whose address
is a constant offset from the stack pointer, e.g.,
Therefore, on machines that pass some parameters in registers, you must lie to VPO about their locations---VPO will rewrite parameter references into the correct locations according to the rules of the calling convention. [Note, however, that you must not lie to VPO when passing parameters at a call site---as a caller, you must use the correct locations.] A future version of VPO will lift the restriction and require you to identify the actual locations in which the parameters will be passed. (It will also provide some support for identifying the correct location for each parameter, which is tricky under some conventions.)Rtl_location('m',
Rtl_binary(Rtl_op_add, Rtl_fetch(Rtl_loc_ST, 32), Rtl_int(offset)));
After execution of VPOi_parameterDeclare(sym, ...), what
does the address sym->relAddr
stand for ???
It seems to stand for an offset from a machine-dependent register (on
the SPARC, the frame pointer).
Perhaps this interface would be simplified if the declaration
functions simply returned the location of the declared variable?
Currently VPO supports at most 4096 local variables (including function parameters) in each function definition. Exceeding this limit results in a checked run-time error.
Support for labels is fully contained in the Assembly Language interface and the RTL creation interface, but some discussion of how these interfaces create and refer to labels is appropriate.
Labels are declared and defined using the Assembly Language
Interface.
They are not introduced directly to VPO; instead, VPO requires
that labels use a special, target-dependent naming
convention, which is given in the Processor Supplement.
For example, on the SPARC, all label names
must be of the form .Ln where n is a digit string
that does not begin with zero.
Before a label can be used in an RTL (such as the target
of a jump) it must be declared with VPOi_asm -> local(), which
accepts the (conventional) label name and returns a symbol.
The relocatable address of that symbol can then be passed to Rtl_relAddr().
Before the end of the function, each label must be bound to a
particular location by calling VPOi_asm -> define_symbol_here().
As an example, this hypothetical code might be used to
generate code for an if statement.
<control-flow example>=
char *newlabel(void) {
static int n = 0;
char buf[32];
sprintf(buf, ".L%d", ++n);
return strsave(buf);
}
void emit_if(Expr condition, Statement t, Statement e) {
Rtl_ty_loc tmp = newtmp();
AsmSymbol l1 = VPOi_asm->local(newlabel());
AsmSymbol l2 = VPOi_asm->local(newlabel());
emit_conditional_branch(negate(condition), l1);
emit_stmt(t);
VPOi_rtl(Rtl_assign(VPOi_PC, 32, VPOi_relAddr(l2->relAddr))); /* goto l2 */
VPOi_asm->define_symbol_here(l1);
emit_stmt(e);
VPOi_asm->define_symbol_here(l2);
}
Definesemit_if,newlabel(links are to index).
VPO lays out the storage for parameter lists that are passed to functions and emits code for saving and restoring registers across function calls. To accomplish this VPO must know when a function definition begins and ends, and must also know about all function calls and returns.
The following function announces the beginning of a function definition in the VPO input.
<prototypes of interface functions>+= (<-U) [<-D->] void VPOi_functionDefine(AsmSymbol symbol, VPOi_ty_locSet save);
DefinesVPOi_functionDefine(links are to index).
The symbol argument is an AsmSymbol obtained by
declaring the function name with VPOi_asm -> export() or
VPOi_asm -> local() (see the Assembly Language interface).
The save argument is a list of hardware registers that this
function must save, i.e., callee-save registers. This argument
should be null on targets that do not use callee save.
It is also necessary to call
VPOi_asm -> define_symbol_here() to label the start of the
function. Before any call to this function can be generated,
(including a recursive call) the function must be declared with
VPOi_globalDeclare().
The following function indicates the end of a function definition in the VPO input.
<prototypes of interface functions>+= (<-U) [<-D->] void VPOi_functionEnd(void);
DefinesVPOi_functionEnd(links are to index).
The function leave directive announces that control leaves the current function and does not come back. This announces a function return or possibly a tail call.
<prototypes of interface functions>+= (<-U) [<-D->] void VPOi_functionLeave(Rtl_ty_rtl rtl, VPOi_ty_locSet live);
DefinesVPOi_functionLeave(links are to index).
The rtl argument is the RTL that transfers control out
of the function.
For now, rtl must be an assignment of the fetched contents
of Rtl_loc_RT to the program counter Rtl_loc_PC:
Rtl_assign (Rtl_loc_PC, 32, Rtl_fetch (Rtl_loc_RT, 32));
The live argument is a (possibly null) list of
hardware registers that are ``live'', i.e., the register(s)
for storing the function return value. VPO automatically
inserts code to restore any callee-save registers.
The function call directive announces a function call to VPO.
<prototypes of interface functions>+= (<-U) [<-D->] void VPOi_functionCall( Rtl_ty_rtl rtl, /* code for the call */ VPOi_ty_locSet dead, /* temps not needed after the call */ VPOi_ty_locSet used, /* parameters used by callee */ VPOi_ty_locSet defined, /* return values defined by callee */ VPOi_ty_locSet callerSave, /* locations written by callee */ VPOi_ty_locSet killed /* locations read, then written by callee */ );
DefinesVPOi_functionCall(links are to index).
The rtl argument is the RTL that transfers control to the
called function. Control is expected to return to the
succeeding instruction.
For now, rtl must be an assignment of the address of the
called function to the program counter VPOi_loc_PC; future
versions of VPO will support the proper machine-dependent call instructions.
The dead parameter is a list of locations that are dead
after the rtl executes, as in VPOi_rtl().
The remaining arguments specify the expected behavior of the caller and callee with respect to hardware registers.
usedused list consists of the registers
(if any) that contain parameters for this function call.
definedcallerSavekilledcallerSave is the same for all function calls.
Likewise, killed is usually the same for all function calls.
The used list depends on the number and types of the parameters
being passed and the defined list depends on the type of
the return value, if any.
Table [->] repeats this information in terms of
definitions and uses.
Note that used, defined, killed, and callerSave
are not necessarily disjoint, so a particular hardware register may
be a member of multiple sets.
| U_e | U_r | D_e | SPARC example Description | ||
| used | D_r | ? | %o0 | Register defined in caller and used in the callee. May be redefined in callee. Typically identifies parameters. | |
| defined | D_e | X | %o0 | Register defined in callee and used in caller. Typically identifies return results. | |
| killed | D_r? | X | %o3 | A register that is used in the callee and that the callee destroys. Definitions of such registers preceding the callee are deemed to reach uses in the callee, but not to reach uses in the caller (after the call). | |
| callerSave | D_r | X | %f0 | A register that the callee destroys but does not use. Definitions of such registers preceding the callee are deemed to reach uses in the caller (after the call). VPO is therefore required to save and restore any such registers that are live at the call. | |
The table above uses these abbreviations:
The columns labeled U_r and U_e show which definitions (if any), reach uses in the caller and callee. The column labeled D_e shows whether there is a definition in the callee.
D_r A definition in the caller, before the call. D_e A definition in the callee. U_r A use in the caller, after the call. U_e A use in the callee.
Definitions and uses at call sites [*]
VPOi_parameterListSize(n) that there is a call passing n bytes
of parameters.
<prototypes of interface functions>+= (<-U) [<-D->] void VPOi_parameterListSize(int bytes);
DefinesVPOi_parameterListSize(links are to index).
When VPO calculates the size of the argument-build area, it uses the
largest parameter list size directive in
the function's definition. VPOi_parameterListSize
may be called before or after each
VPOi_functionCall(),
Or, if the front end knows the size of the largest call, it can simply
call VPOi_parameterListSize once with that size.
The exact location of the argument-build area, and the addressing modes used to put arguments there, are target-dependent, and they are specified in the Processor Supplement. Typical calling conventions put the argument-build area at the bottom of the stack frame, pointed to by the stack pointer.
VPO's common-subexpression eliminator, called the cacher, maintains a table mapping locations l to expressions e. VPO computes a version of the table for each program point. The semantics of the table are that for every pair l |-->e, the value contained in location l is equal to e. In particular, if an RTL is emitted at that point, vpo may substitute fetch l for e anywhere that e is free in the RTL. [Subject to the usual restriction that the resulting RTL must be representable in a single instruction on the target machine] . VPO maintains an additional invariant, which is that no l appears free in any e in the table. More precisely, if l |-->e and l' |-->e' are in the table, then l is not free in e'.
When an RTL l :=e is emitted, the cacher updates the table as follows:
In the presence of aliasing, front ends may have to trash locations explicitly. For example, if a store through a pointer could be aliased to a value of a local variable, the front end should trash the location holding that variable. Users can cause deletions from the table by calling
<functions>= [D->] extern void trash(LocationSet locs);
Definestrash(links are to index).
which trashes every l in the set locs.
Users can empty the table completely by calling
<functions>+= [<-D] extern void trashAll(void);
DefinestrashAll(links are to index).
As part of a call directive, VPO automatically trashes all those register locations that are listed as volatile across the call.
A future version of VPO may provide a trash directive that trashes all locations satisfying a given predicate.
The source file name directive gives the name of the source file that was compiled to produce the code being optimized. This directive allows error messages to name the offending source file. It sets the ``current source file name,'' which is in effect from the current point up to the next source file directective.
<prototypes of interface functions>+= (<-U) [<-D->] void VPOi_sourceFile(char *filename);
DefinesVPOi_sourceFile(links are to index).
The source coordinate directive gives the line number and column of the current source file that corresponds to the generated code. Like the function name and source file name, this directive allows error messages to name the offending source file location. In particular, this directive asserts that every instruction and declaration from this point up to the next source coordinate directive comes from a source-language construct that begins at the specified source-code location.
<prototypes of interface functions>+= (<-U) [<-D] void VPOi_sourceCoordinate(int linenum, int column);
DefinesVPOi_sourceCoordinate(links are to index).
int b[10], c[10];
int f1(a)
double a;
{
int i, k;
for (i = 0; i < 10; i++) {
a = b[i] + c[i];
k = f2(i, a);
}
return k;
}
In this example, the target architecture is the SPARC.
Calls to the RTL interface and Assembly Language interface
are also shown.
Note that binary encodings in the raw VPO input file have been
modified to be printable.
--- #1 --- #2
| C code | |
VPOi_begin(); | Mbwrfd |
VPOi_asm->progbeg(); | |
VPOi_asm->section("data"); | - .seg "data" |
bsym = VPOi_asm->common("b",40,4,0); | - .common b,40,4 |
csym = VPOi_asm->common("c",40,4,0); | - .common c,40,4 |
VPOi_asm->section("text"); | - .seg "text" |
VPOi_asm->align(8); | - .align 8 |
f1sym = VPOi_asm->export("f1"); | - .global f1 |
VPOi_asm->define_symbol_here(f1sym); | -f1: |
VPOi_functionDefine(f1sym, 0); | ff1 |
r14 = Rtl_constLoc('r', 14); | |
right = Rtl_binary (Rtl_op_add, Rtl_fetch(r14, 32), Rtl_int(68)); | |
loc = Rtl_location('m', right); | |
asym = VPOi_asm->local(".1_a"); | |
VPOi_parameterDeclare (asym, 'd', 8, 0, loc); | d.1_a LOC[0] 1 4 8 68 |
isym = VPOi_asm->local(".1_i"); | |
VPOi_localVariableDeclare (isym, 'r', 4, 0, 2); | d.1_i LOC[1] 2 2 4 |
ksym = VPOi_asm->local(".1_k"); | |
VPOi_localVariableDeclare (ksym, 'r', 4, 0, 2); | d.1_k LOC[2] 2 2 4 |
t0 = Rtl_constLoc('t', 0); | |
rtl = Rtl_assign(t0, 32, Rtl_int(0)); | |
VPOi_rtl(rtl, 0); | +r[32]=0 |
t1 = Rtl_constLoc('t', 1); | |
right = Rtl_binary (Rtl_op_add, Rtl_fetch(r14, 32), Rtl_relAddr(isym -> relAddr)); | |
rtl = Rtl_assign(t1, 32, right); | |
VPOi_rtl(rtl, 0); | +r[33]=r[14]+LOC[1] |
loc = Rtl_location ('m', Rtl_fetch(t1, 32)); | |
rtl = Rtl_assign(loc, 32, Rtl_fetch(t0, 32)); | |
dead = VPOi_locSetBuild(t0, t1, 0); | |
VPOi_rtl(rtl, dead); | +R[r[33]]=r[32] r[33]r[32] |
loophead = VPOi_asm->local(".L18"); | |
VPOi_asm->define_symbol_here (loophead); | L18 |
| (see above) | +r[32]=r[14]+LOC[1] |
loc = Rtl_location('m', Rtl_fetch(t0, 32)); | |
rtl = Rtl_assign (t1, 32, Rtl_fetch(loc, 32)); | |
dead = VPOi_locSetBuild(t0, 0); | |
VPOi_rtl(rtl, dead); | +r[33]=R[r[32]] r[32] |
| (see above) | +r[34]=2 |
right = Rtl_binary(Rtl_op_lshift, Rtl_fetch(t1, 32), Rtl_fetch(t2, 32)); | |
rtl = Rtl_assign(t1, 32, right); | |
dead = VPOi_locSetBuild(t2, 0); | |
VPOi_rtl(rtl, dead); | +r[33]=r[33]{r[34] r[34] |
VPOi_globalDeclare(bsym, 'r'); | db GLO[0] 0 2 |
right = Rtl_binary(Rtl_op_and, Rtl_relAddr(bsym -> relAddr), Rtl_uint(0xfffffc00)); | |
rtl = Rtl_assign(t3, 32, right); | |
VPOi_rtl(rtl, 0); | +r[35]=HI[GLO[0]] |
right = Rtl_binary(Rtl_op_and, Rtl_relAddr(bsym -> relAddr), Rtl_uint(0x3ff)); | |
right = Rtl_binary(Rtl_op_add, Rtl_fetch(t3, 32), right); | |
rtl = Rtl_assign(t3, 32, right); | |
VPOi_rtl(rtl, 0); | +r[35]=r[35]+LO[GLO[0]] |
right = Rtl_binary(Rtl_op_add, Rtl_fetch(t1, 32), Rtl_fetch(t3, 32)); | |
rtl = Rtl_assign(t1, 32, right); | |
dead = VPOi_locSetBuild(t3, 0); | |
VPOi_rtl(rtl, dead); | +r[33]=r[33]+r[35] r[35] |
| (see above) | +r[36]=R[r[33]] r[33] |
| +r[37]=r[14]+LOC[1] | |
| +r[38]=R[r[37]] r[37] | |
| +r[39]=2 | |
| +r[38]=r[38]{r[39] r[39] | |
| dc GLO[1] 0 2 | |
| +r[40]=HI[GLO[1]] | |
| +r[40]=r[40]+LO[GLO[1]] | |
| +r[38]=r[38]+r[40] r[40] | |
| +r[41]=R[r[38]] r[38] | |
| +r[36]=r[36]+r[41] r[41] | |
dxsym = VPOi_asm->local("dxfer_1"); | |
VPOi_localVariableDeclare(dxsym, 'f', 4, 1, 2); | dxfer_1 LOC[3] 2 3 4 1 |
right = Rtl_binary(Rtl_op_add, Rtl_fetch(r14, 32), Rtl_relAddr(dxsym -> relAddr)); | |
loc = Rtl_location('m', right); | |
rtl = Rtl_assign(loc, 32, Rtl_fetch(t4, 32)); | |
dead = VPOi_locSetBuild(t4, 0); | |
VPOi_rtl(rtl, dead); | +R[r[14]+LOC[3]]=r[36] r[36] |
VPOi_trash(loc, 32); | tR[r[14]+LOC[3]] |
x10 = Rtl_constLoc('x', 10); | |
rtl = Rtl_assign(x10, 32, Rtl_fetch(loc, 32)); | |
VPOi_rtl(rtl, 0); | +f[42]=F[r[14]+LOC[3]] |
y11 = Rtl_constloc('y', 11); | |
right = Rtl_unary(Rtl_op_cvtIF, Rtl_fetch(x10, 32)); | |
rtl = Rtl_assign(y11, 64, right); | |
dead = VPOi_locSetBuild(x10, 0); | |
VPOi_rtl(rtl, dead); | +d[43]=FI[f[42]] f[42] |
| (see above) | +r[44]=r[30]+LOC[0] |
loc = Rtl_location('m', Rtl_fetch(t12, 32)); | |
rtl = Rtl_assign(loc, 64, Rtl_fetch(y11, 64)); | |
dead = VPOi_locSetBuild(y11, t12, 0); | |
VPOi_rtl(rtl, dead); | +D[r[44]]=d[43] r[44]d[43] |
| (see above) | +r[32]=r[14]+LOC[1] |
| +r[33]=R[r[32]] r[32] | |
| +r[34]=r[30]+LOC[0] | |
loc = Rtl_location('m', Rtl_fetch(t2, 32)); | |
rtl = Rtl_assign(y3, 64, Rtl_fetch (loc, 64)); | |
dead = VPOi_locSetBuild(t2, 0); | |
VPOi_rtl(rtl, dead); | +d[35]=D[r[34]] r[34] |
f2sym = VPOi_asm->import("f2"); | |
VPOi_globalDeclare(f2sym, 'r'); | df2 GLO[2] 0 2 |
| (see above) | +r[36]=HI[GLO[2]] |
| +r[36]=r[36]+LO[GLO[2]] | |
rtl = Rtl_assign(r8, 32, Rtl_fetch(t1, 32)); | |
dead = VPOi_locSetBuild(t1, 0); | +r[8]=r[33] r[33] |
VPOi_rtl(rtl, dead); | ur[8] |
rtl = Rtl_assign(y5, 64, Rtl_fetch(y3, 64)); | |
dead = VPOi_locSetBuild(y3, 0); | |
VPOi_rtl(rtl, dead); | +d[37]=d[35] d[35] |
right = Rtl_binary(Rtl_op_add, Rtl_fetch(r14, 32), Rtl_int(72)); | |
loc = Rtl_location('m', right); | |
rtl = Rtl_assign(loc, 64, Rtl_fetch(y5, 64)); | |
dead = VPOi_locSetBuild(y5, 0); | |
VPOi_rtl(rtl, dead); | +D[r[14]+72]=d[37] d[37] |
rtl = Rtl_assign(r9, 32, Rtl_fetch(loc, 32)); | +r[9]=R[r[14]+72] |
VPOi_rtl(rtl, 0); | ur[9] |
right = Rtl_binary(Rtl_op_add, Rtl_fetch(r14, 32), Rtl_int(76)); | |
loc = Rtl_location('m', right); | |
rtl = Rtl_assign(r10, 32, Rtl_fetch(loc, 32)); | +r[10]=R[r[14]+76] |
VPOi_rtl(rtl, 0); | ur[10] |
VPOi_parameterListSize(80); | a80 |
used = VPOi_locSetBuild(r8, r9, r10, 0); | |
defined = VPOi_locSetBuild(r8, 0); | |
dead = VPOi_locSetBuild(VPOi_loc_IC, VPOi_loc_FC, t4, r8, r9, r10, 0); | |
killed = VPOi_locSetBuild(r8, r9, r10, r11, r12, r13, 0); | |
cSave = 0; | |
for (i=0; i<32; i+=2) | |
cSave = VPOi_locSetAdd(cSave, | |
Rtl_constLoc('d', i); | |
for (i = 1; i<9; i++) | |
cSave = VPOi_locSetAdd(cSave, | |
Rtl_constLoc('r', i); | |
rtl = Rtl_assign(VPOi_loc_PC, 32, Rtl_fetch(t4, 32)); | +ST=r[36] ICFCr[36]r[8]r[9]r[10] |
VPOi_functionCall(rtl, dead, used, defined, cSave, killed); | Pr[8]r[9]r[10] |
| Cd[0]d[2]...d[30]r[1]...r[7] | |
| Sr[8]r[9]r[10]r[11]r[12]r[13] | |
VPOi_trashAll(); | t* |
rtl = Rtl_assign(t6, 32, Rtl_fetch(r8, 32)); | +r[38]=r[8] |
VPOi_rtl(rtl, 0); | rr[8] |
| (see above) | +r[39]=r[14]+LOC[2] |
| +R[r[39]]=r[38] r[39]r[38] | |
| L16 | |
| +r[32]=r[14]+LOC[1] | |
| +r[33]=R[r[32]] r[32] | |
| +r[34]=1 | |
| +r[33]=r[33]+r[34] r[34] | |
| +r[35]=r[14]+LOC[1] | |
| +R[r[35]]=r[33] r[35]r[33] | |
| +r[32]=r[14]+LOC[1] | |
| +r[33]=R[r[32]] r[32] | |
| +r[34]=10 | |
right = Rtl_binary(Rtl_op_cmp, Rtl_fetch(t1, 32), Rtl_fetch(t2, 32)); | |
rtl = Rtl_assign(VPOi_loc_IC, 32, right); | |
dead = VPOi_locSetBuild(t1, t2, 0); | |
VPOi_rtl(rtl, dead); | +IC=r[33]?r[34] r[34]r[33] |
L20sym = VPOi_asm->local(".L20"); | |
guard = Rtl_binary(Rtl_op_ge, Rtl_fetch(VPOi_loc_PC, 32), Rtl_int(0)); | |
rtl = Rtl_assign(VPOi_loc_PC, 32, Rtl_relAddr(L20sym -> relAddr)); | |
rtl = Rtl_guard(guard, rtl); | |
VPOi_rtl(rtl, 0); | +PC=IC`0,L20 |
rtl = Rtl_assign(VPOi_loc_PC, 32, Rtl_relAddr(loophead -> relAddr)); | |
VPOi_rtl(rtl, 0); | +PC=L18 |
VPOi_asm->define_symbol_here(L20sym); | L20 |
| (see above) | L17 |
| +r[32]=r[14]+LOC[2] | |
| +r[33]=R[r[32]] r[32] | |
right = Rtl_unary(Rtl_op_restore, Rtl_fetch(t1, 32)); | |
rtl = Rtl_assign(r8, 32, right); | |
dead = VPOi_locSetBuild(t1, 0); | +r[8]=RS[r[33]] r[33] |
VPOi_rtl(rtl, dead); | ur[8] |
rtl = Rtl_assign(VPOi_loc_PC, 32, Rtl_fetch(VPOi_loc_RT, 32)); | |
defined = VPOi_locSetBuild(r8, 0) | +PC=RT |
VPOi_functionLeave(rtl, defined); | s=r[8]; |
| (see above) | L15 |
VPOi_functionLeave(rtl, 0); | +PC=RT |
VPOi_functionEnd(); | * |
VPOi_asm->section("data"); | - .seg "data" |
VPOi_asm->progend(); | |
VPOi_end(); |
printf>: D1
| Intro | RTL Creation Interface | Assembly Language Interface | VPO Code Generation Interface |