HP OpenVMS Calling Standard
3.4.7 Procedure Descriptor for Null Frame Procedures
The null frame procedure descriptor built by a
compiler provides information about a procedure with no frame. The size
of the descriptor is 16 bytes (defined by PDSC$K_NULL_SIZE).
The fields defined in the null frame descriptor are illustrated in
Figure 3-7 and described in Table 3-5.
Figure 3-7 Null Frame Procedure Descriptor (PDSC)
Format
Table 3-5 Contents of Null Frame Procedure Descriptor (PDSC)
Field Name |
Contents |
PDSC$W_FLAGS
|
The PDSC descriptor flag bits <15:0> are defined as follows:
PDSC$V_KIND
|
A 4-bit field <3:0> that identifies the type of procedure
descriptor. For a null frame procedure, this field must specify a value
8 (defined by constant PDSC$K_KIND_NULL).
|
Bits 4--7
|
Must be 0.
|
PDSC$V_REI_RETURN
|
Bit 8. If set to 1, the procedure expects the stack at entry to be set,
so an REI instruction correctly returns from the procedure. Also, if
set, the contents of the PDSC$B_SAVE_RA field are unpredictable and the
return address is found on the stack.
|
Bit 9
|
Must be 0 (reserved).
|
PDSC$V_BASE_FRAME
|
For compiled code, this bit must be 0. If set to 1, indicates the
logical base frame of a stack that precedes all frames corresponding to
user code. The interpretation and use of this frame and whether there
are any predecessor frames is system software defined (and subject to
change).
|
Bit 11
|
Must be 0 (reserved).
|
PDSC$V_NATIVE
|
For compiled code, this bit must be set to 1.
|
PDSC$V_NO_JACKET
|
For compiled code, this bit must be set to 1.
|
PDSC$V_TIE_FRAME
|
For compiled code, this bit must be 0. Reserved for use by system
software.
|
Bit 15
|
Must be 0 (reserved).
|
|
PDSC$V_FUNC_RETURN
|
A 4-bit field <11:8> that describes which registers are used for
the function value return (if there is one) and what format is used for
those registers.
Table 5-4 lists and describes the possible encoded values of
PDSC$V_FUNC_RETURN.
|
PDSC$W_SIGNATURE_OFFSET
|
A 16-bit signed byte offset from the start of the procedure descriptor.
This offset designates the start of the procedure signature block (if
any). A 0 in this field indicates that no signature information is
present. Note that in a bound procedure descriptor (as described in
Section 3.6.4), signature information might be present in the related
procedure descriptor. A 1 in this field indicates a standard default
signature. An offset value of 1 is not otherwise a valid offset because
both procedure descriptors and signature blocks must be quadword
aligned.
|
PDSC$Q_ENTRY
|
The absolute address of the first instruction of the entry code
sequence for the procedure.
|
3.5 Procedure Call Stack
Except for null-frame procedures, a procedure is an active
procedure while its body is executing, including while any
procedure it calls is executing. When a procedure is active, it may
handle an exception that is signaled during its execution.
Associated with each active procedure is an invocation
context, which consists of the set of registers and space in
memory that is allocated and that may be accessed during execution for
a particular call of that procedure.
When a procedure begins to execute, it has no invocation context. The
initial instructions that allocate and initiallize its context, which
may include saving information from the invocation context of its
caller, are termed the procedure prologue. Once
execution of the prologue is complete, the procedure is said to be
active.
When a procedure is ready to return to its caller, the instructions
that deallocate and discard the procedure's invocation context (which
may include restoring state of the caller's invocation context that was
saved during the prologue), are termed a procedure
epilogue. A procedure ceases to be active
when execution of its epilogue begins.
A procedure may have more than one prologue if there are multiple entry
points. A procedure may also have more than one epilogue if there are
multiple return points. One of each will be executed during any given
invocation of the procedure.
Some procedures, notably null frame procedures (see
Section Section 3.4.6), never have an invocation context of their own
and are said to execute in the body of their caller. A null frame
procedure has no prologue or epilogue, and consists solely of body
instructions. Such a procedure never becomes current
or active in the sense that its handler may be invoked.
A call stack (for a thread) consists of the stack of invocation
contexts that exists at any point in time. New invocation contexts are
pushed on that stack as procedures are called and invocations are
popped from the call stack as procedures return.
The invocation context of a procedure that calls another procedure is
said to precede or be previous to the invocation context of the called
procedure.
3.5.1 Current Procedure
The current procedure is the active procedure whose
execution began most recently; its invocation context is at the top of
the call stack. Note that a procedure executing in its prologue or
epilogue is not active, and hence cannot be the current procedure.
Similarly, a null frame procedure cannot be the current procedure.
In this calling standard, R29 is the frame pointer (FP) register that
defines the current procedure.
Therefore, the current procedure must always maintain in FP
one of the following pointer values:
- Pointer to the procedure descriptor for that procedure.
- Pointer to a naturally aligned quadword containing the address of
the procedure descriptor for that procedure. For purposes of finding a
procedure's procedure descriptor, no assumptions must be made about the
quadword location. As long as all other requirements of this standard
are met, a compiler is free to use FP as a base register for any
arbitrary storage, including a stack frame, provided that while the
procedure is current, the quadword pointed to by the value in FP
contains the address of that procedure's descriptor.
At any point in time, the FP value can be interpreted to find the
procedure descriptor for the current procedure by examining the value
at 0(FP) as follows:
- If 0(FP)<2:0> = 0, then FP points to a quadword that contains
a pointer to the procedure descriptor for the current procedure.
- If 0(FP)<2:0> <> 0, then FP points to the procedure
descriptor for the current procedure.
By examining the first quadword of the procedure descriptor, the
procedure type can be determined from the PDSC$V_KIND field.
The following code is an example of how the current procedure
descriptor and procedure type can be found:
LDQ R0,0(FP) ;Fetch quadword at FP
AND R0,#7,R28 ;Mask alignment bits
BNEQ R28,20$ ;Is procedure descriptor pointer
LDQ R0,0(R0) ;Was pointer to procedure descriptor
10$: AND R0,#7,R28 ;Do sanity check
BNEQ R28,20$ ;All is well
;Error - Invalid FP
20$: AND R0,#15,R0 ;Get kind bits
;Procedure KIND is now in R0
|
IF PDSC$V_KIND is equal to PDSC$K_KIND_FP_STACK, the current procedure
has a stack frame.
If PDSC$V_KIND is equal to PDSC$K_KIND_FP_REGISTER, the current
procedure is a register frame procedure.
Either type of procedure can use either type of mechanism to point to
the procedure descriptor. Compilers may choose the appropriate
mechanism to use based on the needs of the procedure involved.
3.5.2 Procedure Call Tracing
Mechanisms for each of the following functions are needed to support
procedure call tracing:
- To provide the context of a procedure invocation
- To walk (navigate) the procedure call stack
- To refer to a given procedure invocation
This section describes the data structure mechanisms. The routines that
support these functions are described in Section 3.5.3.
3.5.2.1 Referring to a Procedure Invocation from a Data Structure
When referring to a specific procedure invocation at run time, a
procedure invocation handle, shown in Figure 3-8,
can be used. Defined by constant LIBICB$K_INVO_HANDLE_SIZE, the
structure is a single-field longword called HANDLE. HANDLE describes
the invocation handle of the procedure.
Figure 3-8 Procedure Invocation Handle Format
To encode a procedure invocation handle, follow these steps:
- If PDSC$V_BASE_REG_IS_FP is set to 1 in the corresponding procedure
descriptor, then set INVO_HANDLE to the contents of the FP register in
that invocation.
If PDSC$V_BASE_REG_IS_FP is set to 0, set
INVO_HANDLE to the contents of the SP register in that invocation.
(That is, start with the base register value for the frame.)
- Shift the INVO_HANDLE contents left one bit. Because this value is
initially known to be octaword aligned (see Section 3.6.1), the result
is a value whose 5 low-order bits are 0.
- If PDSC$V_KIND = PDSC$K_KIND_FP_STACK, perform a logical OR on the
contents of INVO_HANDLE with the value 1F16, and then set
INVO_HANDLE to the value that results.
If PDSC$V_KIND =
PDSC$K_KIND_FP_REGISTER, perform a logical OR on the contents of
INVO_HANDLE with the contents of PDSC$B_SAVE_RA, and then set
INVO_HANDLE to the value that results.
Note that a procedure invocation handle is not defined for a null frame
procedure.
Note
So you can distinguish an invocation of a register frame procedure that
calls another register frame procedure (where the called procedure uses
no stack space and therefore has the same base register value as the
caller), the register number that saved the return address is included
in the invocation handle of a register frame procedure. Similarly, the
number 3110 in the invocation handle of a stack frame
procedure is included to distinguish an invocation of a stack frame
procedure that calls a register frame procedure where the called
procedure uses no stack space.
|
3.5.2.2 Invocation Context Block
The context of a specific procedure invocation is provided through the
use of a data structure called an invocation context
block. The minimum size of the block is 528 bytes and is
system defined using the constant LIBICB$K_INVO_CONTEXT_BLK_SIZE. The
size of the last field (LIBICB$Q_SYSTEM_DEFINED[n]) defined by
the host system determines the total size of the block.
The fields defined in the invocation context block are illustrated in
Figure 3-9 and described in Table 3-6.
Figure 3-9 Invocation Context Block Format
Table 3-6 Contents of the Invocation Context Block
Field Name |
Contents |
LIBICB$L_CONTEXT_LENGTH
|
Unsigned count of the total length in bytes of the context block; this
represents the sum of the lengths of the standard-defined portion and
the system-defined section.
|
LIBICB$R_FRAME_FLAGS
|
The procedure frame flag bits <24:0> are defined as follows:
LIBICB$V_EXCEPTION_FRAME
|
Bit 0. If set to 1, the invocation context corresponds to an exception
frame.
|
LIBICB$V_AST_FRAME
|
Bit 1. If set to 1, the invocation context corresponds to an
asynchronous trap.
|
LIBICB$V_BOTTOM_OF_STACK
|
Bit 2. If set to 1, the invocation context corresponds to a frame that
has no predecessor.
|
LIBICB$V_BASE_FRAME
|
Bit 3. If set to 1, the BASE_FRAME bit is set in the FLAGS field of the
associated procedure descriptor.
|
|
LIBICB$B_BLOCK_VERSION
|
A byte that defines the version of the context block. Because this
block is currently the first version, the value is set to 1.
|
LIBICB$PH_PROCEDURE_DESCRIPTOR
|
Address of the procedure descriptor for this context.
|
LIBICB$Q_PROGRAM_COUNTER
|
Quadword that contains the current value of the procedure's program
counter. For interrupted procedures, this is the same as the
continuation program counter; for active procedures, this is the return
address back into that procedure.
|
LIBICB$Q_PROCESSOR_STATUS
|
Contains the current value of the processor status.
|
LIBICB$Q_IREG[
n]
|
Quadword that contains the current value of the integer register in the
procedure (where
n is the number of the register).
|
LIBICB$Q_FREG[
n]
|
Quadword that contains the current value of the floating-point register
in the procedure (where
n is the number of the register).
|
LIBICB$Q_SYSTEM_DEFINED[
n]
|
A variable-sized area with locations defined in quadword increments by
the host environment that contains procedure context information. These
locations are
not defined by this standard.
|
3.5.2.3 Getting a Procedure Invocation Context with a Routine
A thread can obtain its own context or the current context of any
procedure invocation in the current stack call (given an invocation
handle) by calling the run-time library functions defined in
Section 3.5.3.
3.5.2.4 Walking the Call Stack
During the course of program execution, it is sometimes necessary to
walk the call stack. Frame-based exception handling is one case where
this is done. Call stack navigation is possible only in the reverse
direction (in a latest-to-earliest or top-to-bottom sequence).
To walk the call stack, perform the following steps:
- Given a program state (which contains a register set), build an
invocation context block.
For the current routine, an initial
invocation context block can be obtained by calling the
LIB$GET_CURR_INVO_CONTEXT routine (see Section 3.5.3.2).
- Repeatedly call the LIB$GET_PREV_INVO_CONTEXT routine (see
Section 3.5.3.3) until the end of the chain has been reached (as
signified by 0 being returned).
The bottom of stack frame (end of
the call chain) is indicated (LIBICB$V_BOTTOM_OF_STACK) when the target
frame's saved FP value is 0.
Compilers are allowed to optimize high-level language procedure calls
in such a way that they do not appear in the invocation chain. For
example, inline procedures never appear in the invocation chain.
Make no assumptions about the relative positions of any memory used for
procedure frame information. There is no guarantee that successive
stack frames will always appear at higher addresses.
3.5.3 Invocation Context Access Routines
A thread can manipulate the invocation context of any procedure in the
thread's virtual address space by calling the following run-time
library functions.
3.5.3.1 LIB$GET_INVO_CONTEXT
A thread can obtain the invocation context of any active procedure by
using the following function format:
LIB$GET_INVO_CONTEXT(invo_handle, invo_context)
|
Argument |
OpenVMS Usage |
Type |
Access |
Mechanism |
invo_handle
|
invo_handle
|
longword (unsigned)
|
read
|
by value
|
invo_context
|
invo_context_blk
|
structure
|
write
|
by reference
|
Arguments:
|
invo_handle
Handle for the desired invocation.
|
|
invo_context
Address of an invocation context block into which the procedure
context of the frame specified by
invo_handle will be written.
|
Function Value Returned:
|
status
Status value. A value of 1 indicates success; a value of 0
indicates failure.
|
Note
If the invocation handle that was passed does not represent any
procedure context in the active call stack, the value of the new
contents of the context block is unpredictable.
|
3.5.3.2 LIB$GET_CURR_INVO_CONTEXT
A thread can obtain the invocation context of a current procedure by
using the following function format:
LIB$GET_CURR_INVO_CONTEXT(invo_context)
|
Argument |
OpenVMS Usage |
Type |
Access |
Mechanism |
invo_context
|
invo_context_blk
|
structure
|
write
|
by reference
|
Argument:
|
invo_context
Address of an invocation context block into which the procedure
context of the caller will be written.
|
Function Value Returned:
|
Zero. This is to facilitate use in the implementation of the C language
unwind
setjmp
or
longjmp
function (only).
|
3.5.3.3 LIB$GET_PREV_INVO_CONTEXT
A thread can obtain the invocation context of the procedure context
preceding any other procedure context by using the following function
format:
LIB$GET_PREV_INVO_CONTEXT(invo_context)
|
Argument |
OpenVMS Usage |
Type |
Access |
Mechanism |
invo_context
|
invo_context_blk
|
structure
|
modify
|
by reference
|
Argument:
|
invo_context
Address of an invocation context block. The given invocation
context block is updated to represent the context of the previous
(calling) frame. The LIBICB$V_BOTTOM_OF_STACK flag of the invocation
context block is set if the target frame represents the end of the
invocation call chain or if stack corruption is detected.
|
Function Value Returned:
|
status
Status value. A value of 1 indicates success. When the initial
context represents the bottom of the call stack, a value of 0 is
returned. If the current operation completed without error, but a stack
corruption was detected at the next level down, a value of 3 is
returned.
|
|