HP OpenVMS Systems Documentation

Content starts here

HP OpenVMS Calling Standard

Previous Contents Index Argument Information (AI) Register

In addition to the normal parameters, an implicit argument information value is passed in register R25, the Argument Information (AI) register. This value is shown in Figure 4-6.

Figure 4-6 Argument Information Register Representation

Argument Count is an unsigned byte that specifies the number of 64-bit argument slots used for the argument list. (Note that single and double-precision complex values use two slots, which is reflected in this count.)

Argument Register Information is a contiguous group of eight 3-bit fields that correspond to the eight arguments passed in registers. The first group, bits <10:8>, describes the first argument, the second group, bits <13:11>, describes the second argument, and so on. The encoding for each group is described in Table 4-12.

Table 4-12 Argument Information Register Codes
Value OpenVMS Name Meaning
0 AI$K_AR_I64 64-bit or 32-bit sign-extended to 64-bit argument passed in an integer register (including addresses)
Argument is not present
1 AI$K_AR_FF F_floating (also known as VAX single-precision floating-point) argument passed in a general register
2 AI$K_AR_FD D_floating (also known as VAX double-precision floating-point) argument passed in a general register
3 AI$K_AR_FG G_floating (also known as VAX double-precision floating-point) argument passed in a general register
4 AI$K_AR_FS S_floating (also known as IEEE single-precision floating-point) argument passed in a floating-point register
5 AI$K_AR_FT T_floating (also known as IEEE double-precision floating-point) argument passed in a floating-point register
6,7   Reserved Memory Stack Parameters

The remainder of the parameter list, beginning with slot 8, is passed in the outgoing parameter area of the memory stack frame, as described in Section 4.5.1. Parameters are mapped directly to memory, with slot 8 placed at location SP+16, slot 9 placed at location SP+24, and so on. Each argument is stored in memory as a series of one or more 64-bit storage units, with unused bits in the last unit undefined. Variable Argument Lists

The rules above support variable-argument list functions in both the K&R and the ANSI dialects of the C language. (Note that argument location is independent of whether a prototype is in scope.)

The nth argument is in either Rn or Fn regardless of the type of parameter in the preceding register slot. Therefore, a function with variable arguments may assume that the variable arguments that lie within the first eight argument slots can be found in either the stacked input integer registers (IN0-IN7), or in the floating-point parameter registers (F8-F15). Using the information codes from the the AI (Argument Information) register (see Table 4-12), the function can then store these registers to memory using the 16-byte scratch area for IN6/F14 and IN7/F15, and up to 48 bytes at the base of its own stack frame for IN0/F8-IN5/F13, as necessary. This arrangement places all of the variable parameters in one contiguous block of memory. Pointers to Formal Parameters

Whenever the address is formed of a formal parameter that is passed in a register, the compiler must store the parameter to the stack, as it would for a variable argument list. Languages Other than C

The placement of arguments in general registers versus floating-point registers does not depend on any notion or concept of a prototype being in scope. It is therefore applicable to all languages at all times. Rounding Floating-point Values

There must be no difference in behavior between a floating-point parameter passed directly in a register and a floating-point parameter that has been stored to memory and reloaded. In either case, the floating-point value must be the same. This implies that floating-point parameters passed in floating-point registers must be explicitly rounded to the proper precision by the caller. Order of Argument Evaluation

Because most high-level languages do not specify the order of evaluation (with respect to side effects) of arguments, those language processors can evaluate arguments in any convenient order. The choice of argument evaluation order and code generation strategy is constrained only by the definition of the particular language. Programs should not depend on the order of evaluation of arguments. Examples

The following examples illustrate the parameter passing conventions. Floating-point types are IEEE floating-point representations.

Scalar Integers and Floats, With or Without Prototype

extern int func(int, double, double, int);
func(i, a, b, j);

The parameters are passed as follows:

Slot Variable Allocation Argument Register Information
0 i OUT0 AI$K_AR_I64
1 a F9 AI$K_AR_FT
2 b F10 AI$K_AR_FT
3 j OUT3 AI$K_AR_I64

Aggregates Passed by Value

extern int func();
struct { int array[20]; } a;
func(i, a);

