Intro RTL Creation Interface Assembly Language Interface VPO Code Generation Interface

VPOi: Input Generation Interface for VPO

Introduction

This interface produces input for the VPO code improver. Currently the input is written to a file. Eventually it will be placed in a data structure for VPO to process.

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>




TypeAbbreviation     Meaning
Rtl_ty_rtlrtlA register transfer list (RTL), or effect.
Rtl_ty_loclocA location node in an RTL.
Rtl_ty_storagestorageA character representing a storage space (registers, memory, etc.).
VPOi_ty_locSetlocSetA set of locations.
Types used in this interface [*]

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 [*]


Data types

Storage Locations

The RTL creation interface defines a location to be any place where a value may be stored and/or retrieved. VPO supports the following storage spaces: A storage space may include several types of storage. For example, a target may have integer registers and floating-point registers.

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.

Temporaries

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.

Examples

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 8Rtl_constLoc('r', 8)
Temporary register 0Rtl_constLoc('t', 0)
Stack memoryRtl_location ('m',  Rtl_fetch (Rtl_constLoc ('r', 14), 32))

Note that to use register 14 as an address, we must fetch its contents.

Special locations

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;
Defines VPOi_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.

Location Sets

Many of the directives in this interface accept a set of locations as a parameter. The location set usually provides information about hardware registers that are ``live'' upon entering or leaving a function, or locations that are ``dead'' after the execution of a RTL. Location sets are of type VPOi_ty_locSet.

<type and value definitions>+= (<-U) [<-D->]
typedef struct VPOi_st_locSet *VPOi_ty_locSet;
Defines VPOi_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, ...);
Defines VPOi_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);
Defines VPOi_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.

Relocatable Addresses

A relocatable address is an integral constant whose value is not known at compile time, and may not be determined until link time. VPO uses relocatable addresses to denote

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.

Register Transfer Lists

Machine instructions are represented as Register Transfer Lists (RTLs). RTLs are constructed as trees of type 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.

Assembly Language

Much of VPO's input is assembly-language directives and pseudo-operations. Assembly language is used to allocate storage, define symbols, emit constants, etc. The Assembly Language Interface places target assembly code fragments into the VPO input file, which VPO copies unchanged to the target assembly file. A different implementation of this interface exists for each target assembler. During initialization, this interface is loaded into the following variable.

<type and value definitions>+= (<-U) [<-D]
extern Assembler VPOi_asm;
Defines VPOi_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.

VPO Input Generation Functions

Initialization and Termination

The first call to the VPOi interface must be to 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);
Defines VPOi_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.

Register Transfer List

Instructions that are to be processed by VPO should be output as a RTL (Register Transfer List), using the following function.

<prototypes of interface functions>+= (<-U) [<-D->]
void VPOi_rtl(Rtl_ty_rtl rtl, VPOi_ty_locSet dead);
Defines VPOi_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.

Variable Declaration

The following objects must be declared to VPO before they can be used: The declaration may occur anywhere before the first use of the object. It is an unchecked run-time error to declare the same object more than once. Declaring any of the above objects requires two steps. First, the item must be declared in the Assembly Language Generation interface using 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.

Globals
The following function declares a global symbol.

<prototypes of interface functions>+= (<-U) [<-D->]
void VPOi_globalDeclare(AsmSymbol symbol, Rtl_ty_storage regspace);
Defines VPOi_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.

Locals
The following function declares a local variable.

<prototypes of interface functions>+= (<-U) [<-D->]
void VPOi_localVariableDeclare(AsmSymbol symbol,
        Rtl_ty_storage regspace, int size, int volat,
        int blockNumber);
Defines VPOi_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.

Parameters
The following function declares a function parameter.

<prototypes of interface functions>+= (<-U) [<-D->]
void VPOi_parameterDeclare(AsmSymbol symbol,
        Rtl_ty_storage regspace, int size, int volat, Rtl_ty_loc loc);
Defines VPOi_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.,

Rtl_location('m',
Rtl_binary(Rtl_op_add, Rtl_fetch(Rtl_loc_ST, 32), Rtl_int(offset)));
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.)

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.

Labels and control flow

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);
}
Defines emit_if, newlabel (links are to index).

Function Definitions

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);
Defines VPOi_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);
Defines VPOi_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);
Defines VPOi_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 */
);
Defines VPOi_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.

used
is the list of hardware registers that contain live values that the callee is expected to read. On targets that support passing function parameters in registers, the used list consists of the registers (if any) that contain parameters for this function call.
defined
is a list of hardware registers whose values the callee will define, i.e., the function return value.
callerSave
is the list of hardware registers that the caller saves prior to the call and whose values are restored before the execution of the instruction following the call. VPO emits code to save these registers if necessary.
killed
is the list of hardware registers that the callee will potentially leave undefined, and whose values are not restored. VPO assumes any value in a killed register is destroyed by the function call.
If the target's calling sequence has a standard set of caller save registers, then callerSave 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_eU_rD_eSPARC example     Description
usedD_r?%o0Register defined in caller and used in the callee. May be redefined in callee. Typically identifies parameters.
definedD_eX%o0Register defined in callee and used in caller. Typically identifies return results.
killedD_r?X%o3A 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).
callerSaveD_rX%f0A 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:

D_rA definition in the caller, before the call.
D_eA definition in the callee.
U_rA use in the caller, after the call.
U_eA use in the callee.
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.

Definitions and uses at call sites [*]


In general, passing parameters to a callee requires that space for the parameters be reserved in the caller's stack frame. This space is called the argument build area. VPO expects that the front end un-nests calls, so that the same argument-build area can be used for each call. The amount of space required is determined by the length of the largest parameter list, considering all the call sites in the caller. VPOi_parameterListSize(n) that there is a call passing n bytes of parameters.

<prototypes of interface functions>+= (<-U) [<-D->]
void VPOi_parameterListSize(int bytes);
Defines VPOi_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.

Cacher directives for common-subexpression elimination

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:

General RTLs have multiple assignments and kill effects; a general RTL can be written l_i :=e_i; kill l_j for some set of i and j. In this case, the cacher computes a set of expressions ê_i, trashes all the l_i and l_j, and adds the pairs l_i |-->ê_i.

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);
Defines trash (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);
Defines trashAll (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.

Source Code Directives

The following directives are used to give VPO information about the source code.

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);
Defines VPOi_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);
Defines VPOi_sourceCoordinate (links are to index).

Annotated Example

The following annotated example shows the VPOi interface calls that generate the VPO input file for the following C source code.

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
VPO input file
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();

Index of identifiers

List of code chunks

Intro RTL Creation Interface Assembly Language Interface VPO Code Generation Interface