 |
OpenVMS Calling Standard
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:
- If PDSC$L_SIZE is not 0, set register SP = SP - PDSC$L_SIZE.
- 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).
- 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:
- 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).
- If PDSC$V_KIND = PDSC$K_KIND_FP_REGISTER, copy the register
specified by PDSC$B_SAVE_FP to register FP.
- If PDSC$V_KIND = PDSC$K_KIND_FP_STACK, reload FP from the saved FP
in the register save area.
- 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.)
- 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.
|