[an error occurred while processing this directive]

HP OpenVMS Systems Documentation

Content starts here

HP OpenVMS Calling Standard


Previous Contents Index

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:

  1. 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.)
  2. 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.
  3. 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:

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


Previous Next Contents Index