Document revision date: 30 March 2001

## OpenVMS Calling Standard

Previous | Contents | Index

### 3.7.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.7.5.1 Entry Code Sequence

Since 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:

- All registers specified by this standard as saved across a standard call must contain their original (at entry) contents.
- No standard calls may be made.

Note 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 since 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:

1. If PDSC$L_SIZE is not 0, set register SP = SP - PDSC$L_SIZE.
2. If PDSC$V_BASE_REG_IS_FP is 1, store the address of the procedure descriptor at 0(SP).
   If PDSC$V_KIND = PDSC$K_KIND_FP_REGISTER, copy the return address to the register specified by PDSC$B_SAVE_RA, if it is not already there, and copy the FP register to the register specified by PDSC$B_SAVE_FP.
   If PDSC$V_KIND = PDSC$K_KIND_FP_STACK, copy the return address to the quadword at the RSA$Q_SAVED_RETURN offset in the register save area denoted by PDSC$W_RSA_OFFSET, and store the registers specified by PDSC$L_IREG_MASK and PDSC$L_FREG_MASK in the register save area denoted by PDSC$W_RSA_OFFSET. (This step includes saving the value in FP.)
   Execute TRAPB if required (see Section 6.5.3.2 for details).
3. If PDSC$V_BASE_REG_IS_FP is 0, load register FP with the address of the procedure descriptor or the address of a quadword that contains the address of the procedure descriptor.
   If PDSC$V_BASE_REG_IS_FP is 1, copy register SP to register FP.

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-2 is an entry code example for a stack frame. The example assumes that:

- This is a stack frame procedure
- Registers R2--4 and F2--3 are saved and restored
- PDSC$W_RSA_OFFSET = 16
- The procedure has a static exception handler that does not reraise arithmetic traps
- The procedure uses a variable amount of stack

If the code sequence in Example 3-2 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-2 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-3 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-3 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.

#### 3.7.5.2 Exit Code Sequence

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:

1. If PDSC$V_BASE_REG_IS_FP is 1, then copy FP to SP.
   If PDSC$V_KIND = PDSC$K_KIND_FP_STACK, and this procedure saves or restores any registers other than FP and SP, reload those registers from the register save area as specified by PDSC$W_RSA_OFFSET.
   If PDSC$V_KIND = PDSC$K_KIND_FP_STACK, load a scratch register with the return address from the register save area as specified by PDSC$W_RSA_OFFSET. (If PDSC$V_KIND = PDSC$K_KIND_FP_REGISTER, the return address is already in scratch register PDSC$B_SAVE_RA.)
   Execute TRAPB if required (see Section 6.5.3.2 for details).
2. If PDSC$V_KIND = PDSC$K_KIND_FP_REGISTER, copy the register specified by PDSC$B_SAVE_FP to register FP.
3. If PDSC$V_KIND = PDSC$K_KIND_FP_STACK, reload FP from the saved FP in the register save area.
4. If a function value is not being returned using the stack (PDSC$V_STACK_RETURN_VALUE is 0), then restore SP to the value it had at procedure entry by adding the value that was stored in PDSC$L_SIZE to SP. (In some cases, the returning procedure will leave SP pointing to a lower stack address than it had on entry to the procedure, as specified in Section 3.8.7.)
5. Jump to the return address (which is in a scratch register).

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-4 shows the return code sequence for the stack frame.

Example 3-4 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-4 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-5 contains the return code sequence for the register frame.

Example 3-5 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

## 3.8 Data Passing

This section defines the OpenVMS Alpha calling standard conventions of passing data between procedures in a call chain. An argument item represents one unit of data being passed between procedures.

### 3.8.1 Argument-Passing Mechanisms

This OpenVMS Alpha calling standard defines three classes of argument items according to the mechanism used to pass the argument:

- Immediate value
- Reference
- Descriptor

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.

Immediate value

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.

Reference

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.

Descriptor

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.8.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-14 specifies the standard locations in which argument items can be passed.

Table 3-14 Argument Item Locations
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:

- All argument items are passed in the integer registers or on the stack, except for argument items that are floating-point data passed by immediate value.
- Floating-point data passed by immediate value is passed in the floating-point registers or on the stack.
- Only one location (across an item row in Table 3-14) can be used by any given argument item in a list. For example, if argument item 3 is an integer passed by value, and argument item 4 is a single-precision floating-point number passed by value, then argument item 3 is assigned to R18 and argument item 4 is assigned to F19.
- A single- or double-precision complex value is treated as two arguments for the purpose of argument-item sequence rules. In particular, the real part of a complex value might be passed as the sixth argument item in register F21, in which case the imaginary part is then passed as the seventh argument item in memory.
  An extended precision complex value is passed by reference using a single integer or stack argument item. (An extended precision complex value is not passed by immediate value because the component extended precision floating values are not passed by value. See also Section 3.8.5.1, Sending Mechanism.)

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.8.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:

- Arguments are mapped from left to right to increasing offsets in the argument item sequence. R16 or F16 is allocated to the first argument, and the last quadword of the memory argument list (if any) is allocated to the last argument.
- Each source language argument corresponds to one or more contiguous Alpha calling standard argument items.
- Each argument item consists of 64 bits.
- A null or omitted argument---for example, CALL SUB(A,,B)---is represented by an argument item containing the value 0.
  Arguments passed by immediate value cannot be omitted unless a default value is supplied by the language. (This is to enable called procedures to distinguish an omitted immediate argument from an immediate value argument with the value 0.)
  Trailing null or omitted arguments---for example, CALL SUB(A,,)---are passed by the same rules as for embedded null or omitted arguments.

### 3.8.4 Unused Bits in Passed Data

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-15 lists and defines the various data-type requirements for size and their extensions to set or clear unused bits.

Table 3-15 Data Types and the Unused Bits in Passed Data
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-15:

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

| privacy and legal statement |
5973PRO_007.HTML