No padding is provided in the parameter list for the structure (independent of its external alignment). The parameters are passed as follows:

Slot Variable Allocation Argument Register Information
0 i OUT0 AI$K_AR_I64
1-7 a.array[0--13] OUT1--OUT7 AI$K_AR_I64 (all 7 slots)
8-24 a.array[14--19] In memory, at SP+16 through SP+39 Not applicable

extern int func();
struct { __float128 x; int array[20]; } a;
func(i, a);

The parameters are passed as follows:

Slot Variable Allocation Argument Register Information
0 i OUT0 AI$K_AR_I64
1-2 a.x OUT1--OUT2 AI$K_AR_I64 (both slots)
3-7 a.array[0--9] OUT3--OUT7 AI$K_AR_I64 (all 5 slots)
8-21 a.array[10--19] In memory, at SP+16 through SP+55 Not applicable

Floating-Point Aggregates, With or Without Prototype

struct s { float a, b, c; } x;
extern func();

The parameters are passed as follows:

Slot Variable Allocation Argument Register Information
0 x.a & x.b OUT0 AI$K_AR_I64
1 x.c OUT1 AI$K_AR_I64 (low 32 bits)

4.7.6 Return Values

Values up to 128 bits are returned directly in the registers, according to the rules in Table 4-13.

Integer, enumeration, record, and set values (bit vectors) smaller than 64 bits must be zero-filled (unsigned integers, enumerations, records, sets) or sign-extended (signed integrals) to a full 64 bits. However, for unsigned 32-bit integers, bit 31 is replicated in bits 32--63.

When floating-point values are returned in floating-point registers, they are returned in the register format, rounded to the appropriate precision. When they are returned in the general registers (for example, as part of a record), they are returned in their memory format.

OpenVMS does not support a general notion of homogeneous floating-point aggregates. However, the special case of two single-precision or double-precision floating-point values implementing values of a complex type are handled in an analogous manner.

Table 4-13 Rules for Return Values
Type Size (Bits) Location of Return Value Alignment
Integer/Pointer, small Record, Set 1--64 R8 LSB
IEEE single-precision floating-point (S_floating) 32 F8 N/A
IEEE double-precision floating-point
64 F8 N/A
IEEE single-precision complex (S_floating) 64 F8, F9 N/A
IEEE double-precision complex (T_floating) 128 F8, F9 N/A
VAX single-precision floating-point (F_floating) 32 R8 N/A
VAX double-precision floating-point
(D_ and G_floating)
64 R8 N/A
VAX single-precision floating-point complex (F_floating) 64 R8, R9 N/A
VAX double-precision floating-point complex (D_ and G_floating) 128 R8, R9 N/A


X_floating and X_floating complex are not included in this table because they are returned using the hidden parameter method (see below).

The rules in Table 4-13 are expressed in more detail in Table 4-10. F_floating and F_floating complex values in the general registers are zero-extended (Zero64), because this most closely approximates the effect of using the Alpha register format.

Hidden Parameter

Return values other than those covered by Table 4-13 are returned in a buffer allocated by the caller. A pointer to the buffer is passed to the called procedure as a hidden first parameter, and all normal parameters are shifted one slot to make this possible. The return buffer must be aligned at a 16-byte boundary.

4.7.7 Simple and Bound Procedures

There are two distinct classes of procedures:

  • Simple procedure
  • Bound procedure

A simple procedure is a procedure that does not need direct access to the stack of its execution environment. In order to call a simple procedure, a simple function descriptor is created, as shown in Figure 4-7, and described in Table 4-14.

Figure 4-7 Simple Function Descriptor

Table 4-14 Simple Function Descriptor
FDSC$Q_ENTRY Entry code address for the procedure to be called.
FDSC$Q_GP GP value for the procedure to be called.

A bound procedure is a procedure that does need direct access to the stack of its execution environment, typically to reference an up-level variable or to perform a nonlocal GOTO operation.

When a bound procedure is called, the caller must pass some kind of pointer to the called code that allows it to reference its up-level environment. Typically, this pointer is a frame pointer for that environment, but many variations are possible. When the caller itself is executing within that outer environment, it can usually make such a call directly to the code for the nested procedure without recourse to any additional function descriptors. However, when a procedure value for the nested procedure must be passed outside of that environment to a call site that has no knowledge of the target procedure, a bound function descriptor is created so that the nested procedure can be called just like a simple procedure.

