|
HP OpenVMS systems documentation |
Previous | Contents | Index |
The procedure value for a bound procedure is a pointer to a bound procedure descriptor that, like all other procedure descriptors, contains the address to which the calling procedure must transfer control at offset 8 (see Figure 3-12). This transfer code is responsible for setting up the dynamic environment needed by the target nested procedure and then completing the transfer of control to the code for that procedure. The transfer code receives in R27 a pointer to its corresponding bound procedure descriptor and thus can fetch any required environment information from that descriptor. A bound procedure descriptor also contains a procedure value for the target procedure that is used to complete the transfer of control.
When the transfer code sequence addressed by PDSC$Q_ENTRY of a bound procedure descriptor is called (by a call sequence such as the one given in Section 3.6.3), the procedure value will be in R27, and the transfer code must finish setting up the environment for the target procedure. The preferred location for this transfer code is directly preceding the code for the target procedure. This saves a memory fetch and a branching instruction and optimizes instruction caches and paging.
The following is an example of such a transfer code sequence. It is an example of a target procedure Q that expects the environment value to be passed in R1 and a linkage pointer in R27.
Q_TRANSFER: LDQ R1,24(R27) ;Environment value to R1 LDQ R27,16(R27) ;Procedure descriptor address to R27 Q_ENTRY:: ;Normal procedure entry code starts here |
After the transfer code has been executed and control is transferred to Q's entry address, R27 contains the address of Q's procedure descriptor, R26 (unmodified by transfer code) contains the return address, and R1 contains the environment value.
When a bound procedure value such as this is needed, the bound
procedure descriptor is usually allocated on the parent procedure's
stack.
3.6.5 Entry and Exit Code Sequences
To ensure that the stack can be interpreted at any point during thread
execution, all procedures must adhere to certain conventions for entry
and exit as defined in this section.
3.6.5.1 Entry Code Sequence
Because the value of FP defines the current procedure, all properties of the environment specified by a procedure's descriptor must be valid before the FP is modified to make that procedure current. In addition, none of the properties specified in the calling procedure's descriptor may be invalidated before the called procedure becomes current. So, until the FP has been modified to make the procedure current, all entry code must adhere to the following rules:
If an exception is raised or if an exception occurs in the entry code of a procedure, that procedure's exception handler (if any) will not be invoked because the procedure is not current yet. Therefore, if a procedure has an exception handler, compilers may not move code into the procedure prologue that might cause an exception that would be handled by that handler. |
When a procedure is called, the code at the entry address must synchronize (as needed) any pending exceptions caused by instructions issued by the caller, must save the caller's context, and must make the called procedure current by modifying the value of FP as described in the following steps:
The ENTRY_LENGTH value in the procedure descriptor provides information that is redundant with the setting of a new frame pointer register value. That is, the value could be derived by starting at the entry address and scanning the instruction stream to find the one that updates FP. The ENTRY_LENGTH value included in the procedure descriptor supports the debugger or PCA facility so that such a scan is not required.
Entry Code Example for a Stack Frame Procedure
Example 3-1 is an entry code example for a stack frame. The example assumes that:
If the code sequence in Example 3-1 is interrupted by an asynchronous software interrupt, SP will have a different value than it did at entry, but the calling procedure will still be current.
After an interrupt, it would not be possible to determine the original value of SP by the register frame conventions. If actions by an exception handler result in a nonlocal GOTO call to a location in the immediate caller, then it will not be possible to restore SP to the correct value in that caller. Therefore, any procedure that contains a label that can be the target of a nonlocal GOTO by immediately called procedures must be prepared to reset or otherwise manage the SP at that label.
Example 3-1 Entry Code for a Stack Frame Procedure |
---|
LDA SP,-SIZE(SP) ;Allocate space for new stack frame STQ R27,(SP) ;Set up address of procedure descriptor STQ R26,16(SP) ;Save return address STQ R2,24(SP) ;Save first integer register STQ R3,32(SP) ;Save next integer register STQ R4,40(SP) ;Save next integer register STQ FP,48(SP) ;Save caller's frame pointer STT F2,56(SP) ;Save first floating-point register STT F3,64(SP) ;Save last floating-point register TRAPB ;Force any pending hardware exceptions to ; be raised MOV SP,FP ;Called procedure is now the current procedure |
Entry Code Example for a Register Frame
Example 3-2 assumes that the called procedure has no static exception handler and utilizes no stack storage, PDSC$B_SAVE_RA specifies R26, PDSC$B_SAVE_FP specifies R22, and PDSC$V_BASE_REG_IS_FP is 0:
Example 3-2 Entry Code for a Register Frame Procedure |
---|
MOV FP,R22 ;Save caller's FP. MOV R27,FP ;Set FP to address of called procedure's ; descriptor. Called procedure is now the ; current procedure. |
When a procedure returns, the exit code must restore the caller's context, synchronize any pending exceptions, and make the caller current by modifying the value of FP. The exit code sequence must perform the following steps:
The called routine does not adjust the stack to remove any arguments passed in memory. This responsibility falls to the calling routine that may choose to defer their removal because of optimizations or other considerations.
Exit Code Example for a Stack Frame
Example 3-3 shows the return code sequence for the stack frame.
Example 3-3 Exit Code Sequence for a Stack Frame |
---|
MOV FP,SP ;Chop the stack back LDQ R28,16(FP) ;Get return address LDQ R2,24(FP) ;Restore first integer register LDQ R3,32(FP) ;Restore next integer register LDQ R4,40(FP) ;Restore next integer register LDT F2,56(FP) ;Restore first floating-point register LDT F3,64(FP) ;Restore last floating-point register TRAPB ;Force any pending hardware exceptions to ; be raised LDQ FP,48(FP) ;Restore caller's frame pointer LDA SP,SIZE(SP) ;Restore SP (SIZE is compiled into PDSC$L_SIZE) RET R31,(R28) ;Return to caller's code |
Interruption of the code sequence in Example 3-3 by an asynchronous software interrupt can result in the calling procedure being the current procedure, but with SP not yet restored to its value in that procedure. The discussion of that situation in entry code sequences applies here as well.
Exit Code Example for a Register Frame
Example 3-4 contains the return code sequence for the register frame.
Example 3-4 Exit Code Sequence for a Register Frame |
---|
MOV R22,FP ;Restore caller's FP value ; Caller is once again the current procedure. RET R31,(R26) ;Return to caller's code |
This section defines the OpenVMS Alpha calling standard conventions of
passing data between procedures in a call stack. An argument item
represents one unit of data being passed between procedures.
3.7.1 Argument-Passing Mechanisms
This OpenVMS Alpha calling standard defines three classes of argument items according to the mechanism used to pass the argument:
Argument items are not self-defining; interpretation of each argument item depends on agreement between the calling and called procedures.
This standard does not dictate which passing mechanism must be used by a given language compiler. Language semantics and interoperability considerations might require different mechanisms in different situations.
An immediate value argument item contains the value of the data item. The argument item, or the value contained in it, is directly associated with the parameter.
A reference argument item contains the address of a data item such as a scalar, string, array, record, or procedure. This data item is associated with the parameter.
A descriptor argument item contains the address of a
descriptor, which contains structural information about the argument's
type (such as array bounds) and the address of a data item. This data
item is associated with the parameter.
3.7.2 Argument List Structure
The argument list in an OpenVMS Alpha call is an ordered set of zero or more argument items, which together comprise a logically contiguous structure known as the argument item sequence. An argument item is specified using up to 64 bits.
A 64-bit argument item can be used to pass arguments by immediate value, by reference, and by descriptor. Any combination of these mechanisms in an argument list is permitted.
Although the argument items form a logically contiguous sequence, they are in practice mapped to integer and floating-point registers and to memory in a method that can produce a physically discontiguous argument list. Registers R16--21 and F16--21 are used to pass the first six items of the argument item sequence. Additional argument items must be passed in a memory argument list that must be located at 0(SP) at the time of the call.
Table 3-10 specifies the standard locations in which argument items can be passed.
Item | Integer Register | Floating-Point Register | Stack |
---|---|---|---|
1 | R16 | F16 | |
2 | R17 | F17 | |
3 | R18 | F18 | |
4 | R19 | F19 | |
5 | R20 | F20 | |
6 | R21 | F21 | |
7-- n | 0(SP) -- ( n - 7) * 8(SP) |
The following list summarizes the general requirements that determine the location of any specific argument:
The argument list that includes both the in-memory portion and the
portion passed in registers can be read from and written to by the
called procedure. Therefore, the calling procedure must not make any
assumptions about the validity of any part of the argument list after
the completion of a call.
3.7.3 Argument Lists and High-Level Languages
High-level language functional notations for procedure call arguments are mapped into argument item sequences according to the following requirements:
Whenever data is passed by value between two procedures in registers (for the first six input arguments and return values), or in memory (for arguments after the first six), the bits not used by the data are sign extended or zero extended as appropriate.
Table 3-11 lists and defines the various data-type requirements for size and their extensions to set or clear unused bits.
Data Type | Type Designator | Data Size (bytes) | Register Extension Type | Memory Extension Type |
---|---|---|---|---|
Byte logical | BU | 1 | Zero64 | Zero64 |
Word logical | WU | 2 | Zero64 | Zero64 |
Longword logical | LU | 4 | Sign64 | Sign64 |
Quadword logical | QU | 8 | Data64 | Data64 |
Byte integer | B | 1 | Sign64 | Sign64 |
Word integer | W | 2 | Sign64 | Sign64 |
Longword integer | L | 4 | Sign64 | Sign64 |
Quadword integer | Q | 8 | Data64 | Data64 |
F_floating | F | 4 | Hard | Data32 |
D_floating | D | 8 | Hard | Data64 |
G_floating | G | 8 | Hard | Data64 |
F_floating complex | FC | 2 * 4 | 2 * Hard | 2 * Data32 |
D_floating complex | DC | 2 * 8 | 2 * Hard | 2 * Data64 |
G_floating complex | GC | 2 * 8 | 2 * Hard | 2 * Data64 |
S_floating | FS | 4 | Hard | Data32 |
T_floating | FT | 8 | Hard | Data64 |
X_floating | FX | 16 | N/A | N/A |
S_floating complex | FSC | 2 * 4 | 2 * Hard | 2 * Data32 |
T_floating complex | FTC | 2 * 8 | 2 * Hard | 2 * Data64 |
X_floating complex | FXC | 2 * 16 | N/A | N/A |
Small structures of 8 bytes or less | N/A | <=8 | Nostd | Nostd |
Small arrays of 8 bytes or less | N/A | <=8 | Nostd | Nostd |
32-bit address | N/A | 4 | Sign64 | Sign64 |
64-bit address | N/A | 8 | Data64 | Data64 |
The following are the defined meanings for the extension type symbols used in Table 3-11:
Sign Extension Type | Defined Function |
---|---|
Sign64 | Sign-extended to 64 bits. |
Zero64 | Zero-extended to 64 bits. |
Data32 | Data is 32 bits. The state of bits <63:32> is unpredictable. |
2 * Data32 | Two single-precision parts of the complex value are stored in memory as independent floating-point values (each handled as Data32). |
Data64 | Data is 64 bits. |
2 * Data64 | Two double-precision parts of the complex value are stored in memory as independent floating-point values (each handled as Data64). |
Hard | Passed in the layout defined by the hardware SRM. |
2 * Hard | Two floating-point parts of the complex value are stored in a pair of registers as independent floating-point values (each handled as Hard). |
Nostd | State of all high-order bits not occupied by the data is unpredictable across a call or return. |
Because of the varied rules for sign extension of data when passed as arguments, both calling and called routines must agree on the data type of each argument. No implicit data-type conversions can be assumed between the calling procedure and the called procedure.
Previous | Next | Contents | Index |