[an error occurred while processing this directive]

HP OpenVMS Systems Documentation

Content starts here

HP OpenVMS Calling Standard


Previous Contents Index

3.6.4.2 Bound Procedure Value

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:

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

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

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

3.7 Data Passing

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:

  • 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.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.

Table 3-10 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-10) 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.7.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.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:

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

Table 3-11 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-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