Bound procedure values, as defined by this standard, are designed for multilanguage use and utilize the properties of function descriptors to allow callers of procedures to use common code to call both bound and simple procedures.

A bound function descriptor is similar to a simple function descriptor, with several additional fields as shown in Figure 4-8 and described in Table 4-15.

Figure 4-8 Bound Function Descriptor

Table 4-15 Contents of Bound Function Descriptor
Field Name Contents
FDSC$Q_OTS_ENTRY Code address for a suitable library helper routine, for example, OTS$JUMP_TO_BPV
FDSC$Q_OTS_PSEUDO_GP Address of this bound function descriptor
FDSC$Q_SIGNATURE Signature information field (see Section 5.1.3)
FDSC$Q_TARGET_ENTRY Entry code address for the procedure to be called
FDSC$Q_TARGET_GP GP value for the procedure to be called
FDSC$Q_TARGET_ENVIR Environment value for the procedure to be called

A bound procedure descriptor is inherently dynamic because the environment value must be determined at runtime by code executing within the bound procedure environment. Therefore, when a bound procedure descriptor such as this is needed, it is usually allocated on the creating procedure's stack.

When a procedure value that refers to a bound procedure descriptor is used to make a call, the routine designated in the OTS_ENTRY field (typically OTS$JUMP_TO_BPV) receives control with the GP register pointing to the bound procedure descriptor (instead of a global offset table). This routine performs the following steps:

  1. Load the "real" target entry address into a volatile branch register, for example, B6.
  2. Load the dynamic environment value into the appropriate uplevel-addressing register for the target function, for example, OTS$JUMP_TO_BPV uses R9.
  3. Load the "real" target GP address into the GP register
  4. Transfer control (branch, not call) to the target entry address.

Control arrives at the real target procedure address with both the GP and environment register values established appropriately.

Support routine OTS$JUMP_TO_BPV is included as a standard library routine. The operation of OTS$JUMP_TO_BPV is logically equivalent to the following code:

        add     gp=gp,24        ; Adjust GP to point to entry address
        ld8     r9=[gp],16      ; Load target entry address
        mov     b6=r9
        ld8     r9=[gp],-8      ; Load target environment value
        ld8     gp=[gp]         ; Load target GP
        br      b6              ; Transfer to target

Because the address of a bound function descriptor is a valid function pointer, it may be passed to translated code which uses it to call back into native code; therefore, the value of the signature information field must be the same as that in the official function descriptor for the real target procedure (see Section 5.1.2).

Note that there can be multiple OTS$JUMP_TO_BPV-like support routines, corresponding to different target registers where the environment value should be placed. The code that creates the bound function descriptor is also necessarily compiled by the same compiler that compiles the target procedure, thus can correctly select an appropriate support routine.

4.8 Procedure Call Stack

A procedure is an active procedure while its body is executing, including while any procedure it calls is executing. When a procedure is active, its designated condition handler may handle an exception that is signaled during its execution.

Associated with each active procedure is an invocation context, informally called a frame, 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 a limited invocation context that includes the output registers of its caller (which have been "shifted" to start at register R32). The initial instructions may allocate and initialize additional context, including possibly saving information from the invocation context of its caller. Such instructions, if any, are termed a 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 null frame procedure has no prologue and no epilogue, and consists solely of body instructions. Such a procedure becomes active immediately.

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.

A procedure 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.

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

For OpenVMS, the PC (instruction pointer) register in combination with associated unwind information determines what procedure is current (for exception handling purposes). See Section A.4 for a description of the unwind information data structures.

A procedure is current at a given PC (when OpenVMS semantics apply, see Section A.4.1) if either:

  • The PC is in a range described by any body region unwind descriptor but not in an epilogue
  • The PC is in a range not described by any unwind descriptor, and therefore by default must be within a null frame procedure (see Section A.4.1):

4.8.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
  • To examine or modify the register context of an active procedure

This section describes the data structure mechanisms. The run-time library functions that support these functions are described in Section 4.8.3

Previous Next Contents Index