Preface

This document defines the Windows NT for Alpha Systems Calling Standard. There are currently three closely related Alpha calling standards, namely, for Windows NT®, OpenVMS™, and DIGITAL™ UNIX®. This document describes the Windows NT for Alpha Systems variant that is used for the Windows NT for Alpha Systems product.


Copyright and Trademark Information

Revision 1.11

Originally published January 1998 under DIGITAL Trademark

Republished July 1998 under Compaq Trademark

This revision incorporates changes for ECOs 6 through 10.

Compaq Computer Corporation makes no representation that the use of its products in the manner described in this publication will not infringe on existing or future patent rights, nor do the descriptions contained in this publication imply the granting of licenses to make, use, or sell equipment or software in accordance with the description.

Possession, use, or copying of the software described in the publication is authorized only pursuant to a valid written license from Compaq or an authorized sublicensor.

© 1998 Compaq Computer Corporation. All rights reserved

The following are trademarks of Compaq Computer Corporation:

Digital, OpenVMS, VMS, Compaq, the DIGITAL logo and the Compaq logo.

Windows NT is a registered trademark of Microsoft Corporation. UNIX is a registered trademark in the United States and other countries, licensed exclusively through X/Open Company Ltd.


Document Structure

This document contains eight chapters, each describing one aspect of the Windows NT for Alpha Systems call interface.

  • Chapter 1 provides an overview of the standard. It defines applicability, architectural level, and goals of the standard, and it supplies definitions of commonly used terms.

  • Chapter 2 describes basic concepts such as address representation and register usage.

  • Chapter 3 describes the program flow control procedures.

  • Chapter 4 describes data manipulation. It covers the passing and storage of data.

  • Chapter 5 describes event processing. It contains exception-handling considerations and procedures.

  • Chapter 6 describes multiple thread support. It contains stack information for a multithreaded execution environment.

  • Chapter 7 describes invoking procedures and call chains.

  • Chapter 8 describes the procedure descriptors.


Associated Documents

This document is a component of the Alpha Software Architecture. It depends on other standards and conventions that are not described in this document. These standards and conventions include:

  • Object language (including link-time optimizations) and object file format

  • Status values and message definition, formatting, and reporting

  • Heap memory management and dynamic string management

  • Multithread architecture

  • Names and naming conventions

In addition to documentation for Windows NT, other related information can be found in:

  • Alpha System Reference Manual

  • OpenVMS Calling Standard, December 1995

  • Digital UNIX Calling Standard for Alpha Systems, March 1996

  • POSIX 1003.1, IEEE Standard Portable Operating System Interface for Computer Environments - IEEE Std 1003.1-1988

  • American National Standard for Information Systems Programming Language C - ANSI X3.159-1989 (or its ISO equivalent, ISO 9899)


Document Conventions

The conventions used in this document are as follows:

  • Editorial Comment
    \\
All text enclosed in double backslashes, for example, this  paragraph, is editorial comment. This is not a formal part of the standard, and will not necessarily be in future revisions of this document.
\\
  • Constants

    Constants are presented symbolically with their value given at the point of definition in this standard. Concrete language bindings for each constant are provided in system definition files external to this standard.

    Note The symbols used in this document do NOT follow any particular naming conventions nor do they adhere to POSIX or other conventions.

  • Functional Interfaces

    Functional interface syntax is presented in abstract form. Concrete language bindings for each functional interface are provided in system definition files external to this standard.

  • Algorithms

    Algorithms are presented precisely as a series of steps.

  • Conventions

    All conventions that are important to correct program execution are presented in a form appropriate to each convention.

  • Methods

    Actual or recommended methods are presented informally, using examples, suggestions, or other appropriate form.

  • Numbering

    All numbers are represented as decimal values unless otherwise indicated. Non-decimal numbers are typically represented with the name of the base in parentheses following the number; for example, 1B(Hex).

  • Memory and Register Contents Representation

    Figures that represent memory or register layouts follow the convention that increasing addresses run from top to bottom and from right to left on a page. Most significant bits are on the left and least significant bits are on the right.

  • Code Examples

    All code examples in this document are supplied strictly for purposes of explanation. They are presented in a form that expresses the relevant concept with clarity. They do not reflect optimized and properly scheduled code sequences that a compiler would generate.

  • Assembler Syntax

    Assembler syntax follows the Alpha System Reference Manual (including Appendix A, section A.4.3 on Stylized Code Forms) and does not represent actual Alpha Assembler notation.

  • Data Structures

    Data structures are defined in terms of the physical memory format of each structure. Concrete language bindings for each data structure are provided in system definition files external to this standard.

  • Data Structure Fields

    Record fields are referred to by using the name of the record or subrecord followed by a dot, followed by the field name: for example, RECORD_NAME.SUB_RECORD.FIELD.


Chapter 1 Introduction

This standard defines requirements for a native user mode procedure to operate correctly in a multilanguage and multithreaded environment on Windows NT for Alpha Systems, including:

  • Run-time data structures

  • Constants

  • Algorithms

  • Conventions

  • Methods

  • Functional interfaces

These properties include the contents of key registers, the format and contents of various data structures, and the actions that procedures must perform under various circumstances.

This standard also defines properties of the run-time environment that must apply at various points during program execution. These properties vary in scope and applicability. Some properties apply at all points throughout the execution of user mode code and must, therefore, be held constant at all times. Examples of such properties include those defined for the stack pointer and various properties of the call chain navigation mechanism. Other properties apply only at certain points, such as call conventions that apply only at the point of the transfer of control to another procedure.

Furthermore, some properties are optional depending on circumstances. For example, compilers are not obligated to follow the argument list conventions when a procedure and all of its callers are in the same module, have been analyzed by an interprocedural analyzer, or have private interfaces (such as language support routines).

Note The specifications in this document are presented in an as if definition. This simply means that all conforming code must behave as if the specifications have been met. In particular, this calling standard is designed such that additional link-time information can be utilized to optimize or even remove instructions in critical code paths and as such achieve higher performance levels.

In addition, in many cases significant performance gains can be realized by selective use of non-standard calls when the safety of such calls is known. Compiler writers are encouraged to make optimal use of such optimizations as appropriate while always ensuring that procedures outside the compilation unit can proceed as if the letter of the standard were met.

The conventions specified in this standard are intended to exploit fully the architectural and performance advantages of the Alpha hardware architecture, and are designed to provide a leadership execution environment for applications and languages on Windows NT for Alpha Systems. Some of these conventions are visible to the high-level language programmer, and therefore may require source-level changes in high-level language programs when moving them from other environments.

To achieve source-level compatibility and portability between the Windows NT for Alpha Systems environment and various other environments, users should not depend on the properties of this architecture except indirectly through high-level language facilities that are portable across architectures.

By definition, many of the conventions described in this standard differ from other software implementation architectures. Therefore, programs that depend on properties of this architecture may not be portable to other architectures.


1.1 Applicability

This standard defines the rules and conventions that govern the native user mode run-time environment on Alpha systems. It is applicable to all products executing in native user mode on the Windows NT for Alpha Systems operating system.

Specific examples of uses of this standard are:

  • All externally callable interfaces in standard system software

  • All intermodule calls to major software components

  • All external procedure calls generated by language processors without the benefit of interprocedural analysis or permanent private conventions (such as those used for language support run-time library routines)


1.2 Architectural Level

This standard defines an implementation-level run-time software architecture for Alpha operating systems.

The interfaces, methods, and conventions specified in this document are primarily intended for use by implementors of compilers, debuggers and other run-time tools, run-time libraries, and base operating systems. These specifications may be, but are not necessarily, appropriate for use by higher-level system software and applications.

Compilers and run-time libraries may provide additional support of these capabilities via interfaces that are more appropriate for compiler and application use. This specification neither prohibits nor requires such additional interfaces.


1.3 Goals

In general, this calling standard promotes the highest degree of performance, portability, efficiency, and consistency in the interface between called procedures in the Windows NT for Alpha Systems environment.

The standard must be applicable to all intermodule callable interfaces in the native software system. Specifically, the standard must consider the requirements of important compiled languages including Ada, BASIC, BLISS, C, C++, COBOL, FORTRAN, Pascal, LISP, PL/I, and calls to the operating system and library procedures. The needs of other languages that may be supported in the future must be met by the standard or by compatible revision to it.

It is the goal of this Calling Standard to do the following:

  • Support nearly perfect compatibility with the user visible characteristics of the Windows NT for Alpha Systems operating system as implemented on other hardware architectures.

  • Avoid capabilities specifically for lower-level components (such as assembler routines) that cannot be invoked from the higher-level languages.

  • Allow the calling program and called procedure to be written in different languages. The standard is designed to reduce the need to use language extensions for mixed-language programs.

  • Contribute to the writing of error-free, modular, and maintainable software and promote effective sharing and reuse of software modules.

  • Provide the programmer with control over the fixing and reporting of exception conditions and with management of the flow of control when various types of exceptional conditions occur.

  • Provide subsystem and application writers with the ability to override system messages to provide a more suitable application-oriented interface.

  • Add no space or time overhead to procedure calls and returns that do not establish exception handlers.

  • Be optimized for newer, more complex compilation techniques, such as interprocedural analysis and link-time code transformations. However, the standard is designed to require no such mechanisms for correctness.

  • Provide support for a multilanguage, multithreaded execution environment.

  • Provide an efficient mechanism for calling lightweight procedures that do not need the overhead of setting up a stack call frame. (Procedures are referred to as lightweight because of their expedient way of saving the call context.)

  • Provide for the use of a common calling sequence to invoke lightweight procedures that maintain only a register call frame and heavyweight procedures that maintain a stack call frame. (Procedures are referred to as heavyweight when they store the call context in memory on the stack.)

    This calling standard allows a compiler to determine whether or not to use a stack frame based on the complexity of the procedure being compiled. A recompilation of a called routine that causes a change in stack frame usage does not require a recompilation of its callers.

  • Provide exception handling, traceback, and debugging for lightweight procedures that do not have a stack frame.

  • Make efficient and effective use of the Alpha hardware architecture.

  • Minimize the cost of procedure calls.

  • Support a 32-bit address user mode environment.


1.4 Requirements

This calling standard was developed with the following requirements:

  • The standard must be implementable on all Alpha platforms.

  • The standard must be implementable by non-DIGITAL compiler writers.

  • The standard must not require any complex compilation techniques (such as link-time code movement) for correctness.


1.5 Definitions

The following terms are used in this standard:

  • Address: A 32-bit value used to denote a position in memory. In the case of Windows NT for Alpha Systems, an address is sometimes represented by a 64-bit value, in which case the high-order 33 bits of that value must be the same (that is, the 64-bit value must equal the sign-extended 32-bit value).

  • Argument list: A vector of quadword entries that represents a procedure parameter list and possibly a function value.

  • Asynchronous procedure call: An asynchronous interruption of normal code flow caused by some software event. This interruption shares many of the properties of hardware exceptions that includes forcing some out-of-line code to execute.

  • Bound procedure: A type of procedure that requires knowledge (at run-time) of a dynamically determined larger enclosing scope to execute correctly.

  • Call frame: The body of information that a procedure must save to allow it to properly return to its caller. A call frame may exist on the stack or in registers. A call frame may optionally contain additional information required by the called procedure.

  • Descriptor: A mechanism for passing parameters where the address of a descriptor is an entry in the argument list. The descriptor contains the address of the parameter, data type, size, and additional information needed to fully describe the data passed.

  • Dynamic-link library (DLL): A form of shared image on Windows NT for Alpha Systems that can be loaded into a process for execution.

  • Exception code: A 32-bit value used to uniquely identify an exception condition. An exception code can be returned to a calling program as a function value (status) or raised using the exception-handling mechanism.

  • Exception condition (or exception): Some exceptional condition in the current hardware and/or software state that should be noted or fixed. Its existence causes an interrupt in program flow and forces execution of out-of-line code. Such an event may be caused by an exceptional hardware state such as arithmetic overflows, memory access control violations, and so on, or by actions performed by software, such as subscript range checking, assertion checking, or asynchronous notification of one thread by another.

    While the normal control flow is interrupted by an exception, that condition is termed active.

  • Exception handler: A procedure designed to handle exceptions (conditions) when they occur during the execution of a thread.

  • Function: A procedure that returns a single value in accordance with the standard conventions for value returning. Additional values are returned by means of the argument list.

  • Hardware exception: A particular category of exceptions that directly reflect an exceptional condition in the current hardware state that should be noted or fixed by the software. Hardware exceptions may occur synchronously or asynchronously with respect to the normal program flow.

  • Image: A collection of compiled modules that are combined by a linker into a form that is ready to be loaded for execution.

  • Immediate value: A mechanism for passing input parameters where the actual value is provided in the argument list entry by the calling program.

  • Language support procedure: A procedure called implicitly to implement higher-level language constructs. Such procedures are not intended to be explicitly called from user programs.

  • Library procedure: A procedure explicitly called using the equivalent of a call statement or function reference. Such procedures are usually language independent.

  • Natural alignment: An attribute of certain data types that refers to the placement of the data, such that the lowest addressed byte of the data has an address that is a multiple of the size of the data in bytes. Natural alignment of an aggregate data type generally refers to an alignment, such that all members of the aggregate are naturally aligned.

    There are five natural alignments defined by this standard:

    • Byte — Any byte address

    • Word — Any byte address that is a multiple of two

    • Longword — Any byte address that is a multiple of four

    • Quadword — Any byte address that is a multiple of eight

    • Octaword — Any byte address that is a multiple of 16
  • Procedure: A closed sequence of instructions that is entered from and returns control to the calling program.

  • Procedure value: An address value that represents a procedure — the address of the first instruction of the procedure to be executed.

  • Process: An address space and at least one thread of execution. Selected security and quota checks are done on a per process basis.

    This standard anticipates the possibility of the execution of multiple threads within a process. An operating system that only provides a single thread of execution per process is considered a special case of a multithreaded system where the maximum number of threads per process is one.

  • Reference: A mechanism for passing parameters where the address of the parameter is provided in the argument list by the calling program.

  • Shareable image: An image that can shared by multiple processes. On Windows NT for Alpha Systems, a single copy of the image can be simultaneously included at the same address in multiple using processes. (That is, a shareable image is generally not position independent.)

  • Signal: A POSIX-defined concept used to cause out-of-line execution of code.

  • Standard call: Any transfer of control to a procedure by any means that presents the called procedure with the environment defined by this document and does not place additional restrictions, not defined by this document, on the called procedure.

  • Standard conforming procedure: A procedure is said to be standard conforming if it adheres to all the relevant rules set forth in this document.

  • Thread of execution (or thread): An entity scheduled for execution on a processor. In language terms, a thread is a computational entity utilized by a program unit. Such a program unit might be a task, procedure, loop, or some other unit of computation.

    All threads executing within the same process share the same address space and other process context, but have unique per-thread hardware context that includes program counter, processor status, stack pointer, and other machine registers.

    This standard applies only to threads that execute within the context of a user mode process and are scheduled on one or more processors according to software priority. All subsequent uses of the term thread in this standard refer to such user mode process threads only.

  • Thread safe code: A property of code compiled in such a way as to ensure it will execute properly when run in a threaded environment. This usually adds extra instructions to do certain run-time checks and requires that thread local storage be accessed in a particular fashion.

  • Undefined: Operations or behavior for which there is no directing algorithm used across all implementations that support this standard. Such operations may or may not be well defined for a single implementation, but still remain undefined with reference to this standard. The actions of undefined operations may not be required by standard conforming procedures.

  • Unpredictable: Any results of an operation that cannot be guaranteed across all implementations of this standard. These results may or may not be well defined for a single implementation, but remain unpredictable with reference to this standard. All results caused by operations defined in this standard but where the results are not explicitly specified in this standard are considered unpredictable. No standard conforming procedure may depend on unpredictable results.


Chapter 2 Basic Considerations

This section describes some fundamental concepts of the Windows NT for Alpha Systems Calling Standard.


2.1 Address Representation

One feature of the Windows NT for Alpha Systems flavor of the calling standard is that in all cases bits <63:31> of all values that represent addresses are identical. In most cases this fact is used to save storage since only 32 bits are needed to represent an address. Structures that contain low-level machine state sometimes allocate a full 64 bits for addresses even though bits <63:31> are always the same.


2.2 Procedure Representation

One of the distinguishing characteristics of any calling standard is how procedures are represented. The term used to denote the value that uniquely identifies a procedure is a procedure value. If the value identifies a bound procedure then it is called a bound procedure value.

In the Windows NT for Alpha Systems calling standard, a simple (not bound) procedure value is defined as the address of the first instruction of that procedure's entry code.

A bound procedure value is defined as the address of the first instruction of an instruction sequence that establishes the correct execution context for the bound procedure.

In addition, procedures in the Windows NT for Alpha Systems calling standard are associated with a data structure called a procedure descriptor. This structure describes various aspects of the procedure's code that are required for correct and robust exception handling. The exception processing described by this standard is based on the assumption that any given program counter value can be mapped to an associated procedure descriptor that describes the currently active procedure.

Table2-1 General Purpose, Integer Register Usage

Register | Usage
R0 | Function value register. In a standard call that returns a non-floating-point function result in a register, the result must be returned in this register. In a standard call, this register may be modified by the called procedure without being saved and restored.
R1..R8 | Conventional scratch registers. In a standard call, these registers may be modified by the called procedure without being saved and restored.
R9..R14 | Conventional saved registers. If a standard-conforming procedure modifies one of these registers, it must save and restore it.
R15 | FP, Stack Frame Base register. For procedures with a run-time variable amount of stack, this register is used to point at the base of the stack frame (fixed part of the stack). For all other procedures this register has no special significance. If a standard-conforming procedure modifies this register, it must save and restore it.
R16..R21 | Argument registers. In a standard call, up to six non-floating-point items of the argument list are passed in these registers. In a standard call, these registers may be modified by the called procedure without being saved and restored.
R22..R25 | Conventional scratch registers. In a standard call, these registers may be modified by the called procedure without being saved and restored.
R26 | RA, Return Address register. In a standard call, the return address must be passed and returned in this register.
R27 | Conventional scratch register. In a standard call, this register may be modified by the called procedure without being saved and restored.
R28 | Volatile scratch register. The contents of this register are always unpredictable after any external or internal transfer of control to or from a different procedure. This applies to both standard and nonstandard calls. This register may be used by the operating system for external call fixup, autoloading, and exit sequences. R28 can be freely used as a scratch register within a procedure.
R29 | GP, Global Pointer or saved register. In a main (static) image, this register contains a pointer to a region of global storage (addresses and variables) constructed by the linker. In a dDynamic-lLink library (DLL), this register must not be used in any way.
R30 | SP, the Stack Pointer. This register contains a pointer to the top of the current operating stack. Aspects of its usage and alignment are defined by the hardware architecture. Various software aspects of its usage and alignment are defined in Section 3.2.1, Call Conventions.
R31 | RZ, ReadAsZero/Sink. Hardware defined: binary zero as a source operand, sink (no effect) as a result operand.


2.3 Register Usage Conventions

This section describes the usage of the Alpha hardware general purpose (integer) and floating-point registers.


2.3.1 Integer Registers

In a standard-conforming procedure the general purpose, integer registers are used as shown in Table 2-1.


2.3.2 Floating-Point Registers

In a standard-conforming procedure the floating-point registers are used as shown in Table 2-2.

Table 2-2 Floating-Point Register Usage

Register | Usage
F0 | Floating-point function value register. In a standard call that returns a floating-point result in a register, this register is used to return the real part of the result. In a standard call, this register may be modified by the called procedure without being saved and restored.
F1 | Floating-point function value register. In a standard call that returns a complex floating-point result in registers, this register is used to return the imaginary part of the result. In a standard call, this register may be modified by the called procedure without being saved and restored.
F2..F9 | Conventional saved registers. If a standard-conforming procedure modifies one of these registers, it must save and restore it.
F10..F15 | Conventional scratch registers. In a standard call, may be modified by the called procedure without being saved and restored.
F16..F21 | Argument registers. In a standard call, up to six floating-point arguments may be passed by value in these registers. In a standard call, these registers may be modified by the called procedure without being saved and restored.
F22..F30 | Conventional scratch registers. In a standard call, these registers may be modified by the called procedure without being saved and restored.
F31 | ReadAsZero/Sink. Hardware defined: binary zero as a source operand, sink (no effect) as a result operand.


2.3.3 Register Names

The register names used in this standard follow the conventions used in the Alpha System Reference Manual. That is, the names of general registers begin with "R" and the names of floating-point registers begin with "F". These same names are also used in the examples in this text.

This register naming convention is not necessarily followed by compiler or assembler tools used on Alpha systems. However, in most cases a simple name substitution can convert from the notation used here to the appropriate convention. For example, the following might be used in conjunction with the standard C preprocessor as a prelude to the examples in this text:

/*   replacement    also known as */
#define R0    $0    /* v0         */
#define R1    $1    /* t0         */
...
#define R8    $8    /* t7         */
#define R9    $9    /* s0         */
...
#define R14   $14   /* s5         */
#define R15   $15   /* fp or s6   */
#define FP    $15
#define R16   $16   /* a0         */
...
#define R21   $21   /* a5         */
#define R22   $22   /* t8         */
...
#define R25   $25   /* t11        */
#define R26   $26   /* ra         */
#define RA    $26
#define R27   $27   /* t12        */
#define R28   $28   /* at         */
#define R29   $29   /* gp         */
#define GP    $29
#define R30   $30   /* sp         */
#define SP    $30
#define R31   $31   /* zero       */
#define F0    $f0
#define F1    $f1
#define F2    $f2
...
#define F9    $f9
#define F10   $f10
...
#define F15   $f15
#define F16   $f16
...
#define F21   $f21
#define F22   $f22
...
#define F30   $f30
#define F31   $f31


2.4 Program Image Layout

The Windows NT for Alpha Systems Calling Standard does not define many aspects of an executable image. However, there is one basic concept that is defined to permit optimal access to static data.

A hardware architecture that has the property that instructions cannot contain full virtual addresses is sometimes referred to as a base register architecture. The Alpha architecture is such an architecture. In a base register architecture, normal memory references within a limited range from a given address are expressed by using displacements relative to the contents of some register that contains that address (usually referred to as a base register). Base registers for external program segments, either data or code, are usually loaded indirectly through a program segment of address constants.

To optimize this base register access method, this standard allows the main (static) image of an executable program to have a single global storage region that is addressable by the GP register. Together, the linker and the compilers arrange that various static data is collected together into a single such region that is shared across all procedures in the main image.

During the compilation process a compiler generates object language to designate data as belonging in the global segment. The linker pools these contributions to form the segments; in particular, address constants, literals, and external storage from multiple compilations can often be combined. The total contributions of all modules of the main image must not exceed 64K bytes of contributions after pooling.

For dynamic-link libraries, no shared global segment is provided. A separate compilation mode is required that neither makes contributions to a global segment nor assumes the availability of a global pointer for addressing. (In this latter compilation mode, the GP must not be used in any way.)


Chapter 3 Flow Control

The following sections contain descriptions of various aspects of the calling standard that deal with the flow of control of a program (as opposed to data manipulation).


3.1 Procedure Types

This standard defines three basic types of procedures. A compiler may choose which type to generate based on the requirements of the procedure in question.

The standard procedure types are:

  • Stack frame procedure — A procedure that maintains its caller's context on the stack

  • Register frame procedure — A procedure that maintains its caller's context in registers

  • Null frame procedure — A procedure that executes in the context of its caller

Some procedures maintain their call frame on the stack, others maintain their call frame entirely in registers (although they may use the stack). Very simple procedures do not necessarily maintain any call frame at all and simply execute in the context of their caller. The calling procedure need not distinguish these cases.


3.1.1 Procedure Descriptor Overview

Every procedure other than a null procedure must have a structure associated with it that describes which type of procedure it is as well as various other characteristics of the procedure. This structure, called a procedure descriptor, is a longword-aligned data structure that provides basic information about a procedure. This data structure is used to interpret the call chain at any point in a thread's execution. It is normally built at compile time and is not normally accessed at run time except in support of exception processing or other rarely used code execution.

Table 3-1 contains a brief summary of the properties of a procedure that can be determined from its associated procedure descriptor. Some of these properties are explicitly represented in the procedure descriptor and others must be derived by examination of the code in a procedure's prologue. (Some fields only apply to certain kinds of procedures.) This summary is included here because many of these properties are mentioned in the descriptions that follow in this chapter. For a complete description of procedure descriptors, see Procedure Descriptor Representation.

Table 3-1 Procedure Properties Summary

Field name | Description
REGISTER_FRAME ¹ | Indicates a register (or null) frame procedure rather than a stack frame procedure.
BASE_REG_IS_FP ¹ | Indicates register R15 is used as a frame pointer rather than as just a preserved register.
HANDLER_VALID ¹ | Indicates there is an associated exception handler.
EXCEPTION_MODE | Indicates the error-reporting behavior expected of certain called mathematical library routines.
ENTRY_RA ¹ | Register that contains the return address at the time of a call. This is always R26.
SAVE_RA ¹ | Register in which the return address is saved (when not saved on the stack).
FRAME_SIZE ¹ | Size in longwords of the (fixed part of the) stack frame.
SP_SET ¹ | Offset in instructions from the beginning of the procedure to the instruction that changes the stack pointer.
ENTRY_LENGTH ¹ | Number of instructions in the procedure prologue.
BEGIN_ADDRESS | Address of the first instruction (and entry point) of the procedure.
PROLOG_END_ADDRESS | Address of the first instruction following the prologue of the procedure.
END_ADDRESS | Address of the first location following the last instruction of the procedure.
HANDLER_ADDRESS | Address of an associated exception-handling procedure.
HANDLER_DATA | Supplementary data to be passed to an associated exception handler.

1 This field is not explicitly represented in the procedure descriptor.


3.1.2 Stack Frame Procedure

A stack frame procedure is one that allocates space for and saves its caller's context on the stack. This type of procedure is sometimes called a heavyweight procedure, referring to the cost of storing this context in memory.

Such a procedure can save and restore registers and may make standard calls to other procedures.

The stack frame of this type of procedure consists of a fixed part (the size of which is known at compile time) and an optional variable part. Certain optimizations can be done if the optional variable part is not present. Compilers must be careful to recognize situations that can effectively cause a variable part of the stack to exist in nonintuitive ways, such as:

  • A called routine may use the stack as a means to return certain types of function values.

If any such situation exists a compiler must choose to use a variable-size stack frame procedure when compiling the caller so that an unwind operation can be done correctly.

Stack Frame Format

Although the compiler determines the exact contents of a stack frame, certain properties are common to all stack frames. The two basic flavors of stack frames are described below.

The following figure illustrates the format of the stack frame for a procedure with a fixed amount of stack that uses the SP as the stack base register (i.e., BASE_REG_IS_FP is 0). In this case, R15 is simply another saved register and otherwise has no special significance.

Some parts of the stack frame are optional and occur only as required by the particular procedure. Brackets surrounding a field's name indicate the field is optional.

Figure 3-1 Fixed Size Stack Frame Format

                                                 octaword-aligned
+---------------+---------------+---------------+---------------+
|                                                               | :0
|                                                               |(From SP)
|                                                               |
|                  [fixed temporary locations]                  |
| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
|                      [argument home area]                     |
|                                                               |
+---------------+---------------+---------------+---------------+
|                                                               | :FRAME_SIZE
|                                                               |(From SP)
|                  [arguments passed in memory]                 |
|                                                               |
|                                                               |
+---------------+---------------+---------------+---------------+

The following figure illustrates the format of the stack frame for procedures with a varying amount of stack that uses the FP as the stack base register (i.e., BASE_REG_IS_FP is 1).

Some parts of the stack frame are optional and occur only as required by the particular procedure. Brackets surrounding a field's name indicate the field is optional.

Figures 3-2 Variable Size Stack Frame Format

                                                 octaword-aligned
+---------------+---------------+---------------+---------------+
|                                                               | :0
|                                                               |(From SP)
:                    [stack temporary area]                     :
:                                                               :
:                                                               :
|                                                               |
|                                                               |
+---------------+---------------+---------------+---------------+
|                                                               | :0
|                                                               |(From FP)
|                  [fixed temporary locations]                  |
| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
|                      [argument home area]                     |
|                                                               |
+---------------+---------------+---------------+---------------+
|                                                               | :FRAME_SIZE
|                  [arguments passed in memory]                 |(From FP)
|                                                               |
|                                                               |
+---------------+---------------+---------------+---------------+

In either case the portion of the stack frame designated by FRAME_SIZE must be allocated and initialized by the entry code sequence of a called procedure with a stack frame.

Fixed Temporary Locations

Fixed temporary locations are optional sections of the stack frame that contain language-specific locations required by the procedure context of some high-level languages. This may include, for example, register spill area, language-specific exception-handling context (such as language dynamic exception-handling information), fixed temporaries, etc.

If a compiler chooses, the fixed temporary locations adjacent to the area pointed to by the frame base register plus FRAME_SIZE can be used for a special purpose termed the argument home area. The argument home area is a region of memory used by the called procedure for the purpose of assembling in contiguous memory the arguments passed in registers, adjacent to the arguments passed in memory, so that all arguments can be addressed as a contiguous array. This area may also be used to store arguments that are passed in registers if an address for such an argument must be generated. Generally, either 6 or 12 contiguous quadwords of stack storage will be allocated by the called procedure for this purpose.

A compiler may use the stack temporary area for fixed local variables, such as constant-sized data items and program state, as well as for dynamically sized local variables. The stack temporary area may also be used for dynamically sized items with a limited lifetime; for example, a dynamically sized function result or string concatenation that can't be directly stored in a target variable. When a procedure uses this area, the compiler must keep track of its base and reset SP to the base to reclaim storage used by temporaries.

The high-address end of the stack frame is defined by the value stored in FRAME_SIZE plus the contents of SP or FP, as indicated by BASE_REG_IS_FP. The high-address end is used to determine the value of SP for the predecessor procedure in the calling chain.

Note Floating registers saved in the stack are stored as a 64-bit exact image of the register; i.e., no reordering of bits is done on the way to or from memory. Compilers must use an STT instruction to store the register regardless of floating-point type.


3.1.3 Register Frame Procedure

A register frame procedure does not maintain a call frame on the stack and must therefore save its caller's context in registers. This type of procedure is sometimes referred to as a lightweight procedure, referring to the relatively fast way of saving the call context.

Such a procedure cannot save and restore nonscratch registers. Because a procedure without a stack frame must therefore use scratch registers to maintain the caller's context, such a procedure cannot make a standard call to any other procedure.

A procedure with a register frame can have an exception handler and can handle exceptions in the normal way. Such a procedure can also allocate local stack storage in the normal way, although it will not necessarily do so.

Note Lightweight procedures have more freedom than might be apparent. By use of appropriate agreements with callers of the lightweight procedure, with procedures that the lightweight procedure calls, and by the use of unwind handlers, a lightweight procedure may modify nonscratch registers, and may call other procedures.

Such agreements may be by convention (as in the case of language support routines in the RTL) or by interprocedural analysis. Calls employing such agreements are, however, not standard calls, and might not be fully supported by a debugger since, for instance, it might not be able to find the contents of the preserved registers.

Since such agreements must be permanent (for upward compatibility of object code), lightweight procedures should in general follow the normal restrictions.


3.1.4 Null Frame Procedure

A null frame procedure is a particularly simple, special case of a register frame procedure, in which:

  • The entry return address register is R26 (ENTRY_RA = 26).

  • The return address is not saved in any other register (SAVE_RA = ENTRY_RA).

  • No stack space is allocated (SP_SET = 0 and FRAME_SIZE = 0).

  • As a result of the above, the prologue requires no instructions (ENTRY_LENGTH = 0).

  • There is no associated exception handler (HANDLER_ADDRESS = 0).

This special case of a register frame procedure is of interest because such procedures may not need an associated procedure descriptor.


3.2 Transfer of Control

A standard procedure call can use any sequence of instructions that present the called routine with the required environment. Although this is true, it is believed that the vast majority of standard conforming external calls will be done with a common sequence of instructions and conventions. This common set of call conventions is so pervasive that it is included for reference as part of this standard.

One important feature of the calling standard is that it is designed such that the same instruction sequence can be used to call each of the different types of procedure. That is, the caller does not have to know which type of procedure is being called.


3.2.1 Call Conventions

The call conventions describe the rules and methods used to communicate certain information between the caller and the called procedure during invocation and return. For a standard call, these conventions include the following:

  • Return Address

    The calling procedure must pass to the called procedure the address to which control must be returned during a normal return from the called procedure. In most cases, the return address is the address of the instruction following the one that transferred control to the called procedure. For a standard call, the return address is passed in the return address register (R26).

  • Argument List

    The argument list is an ordered set of zero or more argument items, that together comprise a logically contiguous structure known as an argument item sequence. This logically contiguous sequence is in practice mapped to registers and memory in a fashion that produces an argument list that is not physically contiguous. In a standard call, the first six items are passed in registers R16..R21 and/or registers F16..F21. The remaining items are collected in a memory argument list that is a naturally aligned array of quadwords. In a standard call, this list (if present) must be passed at 0 (SP).

  • Function Result

    If a standard-conforming procedure is a function, and the function result is returned in a register, then the result is returned in R0, F0, or F0..F1. Otherwise, the function result is returned via the first argument item or dynamically.

  • Stack Usage

    The stack pointer (SP) must at all times denote an address that has octaword alignment. (This has the side effect that the in-memory portion of the argument list, if any, starts on an octaword boundary.) During a procedure invocation the SP may never be set to a value that is higher than the value of the SP at entry to that procedure invocation.

    The contents of the stack located above the portion of the argument list that is passed in memory (if any) belong to the calling procedure and should therefore not be read or written by the called procedure, except as specified by indirect arguments or language-controlled up level references.

    SP is used by the hardware in raising exceptions and asynchronous interrupts. It must be assumed that the contents of the stack below the current SP value and within the stack for the current thread are continually and unpredictably modified, as specified in the Alpha System Reference Manual and as a result of asynchronous software actions.


3.2.2 Linkage

In a main (static) image, the GP register is initialized to point to the shared global storage region at the beginning of program execution. The GP register must not be modified by any procedure that is included in either the main image or a dynamic-link library.

Because there is no GP or other base pointer available to code that is part of a dynamic-link library image, addressing data that is not part of the same compilation generally requires using an LDAH/LDA sequence to dynamically create a suitable base address. Such code thereby becomes non–position independent (non-PIC). That is, it requires relocation if it is to be moved and executed at a different virtual address. You can minimize the number of such relocations in the code stream by loading a linkage pointer (a sort of "procedure local GP" value) near the beginning of a procedure's execution, with the address of a linkage section, if needed, where the linkage section contains variables, addresses, and/or literal data that is needed by the code. (This decreases the density of non-PIC references in the code stream.)


3.2.3 Link-Time Optimization

The design of this calling standard assumes that the system linker will assist in implementing a transfer of control to a target location that is too far away to reach with a BSR instruction.

A typical standard call looks like:

        BSR     FOO'

where FOO' is the displacement from the location of the BSR instruction to the entry point of the target procedure FOO. If the displacement is too large to be represented in the 21-bit displacement field of the BSR instruction, or if the displacement is not known at link-time, then the linker must intervene to create code such as the following:

FOO":   LDAH    R28, FOO-hi(R31)
        LDA     R28, FOO-lo(R28)
        JMP     (R28)

The value of FOO' is then the displacement from the location of the BSR instruction to the FOO" entry in the exit transfer vector. The exit transfer vector loads the full absolute target address for procedure FOO and completes the transfer of control. Note that control does not return to the transfer vector; rather, it returns to the location following the BSR instruction.

Note This design requires that the Windows NT for Alpha Systems operating environment manage the offsets in the LDAH/LDA instructions of the exit transfer vector whenever a dynamic-link library image is loaded and/or relocated.


3.2.4 Calling Computed Addresses

Most calls are made to a fixed address whose value is completely determined by the time the program starts execution. There are, however, certain cases that cause the exact address not to be determined until the code is actually executed. In this case, the procedure value representing the procedure to be called will be computed in a register.

Suppose R4 contains such a computed procedure value (simple or bound). An example of the code to call the procedure that it describes is:

          JSR     R26, (R4)


3.2.5 Bound Procedure Values

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. 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 non-local goto. Both a simple procedure and a bound procedure will have an associated procedure descriptor as described in previous sections. Bound procedure values are designed for multilanguage use and allow callers of procedures to use common code to call both bound and simple procedures.

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 such a pointer is the frame pointer for that environment, but many variations are possible. When the caller is itself executing within that outer environment then it can usually make such a call directly to the code for the nested procedure without recourse to any additional mechanism. 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 special bound procedure is created so that the nested procedure can be called just like a simple procedure.

The procedure value of a bound procedure is defined as the address of the first instruction of a sequence of instructions that establish the proper environment for the bound procedure and then transfer control to that procedure.

One direct scheme for constructing a jacket to a bound procedure that can be called like a simple procedure is to allocate a sequence of instructions on the stack and use the address of those instructions as the procedure value. Assume that a bound procedure named PROC expects its static link to be passed in R1. Then, a suitable sequence of instructions might look like the following:

    LDAH    R1,frame-hi(R31)     ;Create up-level pointer in R1
    LDA     R1,frame-lo(R1)      ;
    LDAH    R28,PROC-hi(R31)     ;Materialize address of bound procedure
    LDA     R28,PROC-lo(R28)     ;
    JMP     (R28)                ;Transfer to the bound procedure

Note that this sequence can be created only by code that is executing within the context of the containing procedure so that the appropriate frame pointer value is known and can be easily incorporated into the sequence illustrated. The lifetime of this sequence is, of course, limited to the lifetime of the stack frame in which it is allocated.

After creating the above jacket instructions, it is necessary to execute an IMB instruction prior to execution of them to ensure instruction cache coherence, as described in the Alpha System Reference Manual.


3.2.6 Entry and Exit Code Sequences

This section describes the steps that must be executed in procedure entry and exit sequences. These conventions must be followed in order for the call chain to be well defined at every point during thread execution.

Entry Code Sequence

Since the value of the PC defines the currently executing procedure, it must be possible using the procedure descriptor associated with that PC, as well as the instructions of the procedure itself, to recover the identity (PC) and environment of the caller. This leads to the following restriction: A standard call cannot be made from the prologue.

Note If an exception is raised or an exception occurs in the prologue of a procedure, that procedure's exception handler (if any) will not be invoked since the procedure is not current yet. This implies that if a procedure has an exception handler then 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, save the caller's context, and make the called procedure current (by executing the last instruction of the procedure prologue).

This is done by performing the following actions in the order given:

  1. If stack space is allocated (FRAME_SIZE ¹ SP - FRAME_SIZE).

    After any necessary calculations and stack limit checks, this step must be completed by exactly one instruction that modifies SP.

  2. For a stack frame procedure (REGISTER_FRAME is 0): Store the return address and any registers that are being preserved in the stack frame. The stack frame reference must use SP.

    Note If the locations allocated for saving the registers must be more than 32768 bytes away from the top of stack (for example, because the procedure contains an actual parameter list whose size exceeds 32768 bytes), then it is not possible to allocate the entire stack in a single step. In this case, the variable stack size form of stack frame must be used. (Recall that memory reference instructions in the Alpha architecture can address at most 32768 bytes relative to the contents of some general register.)

  3. Execute TRAPB if required.

  4. For a variable-sized stack frame procedure (BASE_REG_IS_FP is 1), copy register SP to register FP.

    This step must be completed by exactly one instruction that modifies FP.

When the above steps have been completed, the executing procedure is said to become current for the purposes of exception handling. The handler for a procedure will not be called except when that procedure is current.

It is possible for a single routine to have more than one entry point, hence more than one entry code sequence of instructions. When this occurs, all of the entry code sequences must be equivalent in the sense that after execution leaves each prologue, any one of the entry code sequences can be used for exception-handling purposes regardless of which entry code sequence was actually used to enter the routine. The fixed-size stack allocation, where specific registers are saved in the stack frame, and so on, are all properties of the routine as a whole, not a particular entry point, and every entry point must establish the same run-time frame before completing the prologue. (In general, after execution completes one of the prologues, control passes into code that is common to all entry points and it is not possible to tell which entry was actually called.)

Unwindable Entry Code Instructions

For each of the steps described above, there are specific instructions or sequences of instructions that must be used. These are the only instructions that are interpreted when unwinding a stack frame for purposes of exception dispatching or stack walking. (Such prologue interpretation is sometimes called reverse execution. See Getting a Procedure Invocation Context with a Routine for related discussion.)

  1. Allocate stack

    Let N be the number of bytes to allocate for the fixed part of the stack. The simpler case occurs when the size of the extension does not exceed the value of MAX_NOCHK_EXTEND, 4096, and can therefore be represented in the 16-bit offset field of an LDA instruction. In this case, use

    LDA   SP, -N(SP)

If the previous case does not apply, then use the sequence

load  Rx, N
...
SUBQ  SP, Rx, SP

where Rx is a scratch register for the procedure and load stands for one of a number of alternatives for loading the constant N into register Rx. The load of Rx and update of the stack pointer are separated by instructions that check that the stack limit is not exceeded by the allocation as described in Section 6.1, Stack Limit Checking.

The following instructions may be used to load a constant value N into a register Rx:

BIS    R31, N, Rx       ; 0 ≤? N ≤? 255

or

ADDQ   R31, N, Rx       ; 0 ≤? N ≤? 255

or

LDA    Rx, N(R31)       ; 0 ≤? N ≤? 32767

or

LDAH   Rx, Hi(R31)      ; 0 ≤? Hi ≤? 32767, N = Hi*65536

or

LDAH   Rx, Hi(R31)      ; 0 ≤? Hi ≤? 32767
LDA    Rx, Lo(Rx)       ; -32768 ≤? Lo ≤? 32767
                        ; N = Hi*65536 + Lo

In the last case, the LDAH and LDA instructions need not be contiguous.

  2. Save return address and preserved registers

    General-purpose registers that are saved on the stack must be saved using

    STQ    Rx, n(SP)

    where Rx is the register being saved (including the return address register) and n is the offset in the stack for saving that register. Similarly, floating-point registers that are saved on the stack must be saved using

    STT    Fx, n(SP)

    where Fx is the register being saved and n is the offset in the stack for saving that register. (Note that floating-point registers are never saved using STF, STD, STG, or STS because the called routine has no a priori information about the type of the variable contained in the register. Similarly, LDT is always used to restore a floating register.)

    Preservation of registers in this manner is not limited to just the standard preserved integer and floating-point registers.

    A register that is preserved may also be saved by moving it to a scratch register in the case of a register frame procedure.

    There are no requirements concerning the order in which registers are saved or the position within the stack frame where they are saved.

    The first use of a register that is preserved within a prologue must be to save it. The save operation may consist of a sequence of moves that start with a given register, copy the contents to other registers, and optionally store the register in the stack. (This may be necessary, for example, to temporarily preserve the return address if a routine is called in the prologue to perform stack limit checking. Note that such a called routine is necessarily nonstandard.) The last location in this sequence (whether a register or a stack location) must thereafter not be modified during the execution of the procedure. The registers involved in such a sequence (other than the last if the contents is not stored in the stack) can be used for other purposes provided, either

    • They do not occur in any of the instructions described in this section, or

    • They occur following the last instruction of the sequence.

    In the case of the return address register (R26), the occurrence of more than one of the above instructions that reads that register in a prologue is reserved for use by the kernel. The occurrence of more than one such instruction is used in kernel routine prologues that are invoked by exceptions and traps to indicate that the associated frame is an exception frame (as defined in the Alpha System Reference Manual); see Section 7.3 for further discussion. If there is a need to use the return address value in the prologue in addition to saving it in the normal manner, then the return address value must be read from the saved location rather than directly from R26.

  3. Force pending exceptions

    The TRAPB instruction is used to ensure that any pending exceptions occur prior to the end of the prologue (prior to the routine becoming current).

  4. Initialize frame pointer register

    The frame pointer must be set using

    MOV     SP, FP
  5. Move a register to another register

    A general register Rx must be moved (MOV) to another general register Ry using

    BIS     R31, Rx, Ry

or

BIS     Rx, Rx, Ry

or

BIS     Rx, R31, Ry

    A floating-point register Fx must be moved to another general register Fy using

    CPYS    Fx, Fx, Fy

Note The SP register is normally "saved" and "restored" by adjusting its contents by a fixed value that is part of the instruction stream rather than by copying its contents to another register or to a location on the stack. However, a STx SP,n(SP) or MOV SP,Rx instruction is reverse executed as just described in paragraphs 2 and 5 above. It follows that these instructions can be included in a procedure prologue only if their reverse execution is in fact intended as part of restoring the SP contents. In particular, code scheduling must not allow such instructions, whose use of the SP contents is unrelated to SP maintenance, to be moved into the prologue of a procedure.

Prologue Length

As a general rule, it is valid to include instructions in the prologue in addition to those required above in order to take advantage of available processor cycles that are not otherwise used. However, to avoid undesirable reverse execution overhead during exception, dispatching or unwinding the length of the prologue should not be increased in ways that do not exploit such available cycles except possibly to include instructions that have an unusually long latency. In any case, it must not be possible for such additional instructions to cause an exception that would be handled by that procedure if that exception were raised after the procedure became current. (An exception may be considered to be not handled by a procedure if it is known a priori that the handler will always resignal that exception.)

It follows from these considerations that the length of the prologue of a procedure should typically be no larger than two instructions times the number of registers that are preserved in that procedure, in addition to those instructions that perform stack limit checking (if any). In Windows NT for Alpha Systems, there must not be more than 1024 instructions in a prologue. If a larger number is detected, then the containing program may be aborted.

Frame Pointer Conventions

After procedure prologue completion, the register indicated by BASE_REG_IS_FP must contain the frame pointer of the stack frame, which is the address of the lowest-addressed byte of the fixed portion of the stack frame allocated by the procedure prologue. The value of the frame pointer is the value of FRAME_SIZE subtracted from the value of the stack pointer upon procedure entry.

For fixed frame procedures, the frame pointer is the stack pointer, which is not modified by that procedure after the instruction in that procedure prologue specified by SP_SET.

Entry Code Example for a Stack Frame

This example assumes that this is a stack frame procedure, that registers R9..R11 and F2..F3 are saved and restored, that the procedure has a static exception handler that does not reraise arithmetic traps, and that the procedure uses a fixed amount of stack (BASE_REG_IS_FP is 0).

LDA     SP,-SIZE(SP)    ;Allocate space for new stack frame
STQ     R26,16(SP)      ;Save return address
STQ     R9,24(SP)       ;Save first integer register
STQ     R10,32(SP)      ;Save next integer register
STQ     R11,40(SP)      ;Save next integer register
STT     F2,48(SP)       ;Save first floating-point register
STT     F3,56(SP)       ;Save last floating-point register
TRAPB                   ;Force any pending hardware exceptions
                        ;  to be raised
                        ;Called procedure is now the current procedure

Entry Code Example for a Register Frame

The following entry code example is for a called procedure that has no static exception handler. Both SAVE_RA and ENTRY_RA specify R26 and the procedure utilizes a fixed amount of stack storage (BASE_REG_IS_FP is 0).

LDA     SP,-SIZE(SP)       ;Allocate space for new stack frame
                           ;Called procedure is now the current procedure

Exit Code Sequence

The end of procedure entry code can be determined easily given a PC value together with the ENTRY_LENGTH value. Since there may be multiple return points from a procedure, detecting that a procedure exit sequence is being executed is not quite as straightforward. Unwind support routines must be able to detect if the stack pointer has been reset or not and, if not, how to reset it. This is done by using a reserved instruction sequence.

Reserved Instruction Sequence for Procedure Exit

To allow the stack to be properly restored during an unwind, a reserved instruction or sequence of instructions must be used. None of these sequences may be used in any other way.

The following reserved instruction must appear at every exit point from any procedure that uses stack (FRAME_SIZE ¹ 0):

RET     R31,(Rn),0001      ;Return to caller with usage hint 0001

Note Usage hint refers to the value of the branch prediction bits encoded in the RET instruction. The Alpha System Reference Manual documents that these bits, <13:0> of the instruction longword, are reserved to software when the instruction is RET or JSR_COROUTINE. (See Section 4.3 of the Alpha System Reference Manual.) This calling standard further requires that these bits contain the value 0001 (hex) for procedure returns and 0000 otherwise. It is the occurrence of the usage hint value 0001 that identifies a RET instruction as one that is reserved for use only as described in this section. RET instructions may be used for other purposes provided they contain a usage hint of 0000. Such RET instructions will not be recognized and treated in a special way for the purposes of exception handling or unwinding.

Furthermore, for any such procedure that does not return a value on the stack, the above instruction must be directly preceded by one of the two reserved stack resetting instructions, as in:

LDA     SP,*                ;Reset stack
RET     R31,(*),0001        ;Return to caller with usage hint

or

ADDQ    *,*,SP              ;Reset stack
RET     R31,(*),0001        ;Return to caller with usage hint

That is, any LDA instruction whose destination is the SP register or any ADDQ instruction whose destination is the SP register is interpreted as part of a procedure exit sequence when immediately followed by the reserved procedure return instruction. The LDA instruction must have the form LDA SP,n(Rx) if the amount by which the stack is being adjusted can be represented as the offset n. Otherwise, the latter form is used, in which case the ADDQ instruction must have the form ADDQ Rx, Ry, SP where, in particular, Ry is the name of a register rather than a literal operand.

A stack resetting instruction may not be present (in the case of a procedure that returns a result on the top of the stack). However, if present, it will occur immediately preceding the reserved RET instruction.

Furthermore, for any such procedure that has BASE_REG_IS_FP set to 1, the resulting sequence must be directly preceded by the FP reloading instruction, as in:

LDQ     FP,*                ;Restore FP
LDA     SP,*                ;Or ADDQ *,*,SP to Reset stack
RET     R31,(*),0001        ;Return to caller with usage hint

That is, any LDQ instruction whose destination is the FP register (R15) is interpreted as part of a procedure exit sequence when it is immediately followed by the reserved procedure return instruction or by a stack resetting instruction as described above that is immediately followed by the reserved procedure return instruction.

Procedures that do not use the stack need not use these reserved instruction sequences.

The unwind support code uses the above sequences to make the following assumptions about an interrupted PC value:

  • If the PC points within the prologue then the effect of the prologue performed so far is undone via reverse execution starting at PC - 4 (see Getting a Procedure Invocation Context with a Routine ), and the unwind can proceed.

  • If the PC points at a RET R31,(*),0001 instruction, then SP has already been reset, the registers have already been restored, and the unwind can proceed.

  • If the PC points to either a LDA SP,* or an ADDQ *,*,SP instruction that is immediately followed by the instruction described previously, then the registers have already been restored but the SP must be incremented by FRAME_SIZE before the unwind can proceed.

  • If the PC points to a LDQ FP,* instruction that is immediately followed by either of the instructions described previously and REGISTER_FRAME is 0, then all registers other than FP have been restored, FP still retains the frame base pointer that should be copied to SP, then FP must be restored, SP must be incremented by FRAME_SIZE, and the unwind can proceed.

Otherwise, the registers must be restored, SP reset, and the unwind can proceed.

When a procedure has executed the first instruction of one of the reserved sequences just described, the procedure becomes no longer current for the purposes of exception handling. The handler for a procedure will not be called in the midst of one of these reserved instruction sequences in that procedure.

Exit Code Sequence Steps

When a procedure returns, the exit code must restore the caller's context, synchronize any pending hardware exceptions, and make the calling procedure current by returning control to it.

This is done by performing the following actions:

Perform step 1, followed by steps 2 - 4 in any order, followed by steps 5 - 7 in that order.

  1. For a variable-size stack frame procedure that does not return a value on the top-of-stack (BASE_REG_IS_FP is 1), copy FP to SP.

  2. For a stack frame procedure (REGISTER_FRAME is 0), reload any saved registers. (For a fixed-size stack frame procedure (BASE_REG_IS_FP is 0), R15 is reloaded if it was saved on entry.)

  3. Reload the register that held the return address on entry with the saved return address if necessary. For a stack frame procedure (REGISTER_FRAME is 0), load the register designated by ENTRY_RA (R26 in a standard call) with the return address.

  4. Execute TRAPB if required.

  5. For a variable-size stack frame procedure (REGISTER_FRAME is 0 and BASE_REG_IS_FP is 1), reload R15 (FP) as for any other saved register.

    After any necessary calculations, this step must be completed by exactly one instruction as described above.

  6. If a function value is not being returned on the stack, then restore SP to the value it had at procedure entry by adding the value that was stored in FRAME_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.)

    After any necessary calculations, this step must be completed by exactly one instruction as described above.

  7. Execute the RET R31,(Rn),0001 instruction as described above to return control to the calling procedure.

Note that the called routine does not adjust the stack to remove any arguments passed in memory. This responsibility falls to the calling routine, which may choose to defer their removal due to optimizations or other considerations.

Exit Code Example for a Stack Frame

The following is the return code sequence for the stack frame example above:

LDQ     R26,16(SP)      ;Get return address
LDQ     R9,24(SP)       ;Restore first integer register
LDQ     R10,32(SP)      ;Restore next integer register
LDQ     R11,40(SP)      ;Restore next integer register
LDT     F2,48(SP)       ;Restore first floating-point register
LDT     F3,56(SP)       ;Restore last floating-point register
TRAPB                   ;Force any pending hardware exceptions
                        ;  to be raised
LDA     SP,SIZE(SP)     ;Restore SP
RET     R31,(R26),0001  ;Return to caller with usage hint

Exit Code Example for a Register Frame

The following is the return code sequence for the register frame example above:

LDA     SP,SIZE(SP)     ;Restore the SP
RET     R31,(R26),0001  ;Return to caller with usage hint


Chapter 4 Data Manipulation

This section deals with the passing and storage of data.


4.1 Data Passing

The fundamental unit of data that gets passed between procedures has been abstracted for purposes of discussion to a concept called an argument item. An argument item represents one unit of data being passed.


4.1.1 Argument Passing Mechanisms

This calling standard distinguishes 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 of the above mechanisms must be used by a given language compiler. Language semantics and/or interoperability considerations may require different mechanisms to be used in any given situation.

Immediate Value

An immediate value argument item contains the value of the data item. The argument item, or the value contained in it, is to be 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. That data item is to be 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. That data item is to be associated with the parameter.

This standard does not define a standard set of descriptors. Consequently, descriptors cannot be used as part of a standard call.


4.1.2 Normal Argument List Structure

The argument list in an 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 represented in 64 bits.

An argument item may be used to pass immediate arguments, arguments by reference, and arguments by descriptor. The standard permits any combination of these mechanisms in an argument list.

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 fashion that may produce a physically discontiguous argument list. Registers R16..R21 and F16..F21 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.

The following table specifies the standard locations in which argument items can be passed.

Table 4-1 Argument Item Locations

Item | Integer registers | Floating-point registers | 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 general rules that determine the location of any specific argument can be summarized as follows:

  • All argument items are passed in the integer registers or on the stack, except 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 in any row in the above table may be used by any given argument list. So, 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 passed by immediate value is treated as two arguments for the purpose of these rules, with the real part coming first. In particular, the real part of a complex value may happen to be passed as the sixth argument item in register F21, in which case the imaginary part will then be passed as the seventh argument item in memory.

The argument list, including both the in-memory portion as well as the portion that is passed in registers, may be read and written by the called procedure. The calling procedure must therefore not make any assumptions about the validity of any part of the argument list after the completion of a call.


4.1.3 Homed Memory Argument List Structure

In certain cases (for example, for C procedures that use varargs), it is useful to form a contiguous in-memory structure that includes the contents of all of the formal parameter values. In nearly all cases, a compiler can arrange to allocate and initialize this structure so that the parameter values that are passed in registers are placed adjacent to the parameters that are passed on the stack (without making a copy of the stack arguments). Storage for the parameters passed in registers is called the argument home area (see Figures 3-1 and 3-2). The resulting in-memory homed argument list structure is illustrated in Figure 4-1.

Because it is generally not possible to tell statically whether a particular argument is an integer or floating-point argument, it is necessary to store both integer and floating-point register argument contents in this structure. However, it is sometimes possible to determine statically that there are no floating arguments anywhere at all (either in registers or on the stack), in which case the first six entries can be omitted. To facilitate this special case, the address used to reference this structure is always the address of the first integer argument position.

The C language type va_list is defined as follows:

typedef longlong quad;
typedef struct {
   quad     *base;
   int      offset;
   } va_list;

To load the next integer argument, read the quadword at location (base + offset)), and add 8 to offset. To load the next floating argument, if offset is less than or equal 6*8, read the quadword at location (base + offset - 6*8)); otherwise, read the quadword at location (base + offset); in either case add 8 to offset.

Figure 4-1 In-Memory Homed Argument List Structure

octaword aligned
+----------------+
| flt arg #1:F16 | :-48  <- start of argument block
+----------------+
|            F17 | :-40
+----------------+
|            F18 | :-32
+----------------+
|            F19 | :-24
+----------------+
|            F20 | :-16
+----------------+
| flt arg #6:F21 | :-8
+----------------+
| int arg #1:R16 | :0   <- start of contiguous integer arguments
+----------------+
|            R17 | :8
+----------------+
|            R18 | :16
+----------------+
|            R19 | :24
+----------------+
|            R20 | :32
+----------------+
| int arg #6:R21 | :40
+----------------+
| arg #7: memory | :48  <- SP on entry (octaword aligned)
+----------------+
:         memory :
|                |


4.1.4 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 rules:

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

    No arguments passed by the immediate mechanism may 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 embedded null or omitted arguments.


4.1.5 Unused Bits in Passed Data

Whenever data is passed by value between two procedures either in registers, as is the case for the first six input arguments and return values, or in memory, as is the case for arguments after the first six, the bits not used by the data are usually sign extended or zero extended as appropriate.

The table below defines the rules for setting or clearing the unused bits.

Key:

  • Sign32 means sign extended to 32 bits —— The state of bits <63:32> is unpredictable.

  • Sign64 means sign extended to 64 bits.

  • Zero32 means zero extended to 32 bits —— The state of bits <63:32> is unpredictable.

  • Zero64 means zero extended to 64 bits.

  • Data32 means data is 32 bits —— The state of bits <63:32> is unpredictable.

  • 2 * Data32 means two single-precision parts of the complex value are stored in memory as independent floating-point values (each handled as Data32).

  • Data64 means data is 64 bits.

  • 2 * Data64 means two double-precision parts of the complex value are stored in memory as independent floating-point values (each handled as Data64).

  • Hard means passed in the layout defined by the Alpha System Reference Manual.

  • 2 * Hard means two double-precision parts of the complex value are stored in a pair of registers as independent floating-point values (each handled as Hard).

  • Nostd means that the state of all high-order bits not occupied by the data is unpredictable across a call or return.

Table 4-2 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
IEEE floating single S | FS | 4 | Hard | Data32
IEEE floating double T | FT | 8 | Hard | Data64
IEEE floating extended X | FX | 16 | N/A | N/A
IEEE floating single S complex | FSC | 2 * 4 | 2 * Hard | 2 * Data32
IEEE floating double T complex | FTC | 2 * 8 | 2 * Hard | 2 * Data64
IEEE floating extended X complex | FXC | 2 * 16 | N/A | N/A
Structures | N/A |  | 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

Note Sign64 applied to a longword logical duplicates bit 31 through bits <63:32>, which may cause the 64-bit integer value to appear negative. However, careful use of 32-bit arithmetic and 64-bit logical instructions (but no right shifts) will preserve the 32-bit unsigned nature of the argument.

Because of the varied rules for sign extension of data when passed as arguments it is important that both calling and called routines agree on the data type of each argument. No implicit data type conversions can be assumed between the calling procedure and the called procedure.


4.1.6 Sending Data

This section defines the Windows NT for Alpha Systems Calling Standard requirements for mechanisms to send data and order of argument evaluation.

Sending Mechanism

As previously defined, the argument-passing mechanisms allowed are immediate value, reference, and descriptor. Requirements for use of these mechanisms follow.

  • By immediate value — An argument may be passed by immediate value only if the argument is one of the following:
    • One of the noncomplex scalar data types with a size known (at compile time) to be ≤≤ 64 bits

    • A record with a known size (at compile time)

    • A set, implemented as a bit vector, with a size known (at compile time) to be ≤≤ 64 bits

    No form of string, array, or complex data type may be passed by immediate value in a standard call.

    A standard immediate argument item must fill 64 bits. This means that unused high-order bits of all data types (excluding records) must be zero-extended or sign-extended, as appropriate, depending on the data type, to fill all unused bits. (See Table 4-2, Unused Bits in Passed Data, for details.)

    Large immediate arguments — Record values are passed by immediate value as follows:

    • Allocate as many fully occupied argument item positions to the argument value as are needed to represent the argument.

    • The value of the unoccupied bits is undefined in a final, partially occupied argument item position.

    • An argument item that is passed in one of the registers is passed in the integer registers (never in a floating register).

    Nonstandard immediate arguments — Nonrecord argument values that are larger than 64 bits can be passed by immediate value using nonstandard conventions, typically using a method similar to that used for records. Thus, for example, a 26-byte string could be passed by value in four integer registers.

  • By reference: Nonparametric — Nonparametric arguments (that is, arguments for which associated information such as string size and array bounds are not required) may be passed by reference in a standard call.

  • By Reference: Parametric — Parametric arguments (that is, arguments for which associated information such as string size and array bounds must be passed to the caller) may be passed by reference followed by one or more immediate arguments for the parametric values. (The parametric values need not immediately follow the reference arguments to which they apply.)

    Note Interlanguage conventions for such calls are not defined by this standard.

  • By descriptor — Parametric arguments (that is, arguments for which associated information such as string size and array bounds must be passed to the caller) may be passed by a single descriptor.

    Note A standard set of descriptors for interlanguage use is not defined by this standard.

Extended floating values are not passed using the immediate value mechanism; rather, they are passed using the by-reference mechanism. (When by-value semantics are required, however, it may be necessary to make a copy of the actual parameter and pass a reference to that copy in order to avoid improper alias effects.)

When a record is passed by immediate value, the component types are not material to how the argument is aligned; the record will always be quadword aligned.

Order of Argument Evaluation

Since most higher-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 be written that depend on the order of evaluation of arguments.


4.1.7 Returning Data

A standard function must return its function value by one of the following mechanisms:

  • Immediate value

  • Reference

  • Descriptor

These mechanisms are the only standard means available for returning function values, and they support the important language-independent data types. Functions that return values by any mechanism other than those specified here are nonstandard, language-specific functions.

Function Value Return by Immediate Value

This section describes the two types of immediate value function return.

Nonfloating Function Value Return by Immediate Value

A function value is returned by immediate value in register R0 only if the type of function value is one of the following:

  • Nonfloating-point scalar data type with size known (at compile time) to be ≤≤ 64 bits

  • Record with size known (at compile time) to be ≤≤ 64 bits

  • Set, implemented as a bit vector, with size known (at compile time) to be ≤≤ 64 bits

No form of string or array may be returned by immediate value.

Two separate 32-bit entities cannot be returned in R0.

A function value <64 bits returned in R0 must have its unoccupied bits extended, as appropriate, to a full quadword, depending on the data type. (See Table 4-2, Unused Bits in Passed Data for more details.)

Floating Function Value Return by Immediate Value

A function value is returned by immediate value in register F0 if, and only if, it is a noncomplex single- or double-precision floating-point value (F,D,G,S,or T).

A function value is returned by immediate value in registers F0..F1 if, and only if, it is a complex single- or double-precision floating-point value (complex F,D,G,S,or T). The real part is in F0 and the imaginary part is in F1.

Function Value Return by Reference

A function value is returned by reference only if the function value satisfies both of the following criteria:

  • Its size is known to both the calling procedure and the called procedure, but the value cannot be returned by immediate value (because the function value requires more than 64 bits, the data type is a string or an array type, and so on).

  • It can be returned in a contiguous region of storage.

The actual-argument list and the formal-argument list are shifted to the right by one argument item. The new, first argument item is reserved for the address of the function value. The calling procedure must provide the required contiguous storage and pass the address of the storage as the first argument. This address must specify storage that is naturally aligned according to the data type of the function value. The called function must write the function value to the storage described by the first argument.

Function Value Return by Descriptor

A function value is returned by descriptor only if the function value satisfies all of the following criteria:

  • It cannot be returned by immediate value (because the function value requires more than 64 bits, the data type is a string or an array type, and so on).

  • Its size is not known to either the calling procedure or the called procedure.

  • It can be returned in a contiguous region of storage.

Function results may not be returned by descriptor in a standard call.

Typically, the called routine creates the return object on its stack and leaves it there on return. This is referred to as the stack return mechanism. The exit code of the called routine does not restore SP to its value before the call (otherwise the return value would be left unprotected in memory below SP). The calling routine must be prepared for SP to have a different value after the call.


4.2 Data Allocation

This section describes the way in which data is stored.


4.2.1 Alignment

On the Alpha architecture, memory references to data that is not naturally aligned may result in alignment faults, which can severely degrade the performance of all procedures that reference the unnaturally aligned data.

To avoid such performance degradation, all data values on Alpha systems should be naturally aligned. Moreover, the base address of a global variable or aggregate (but not necessarily the components of the aggregate) shared across a standard call must be quadword aligned. Table 4-3 shows the data alignment recommendations for non-global data.

Table 4-3 Data Alignment Addresses

Data type | Alignment starting position
8-bit character string | Byte boundary
16-bit integer | Address that is a multiple of 2 (word alignment)
32-bit integer | Address that is a multiple of 4 (longword alignment)
64-bit integer | Address that is a multiple of 8 (quadword alignment)
IEEE floating single S | Address that is a multiple of 4 (longword alignment)
IEEE floating double T | Address that is a multiple of 8 (quadword alignment)
IEEE floating extended X | Address that is a multiple of 16 (octaword alignment)
IEEE floating single S complex | Address that is a multiple of 4 (longword alignment)
IEEE floating double T complex | Address that is a multiple of 8 (quadword alignment)
IEEE floating extended X complex | Address that is a multiple of 16 (octaword alignment)

For aggregates such as strings, arrays, and records, the data type to be considered for purposes of alignment is not the aggregate itself, but the elements of which the aggregate is composed. The alignment requirement of an aggregate is that all elements of the aggregate should be naturally aligned. Varying 8-bit character strings must, for example, start at addresses that are a multiple of at least 2 (word alignment) because of the 16-bit count at the beginning of the string; 32-bit integer arrays start at a longword boundary, irrespective of the extent of the array.

Note that the rules for passing an argument that is a record by value always provide quadword alignment of the record value independent of the normal alignment requirement of the record. If deemed appropriate by an implementation, normal alignment can be established within the called procedure by making a copy of the record argument at a suitably aligned location.


4.2.2 Granularity

On the Alpha architecture, memory is byte-addressable and the smallest unit in which memory may be accessed (the granularity) is a byte. However, the original Alpha architecture, and thus many existing Alpha processors, provided only longword granularity. Moreover, even for longword-sized data it is often expedient for execution efficiency to access memory in quadword units. In the presence of multiple threads of execution (whether on multiple processors or just one processor), allocation of more than one data element within a single quadword can lead to more complicated access sequences (for example, using LDx_L/STx_C) and/or latent and hard-to-diagnose bugs, because of nonobvious and implicit data sharing.

Therefore, it is generally recommended that independent variables (that is, variables not combined in a larger aggregate) be allocated on quadword boundaries.


4.2.3 Record Layout Conventions

The Alpha calling standard record layout rules are designed to provide good run-time performance on all implementations of the Alpha architecture.

Only the standard record layouts may be used across standard interfaces or between languages. Languages may support other language-specific record layout conventions, but such other record layouts are not standard.

Aligned Record Layout

The aligned record layout conventions ensure that:

  • All components of a record or subrecord are naturally aligned.

  • The layout and alignment of record elements and subrecords is independent of any record or subrecord in which they may be embedded.

  • The layout and alignment of a subrecord is the same as if it were a top-level record.

  • Declaration in high-level languages of standard records for interlanguage use is reasonably straightforward and obvious, and meets the requirements for source-level compatibility between Alpha environments and other environments.

The aligned record layout is defined by the following conventions:

  • The components of a record must be laid out in memory corresponding to the lexical order of its appearance in the high-level language declaration of the record.

  • The first bit of a record or subrecord must be directly addressable; i.e., it must be byte-aligned.

  • Records and subrecords must be aligned according to the largest natural alignment requirements of the contained elements and subrecords.

  • Bitfields (packed subranges of integers) are characterized by an underlying integer type that is a byte, word, longword, or quadword in size together with an allocation size in bits. A bitfield is allocated at the next available bit boundary provided that the resulting allocation does not cross an alignment boundary of the underlying type. Otherwise, the field is allocated at the next byte boundary that is aligned as required for the underlying type. (In the latter case, the space skipped over is left permanently not allocated.) In addition, the alignment of the record as a whole is increased to that of the underlying integer type (if needed).

  • Unaligned bit strings, unaligned bit arrays, and elements of unaligned bit arrays must start at the next available bit in the record; no fill is ever supplied preceding an unaligned bit string, unaligned bit array, or unaligned bit array element.

  • All other components of a record must start at the next available naturally aligned address for the data type.

  • The length of a record must be a multiple of its alignment. (This includes the case when a record is a component of another record.)

  • Strings and arrays must be aligned according to the natural alignment requirements of the data type of which the string or array is composed.

  • The length of an array element is a multiple of its alignment, even if this leaves unused space at its end. The length of the whole array is the sum of the lengths of its elements.


Chapter 5 Event Processing

This chapter discusses specifications related to events that are outside the normal program flow.


5.1 Exception Handling

This section on exception handling discusses the considerations involved in the notification and handling of exceptional events during the course of normal program execution.


5.1.1 Exception-Handling Requirements

The exception-handling capabilities specified in this standard are for support of:

  • Reliable programmer and program control over response to exceptions and reporting of such exceptions, and over the flow of control when exceptions occur.

  • Orderly termination of layered applications.

  • Correct and predictable exception handling in a multilanguage environment.

  • The construction of modular, maintainable multilanguage applications.

  • Support for parallel multithreaded application execution, including:
    • Per-thread exception handling.

    • Handling of asynchronous exceptions.

    • Safe thread exit in a multithreaded environment.
  • Capability for subsystems and applications to override system messages to provide a more suitable application-oriented interface, specifically including modular, multinational message and error reporting.


5.1.2 Exception-Handling Overview

When an exception occurs (is raised), the normal flow of control in the current thread is interrupted, the context is saved, and control is transferred to the exception-handling support code. This support code marshals the exception information and then enters a section of the support code called the exception dispatcher. The exception dispatcher searches for exception handlers and invokes them in the proper sequence.

When a handler is invoked, it is called as a procedure with arguments that describe the nature of the exception, the environment within which the exception was raised, and the environment within which the handler was established. When the handler is called the exception is said to be delivered to the handler.

The handler may respond to the exception in several ways, including various combinations of the following:

  • Perform some action that affects the context of the thread (possibly correcting the circumstances that led to the exception being raised).

  • Modify or augment the description of the exception.

  • Raise a nested exception, causing another exception to occur in the context of the exception handler or in a procedure called directly or indirectly by the handler.

When an exception handler has finished processing an exception, it must indicate this in one of the following ways:

  • Indicate that the exception-handling support code should reraise the exception and resume the search for another handler.

  • Indicate that the exception-handling support code should continue execution of the interrupted thread at the location indicated by the saved exception program counter.

  • Unwind, which causes the exception-handling support code to resume execution of the thread at a point different than the point at which it was interrupted, or terminate the execution of the thread.

All exceptions are handled with the same interfaces, data structures, and algorithms. That is, there is unified exception handling for all kinds of exceptions, regardless of their origination.

Each exception has an exception value that identifies the exception (such as subscript range violation, or memory access control violation). Exceptions may also have associated with them one or more exception qualifiers (such as the name of an array and the subscript that was out of range, or an address associated with a memory access control violation).


5.1.3 Kinds of Exceptions

Exceptions may be divided into these kinds:

  • Those caused by general software or hardware notification mechanisms (called general exceptions).

  • Those caused by an unwind operation (called unwind exceptions).

General Exceptions

General exceptions may be further categorized as either software caused or hardware caused.

Software Caused

A software-caused general exception is raised as the result of the invocation of an exception-raising procedure and is always delivered to the thread that made the call.

Such an exception may be raised at any point during thread execution. Applications and language run-time libraries may raise general exceptions to notify a thread of some exceptional (noteworthy) state in the current thread context. For example, subscript range-checking failures and assertion-checking failures may be raised as general exceptions.

Hardware Caused

A hardware exception occurs when a thread performs some action that causes an exceptional state to exist in the hardware. Such a state will cause the currently active thread to be interrupted. A hardware-caused general exception is always delivered to the thread that executed the exception-causing instruction.

Exactly which hardware events can result in an exception, the state of the machine when a hardware exception occurs, the interpretation of the exception-related information that is delivered to a user mode thread, and circumstances under which execution can be continued are specific to individual hardware exceptions. Hardware exceptions are fully defined by the Alpha System Reference Manual, which should be consulted for additional information.

In the Windows NT for Alpha Systems environment, hardware exceptions (that are not handled by the operating system itself) are reported to user level in the form of a general exception.

Unwind Exceptions

An unwind exception results from the invocation of the unwind support code by a thread, and is always delivered to the thread that invoked the unwind.

Unwind exceptions are delivered as part of the notification process that an unwind is in progress


5.1.4 Status Values and Exception Codes

A status value can be used as a return value from a procedure call to indicate success, failure, and/or other information about the requested operation. A status value can also be used as an exception code to indicate the reason that an exception is being raised.

A status value is represented as shown in Figure 5-1.

Figure 5-1 Status Value Representation

3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+---+-+-------------------------+-------------------------------+
|Sev|C|       Facility          |               Code            |
+---+-+-------------------------+-------------------------------+

The components of this representation are as follows:

Sev

A severity code, which can hold the following bit-encoded values:

  • 00 Success

  • 01 Informational

  • 10 Warning

  • 11 Error

C

A flag that indicates that this status value is customer defined.

Facility

A facility code that indicates the software component that defines this status value.

Code

An identifier value for a particular status condition.


5.1.5 Exception Records

The fundamental data structure for describing exceptions is the exception record.

Exception records can form a linked list. Each record in a list describes one exception.

The first exception record in the list describes the primary exception. Additional secondary exceptions may be specified by additional exception records in the list. Secondary exceptions qualify or elaborate the primary exception; they may be raised at the same time as the primary exception, or a handler may add new secondary exceptions to the list before handling or reraising the exception.

Storage for exception records may be allocated in read-only memory. The exception record that is passed to a handler is a separate copy constructed from information in the original exception record augmented with additional information.

Exception records are defined as shown in Figure 5-2.

Figure 5-2 Exception Record Format

EXCEPTION_RECORD                                 longword-aligned
+---------------+---------------+---------------+---------------+
|                         ExceptionCode                         | :0
|                                                               |
+---------------+---------------+---------------+---------------+
|                         ExceptionFlags                        | :4
|                                                               |
+---------------+---------------+---------------+---------------+
|                        ExceptionRecord                        | :8
|                                                               |
+---------------+---------------+---------------+---------------+
|                        ExceptionAddress                       | :12
|                                                               |
+---------------+---------------+---------------+---------------+
|                        NumberParameters                       | :16
|                                                               |
+---------------+---------------+---------------+---------------+
|                      ExceptionInformation[0]                  | :20
|                                                               |
+---------------+---------------+---------------+---------------+
                             .......
+---------------+---------------+---------------+---------------+
|            ExceptionInformation[NumberParameters-1]           | :x
|                                                               |
+---------------+---------------+---------------+---------------+

sizeof(EXCEPTION_RECORD) == 80

ExceptionCode .

A status value5.1.4_Status_Values_and_Exception_Codes.

ExceptionFlags .

A bitfield of flags that further qualify the exception. These flag bits are significant only in the primary exception record; their state is unpredictable in secondary exception records. ExceptionFlags bits are divided into two groups. The first group (called detail flags) provide additional information about the exception. The second group (called environment flags) provide additional information about the environment in which the exception is being delivered.

Valid ExceptionFlags bits that give additional detail are:

  • EXCEPTION_NONCONTINUABLE If 1, an exception handler must not return ExceptionContinueExecution.

  • EXCEPTION_EXIT_UNWIND If 1, the exception handler is being invoked because of an unwind operation that will terminate execution of the thread.

  • EXCEPTION_UNWINDING If 1, the exception handler is being invoked because of a general unwind operation (with the semantics of longjmp()).

Valid ExceptionFlags bits that give additional information about the environment at the time of exception delivery are:

  • EXCEPTION_NESTED_CALL If 1, an exception or unwind is in progress at the time this exception is delivered.

  • EXCEPTION_STACK_INVALID If 1, the stack is invalid.

    Note This flag is for use by system software, and it will never be 1 in an exception record delivered to a normal handler.

  • EXCEPTION_TARGET_UNWIND If 1, this is the target frame of an unwind operation. (This may be useful to allow a language-specific handler to perform proper handling for the last step of an unwind.)

  • EXCEPTION_COLLIDED_UNWIND If 1, an unwind collision has occurred.

All flag bits other than those defined above must be zero.

ExceptionRecord

Either zero or the address of the next exception record in the list.

ExceptionAddress

The address of the instruction causing the exception.

For a hardware exception or asynchronous software exception, this is the address of the instruction at which the hardware exception or asynchronous exception interrupted execution of the thread.

For synchronous software exceptions and unwind exceptions, this is the address of the call instruction that invoked the library routine for raising the exception or starting the unwind, respectively.

This field is significant only in the primary exception record; its contents are unpredictable in secondary exception records.

NumberParameters

The number of exception-specific qualifiers in the exception record.

ExceptionInformation[n]

Each is a single longword that provides additional information specific to the exception, and may contain information intended for display in messages.

Exception Records for General Exceptions

Software-Caused Exceptions

The information in exception records for general and unwind exceptions may vary widely from a simple single exception value to a long chain of exceptions and exception qualifiers. This specification defines the conventions for constructing these exception records. A complete enumeration of all possible combinations is beyond the scope of this document.

Hardware-Caused Exceptions

All Alpha hardware exceptions have exception information associated with them. This information may be as little as the exception type and exception PC or as much as three additional registers worth of information. The specific information supplied with each exception type is defined in the Alpha System Reference Manual.

When a hardware exception occurs, the Windows NT for Alpha Systems operating system may handle the exception internally or it may cause the exception to be raised in user mode. When a hardware exception is raised in user mode, the exception record passed to exception handlers includes the exception information supplied by the operating system. This information is marshaled into the exception record to produce an exception record, typically as follows:

ExceptionCode

An operating system–defined code unique to the exception condition.

ExceptionFlags

As appropriate (see above).

ExceptionRecord

Value of 0 (null)

ExceptionAddress

The PC of the instruction that caused the hardware exception.

NumberParameters

Contains additional information specific to the exception condition.

ExceptionInformation[n]

Contain additional information specific to the exception condition.

Details on the treatment of particular hardware exceptions are system defined.

Exception Records for Unwind Exceptions

Unwind exceptions are characterized by having at least one of the EXCEPTION_UNWINDING or EXCEPTION_EXIT_UNWIND or EXCEPTION_TARGET_UNWIND flags set to 1.

The reason code for the unwind as well as any supplied qualifiers is represented in the ExceptionCode flag in the same way as for general exceptions.


5.1.6 Exception Handlers

A frame-based handler is established when a procedure whose descriptor specifies an exception handler becomes current. Thus, frame-based handlers are usually associated with a procedure at compile time, and are located at run time via the procedure descriptor. These exception handlers are normally used to implement a particular language's exception-handling semantics.

The frame-based handlers that may be invoked are those established by active procedures, from the most current procedure to the oldest predecessor.

Handling Exceptions

An exception handler that conforms to this standard generally should not handle any exception that its establisher did not cause unless there is a prior agreement between the writers of the exception handler and the writers of the code that raised the exception.

Access to Memory

Exceptions can be raised and unwind operations (which cause exception handlers to be called) can occur when the current value of one or more variables is in registers rather than in memory. Because of this, a handler, and any descendant procedure called directly or indirectly by a handler, must not access any variables except those explicitly passed to the procedure as arguments or those that exist in the normal scope of the procedure.

This rule can be violated for specific memory locations only by agreement between the handler and all procedures that might access those memory locations. The effects of such agreements are not specified by this standard.


5.1.7 Establishing Handlers

The list of established frame-based handlers for a thread is defined by the procedure invocation chain.

A procedure descriptor for which HANDLER_VALID is 1 must specify in HANDLER_ADDRESS the procedure value of an exception handler. The exception handler specified by a procedure descriptor is established when that descriptor is added to the invocation chain (that is, when the procedure designated by the descriptor becomes current), remains established as long as that procedure invocation is part of the invocation chain, and is revoked when that descriptor is removed from the invocation chain (that is, when the procedure invocation designated by the descriptor terminates, either by returning or being unwound).

Thus, the set of frame-based handlers that is established at any moment is defined by the current procedure call chain.

Dynamic activation and deactivation of exception handlers is not defined by this calling standard (and in fact not permitted within the semantics of many language standards). If this capability is required it must be defined on a language-by-language basis. Compilers that choose to support this functionality may set up language-specific static exception handlers that provide the dynamic exception-handling semantics of that language. These static handlers would be established by means of the procedure descriptor of the establishing procedure. If a language compiler decides to support dynamic activation of exception handlers it must be prepared to recognize code that intends to use this feature. This requirement stems from the need to add appropriate TRAPB instructions and other compile time considerations needed to make dynamic exception handling function correctly.

Note There may be additional protocols and conventions for dynamic exception handling. These may be needed, for example, to enable a debugger to do a good job within the language exception-handling environment. These conventions are driven by the requirements of the languages and the language support utilities, and are not addressed by this calling standard.


5.1.8 Raising Exceptions

Raising General Exceptions

A thread may raise a general exception in its own context by calling a system library routine defined as follows:

RtlRaiseException( ExceptionRecord )

Arguments

ExceptionRecord

The address of a primary exception record.

Function Value

None

Remarks

RtlRaiseException() sets ExceptionAddress to the address of the invoking call instruction.

If RtlRaiseException() detects that the exception record passed via the first argument is not a valid exception record, it raises the exception STATUS_INVALID_EXCEPTION.

Raising General Exceptions Using GENTRAP

The Alpha System Reference Manual defines a GENTRAP PALcall, which provides a means for software to raise hardware-like exceptions at minimum cost.

This mechanism is suitable for use in low levels of the operating system or during bootstrapping when only a limited execution environment may be available. In a constrained environment, the GENTRAP can be handled directly via the SCB Vector by which the trap is reported. In a more complete environment, the GENTRAP parameter is transformed into a corresponding exception code and reported as a normal hardware exception. Because of this, low-level software can use this mechanism to report exceptions in a way that is independent of the execution environment. Compiled code may also use this means to raise common generic exceptions more cheaply than making a full procedure call to RtlRaiseException.

The PALcall is defined as follows:

GENTRAP( EXPT_CODE )

Arguments

EXPT_CODE

Code for the exception to be raised.

Remarks

If the EXPT_CODE value is one of the small negative values shown in the following table, then that value is mapped to a corresponding Windows NT for Alpha Systems exception code as shown. Otherwise, STATUS_ALPHA_GENTRAP is raised with the unmapped value included in the exception record as the (first and only) qualifier value. Note that there is no means to associate any parameters with an exception raised using GENTRAP.

GENTRAP sets ExceptionAddress to the address of the PALcall instruction.

EXPT_ CODE | Symbol | Status code
-1 | GENTRAP_INTEGER_OVERFLOW | STATUS_INTEGER_OVERFLOW
-2 | GENTRAP_INTEGER_DIVIDE_BY_ZERO | STATUS_INTEGER_DIVIDE_BY_ZERO
-3 | GENTRAP_FLOATING_DIVIDE_BY_ZERO | STATUS_FLOAT_DIVIDE_BY_ZERO
-4 | GENTRAP_FLOATING_OVERFLOW | STATUS_FLOAT_OVERFLOW
-5 | GENTRAP_FLOATING_UNDERFLOW | STATUS_FLOAT_UNDERFLOW
-6 | GENTRAP_FLOATING_INVALID_OPERAND | STATUS_FLOAT_INVALID_OPERATION
-7 | GENTRAP_FLOATING_INEXACT_RESULT | STATUS_FLOAT_INEXACT_RESULT
-14 |  | STATUS_STACK_OVERFLOW
-18 |  | STATUS_ARRAY_BOUNDS_EXCEEDED

Raising Unwind Exceptions

The mechanism used to raise an unwind exception is described in Section 5.2, Unwinding.


5.1.9 Invocation of Exception Handlers

The search for an exception handler begins with the program counter value that indicates the address at which the exception was raised. As a general rule, a program counter value is associated with a procedure descriptor that provides information needed to identify the procedure that contains the code and to interpret those parts of the stack frame that are needed to walk the procedure call chain. (See also Procedure Invocations and Call Chains.)

If a null frame procedure or other fragment of code does not have an associated procedure descriptor, then it is assumed that an appropriate initial program counter value is located in the normal return address register (R26). If there is a procedure descriptor associated with this address, then the search for an exception handler begins using that address. Otherwise, a fatal exception is raised (and the executing thread is terminated).

Order of Invocation

When an exception is raised, established exception handlers are invoked in a specific order.

Any frame-based handlers are invoked in order from that established by the most current procedure invocation to the oldest predecessor in the invocation chain.

If no frame-based handlers have been established, or if all reraise the exception, then the system last-chance handler is invoked.

Nested Exceptions

A nested exception occurs if an exception is raised while an exception handler is active.

When a nested exception occurs, the structure of the procedure invocation chain, from the most recent procedure invocation to the oldest predecessor, is as follows:

  1. The procedure invocation within which the nested exception was raised.

  2. Zero or more procedures invoked indirectly or directly by the most recently invoked (most current) handler.

  3. The most current handler.

    This is the same invocation as item 1 (that in which the nested exception was raised) if there are zero invocations in item 2. In this case, items 1 and 3 count as just one invocation.

  4. The procedure invocation within which the active exception that immediately preceded the nested exception was raised; that is, the invocation in which the exception was raised for which the most current handler was invoked.

  5. Zero or more procedure invocations, all established handlers that were invoked for the exception that immediately preceded the nested exception, and all of which reraised.

  6. The establisher of the most current handler.

    This is the same as item 4 (the invocation in which the exception that immediately preceded the nested exception was raised) if there are zero invocations in item 5.

  7. Zero or more procedure invocations for which no established handlers have yet been invoked.

Established handlers are invoked in reverse order with respect to that in which their establishers were invoked; that is, the search of stack frames for procedure invocations that have established handlers is in order from 1 to 7.

If further nested exceptions occur, this procedure invocation chain structure is repeated for those further nested exceptions, and frame-based handlers are invoked according to the above rules, in order from those established by the most current procedure to those established by the oldest predecessor.

Steps for Locating and Invoking Handlers for Exceptions

When an exception is raised, the steps that implement the above explanation are detailed in the following. (Note that these steps cover only the search of stack frames for a handler proper and do not address the mapping of a POSIX signal to an exception.)

  1. Let current_invocation be the procedure invocation in which the exception was raised.

  2. [loop]: If current_invocation does not establish a handler, go to step [check-begin] below.

  3. Invoke the handler established by current_invocation.

  4. If the handler returns ExceptionContinueExecution or initiates an unwind, exit these steps.

  5. [check-begin]: If current_invocation is the beginning of the procedure invocation chain, go to step [last-chance] below.

  6. If current_invocation is an active handler, let current_invocation be the invocation in which the exception was raised that invoked this active handler, and go to step [loop] above.

  7. Let current_invocation be the procedure invocation that invoked current_invocation.

  8. Go to step [loop] above.

  9. [last-chance]: Invoke the system last chance handler.

Invalid Thread Stack

If, during the search for and invocation of frame-based handlers, the exception dispatcher detects that the thread's stack is corrupt, then the following steps take place:

  1. The EXCEPTION_STACK_INVALID flag is set to 1.

  2. The search for handlers immediately proceeds to the system last-chance handler.

Handler Invocation and Arguments

Every exception handler is invoked as a function that returns a status value. The function call is defined as follows:

(*ExceptionHandler)( ExceptionRecord, EstablisherFrame, ContextRecord, DispatcherContext )

Arguments

ExceptionRecord

The address of a primary exception record.

EstablisherFrame

Virtual frame pointer of the establisher.

ContextRecord

Address of an invocation context block containing the saved original context at the point where the exception occurred. During an unwind, this is the address of the invocation context block for the establisher.

DispatcherContext

Address of a control record for the exception dispatcher (see below).

Function Value

Status

A value indicating the action to be taken upon handler return. The valid values are ExceptionContinueExecution and ExceptionContinueSearch.

Note The exception dispatcher allows additional return values from its own exception handlers.

Remarks

The control record pointed to by DispatcherContext provides communication between the handler and the exception dispatcher (the system routine that actually invokes the handler). This record provides information about the establisher. Of the fields listed below, all but ControlPC are read-only to exception handlers (except for handlers for the exception dispatcher itself).

ControlPC contains the PC where control left the establisher of the exception handler; i.e., the PC of the call instruction or the instruction that caused the exception. This field may be updated by a handler. If a nested exception occurs during unwinding while the handler is still active, then the value of the PC used for the establisher will be the updated value of ControlPC. This mechanism can be employed to retire nested exception-handling scopes local to a procedure to ensure that each is executed at most once (even in the presence of a nested exception within such a handler). The ControlPC value, however, must always be an address within the procedure whose handler is executing.

FunctionEntry contains a pointer to the primary procedure descriptor for the establisher.


5.1.10 Modification of Exception Records and Context by Handlers

The exception records, exception qualifiers, invocation context blocks, and control records that are passed to an exception handler are always allocated in writable memory. Handlers may write to any location in these data structures. The exception records and exception qualifiers that are passed to a handler are copies of the original ones. Modifications to them are seen by other subsequently called handlers (within the limits defined below) but do not affect the original data structures.

The effect of a handler modifying passed exception information is as follows:

  1. If the EXCEPTION_NONCONTINUABLE flag in the primary exception record is changed from 0 to 1, then the exception handler that made the modification must not return ExceptionContinueExecution, nor may any handler subsequently invoked for the exception return ExceptionContinueExecution.

    If ExceptionContinueExecution is returned after the EXCEPTION_NONCONTINUABLE flag has been changed from 0 to 1, then a nested exception is raised with ExceptionCode = STATUS_NONCONTINUABLE_EXCEPTION, indicating that an attempt was made to continue from a noncontinuable exception. This second exception is also noncontinuable.

  2. If any flags in ExceptionFlags in the primary exception record are modified except as specified above, there is no effect after the exception handler completes; all handlers subsequently invoked for the exception receive a primary exception record with the flags unmodified.

    In particular, if an exception handler changes the EXCEPTION_NONCONTINUABLE flag from 1 to 0, that handler must not return ExceptionContinueExecution, and any and all handlers subsequently invoked for the exception will be invoked with the EXCEPTION_NONCONTINUABLE flag set to 1.

  3. If the contents of the record specified by ContextRecord or DispatcherContext are modified by a handler (except for ControlPC ), the results are unpredictable, and such a handler does not conform to this standard.

  4. Except as specified above, all changes made to the exception information will be visible to handlers subsequently invoked for the exception. Any other effects of modifying the exception information are not defined by this standard.


5.1.11 Handler Completion and Return Value

When an exception handler has finished all its processing, it completes by performing one of the following actions:

  • Reraising the exception

  • Continuing execution of the thread

  • Initiating procedure invocation unwinding

Completion by Reraise

If an exception handler determines that additional handlers should be invoked for the exception (because it could not completely handle the exception), it can reraise the exception by returning ExceptionContinueSearch.

Reraise causes the next exception handler.

If all exception handlers established by the thread reraise the exception, the system last-chance handler is invoked, with system dependent results.

Completion by Continue

By returning ExceptionContinueExecution, an exception handler can continue execution of the thread at the address specified by the continuation PC in the ContextRecord, with the context of the interrupted procedure restored.

If ExceptionContinueExecution is returned and the EXCEPTION_NONCONTINUABLE flag is 1, then a nested exception is raised with ExceptionCode = STATUS_NONCONTINUABLE_EXCEPTION. This second exception is also noncontinuable.

Continuation from Unwind

When an unwind is in progress, the status returned by handlers must be ExceptionContinueSearch; otherwise STATUS_INVALID_DISPOSITION is raised. That is, handlers may not continue during an unwind operation.

Continuation from Signal Exceptions

The legality and effects of continuation from a signal exception are governed by the underlying signal, as specified by the implementation of the POSIX environment.

Completion by Unwinding

The unwind type of completion is more complex than simply returning a value. See Section 5.2, Unwinding, for details and considerations on unwinding.


5.1.12 Other Considerations

The following details certain aspects of the Alpha architecture that have significant implications for exception handling. The rules presented are designed to ensure correct operation across all implementations of that architecture. As with all aspects of this calling standard, optimization information may ensure correct behavior as if these rules were followed without appearing to explicitly do so.

Alternative approaches that exploit implementation-specific characteristics are also possible, but are outside the scope of this standard.

Exception Synchronization

The Alpha hardware architecture allows instructions to complete in a different order than that in which they were issued, and for exceptions caused by an instruction to be raised after subsequently issued instructions have been completed. Because of this, the state of the machine when a hardware exception occurs cannot be assumed with precision unless it has been guaranteed by bounding the exception range with the appropriate insertion of TRAPB instructions.

The rules for bounding the exception range are as follows:

  • If a procedure has an exception handler that does not simply reraise all arithmetic traps caused by code not contained directly within that procedure then it must issue a TRAPB instruction before it establishes itself as the current procedure.

  • If a procedure has an exception handler that does not simply reraise all arithmetic traps caused by code not contained directly within that procedure or by any procedure that might have been called while that procedure was current, then it must issue a TRAPB instruction in the procedure epilogue while it is still the current procedure.

  • If a procedure has an exception handler that is sensitive to the invocation depth, then it must issue a TRAPB instruction immediately before and after any call. Furthermore, the handler must be able to recognize exception PC values that represent TRAPB instructions immediately after a call and adjust the depth appropriately.

These rules ensure that exceptions are detected in the context within which exception handlers have been set up to handle them.

These rules do not ensure that all exceptions are detected while the procedure within which the exception-causing instruction was issued is current. For example, if a procedure without an exception handler is called by a procedure that has an exception handler that is not sensitive to invocation depth, an exception detected while that called procedure is current may have been caused by an instruction issued while the caller was the current procedure. This means that the frame, designated by the exception-handling information, is the frame that was current when the exception was detected, not necessarily the frame that was current when the exception-causing instruction was issued.

Continuation from Exceptions

The Alpha architecture guarantees neither that instructions are completed in the same order in which they were fetched from memory nor that instruction execution is strictly sequential. Continuation after some exceptions is possible, but there are restrictions, as reflected in the following discussions.

Software-raised general exceptions are, by definition, synchronous with the instruction stream and can have a well-defined continuation point. Thus, a handler may have the option of requesting continuation from a software-raised exception. However, since compiler-generated code typically relies on error free execution of previously executed code, continuing from a software-raised exception may produce unpredictable results and unreliable behavior unless the handler has explicitly fixed the cause of the exception in such a way as to be transparent to subsequent code.

Hardware faults on Alpha systems follow rules that, loosely paraphrased, state that if the offending exception is fixed, re-execution of the instruction (as determined from the supplied PC) will yield correct results. This does not imply that no instructions following the faulting instruction have been executed (see the Alpha System Reference Manual for more details). Hardware faults can therefore be viewed as similar to software-raised exceptions and can have well-defined continuation points.

Arithmetic traps cannot be restarted since all the information required for a restart is not available. The most straightforward and reliable way in which software may guarantee the ability to continue from this type of exception is by placing appropriate TRAPB instructions in the code stream. Although this does allow continuation, this technique must be used with extreme caution due to the negative side effect on application performance. A more sophisticated technique that requires typically one TRAPB per basic block is described in the Alpha System Reference Manual, Section 4.7.5.1, Imprecise/Software Completion Trap Modes.


5.2 Unwinding

The unwinding capabilities specified in this section are for support of:

  • Correct and predictable nonlocal GOTO support in a multilanguage environment

  • Support for the construction of modular, maintainable multilanguage applications


5.2.1 Unwind Basic Considerations

Unwinding refers to the action of returning from a procedure or a chain of procedures by a mechanism other than the normal return path. Performing an unwind operation in a thread causes a transfer of control from the location at which the unwind operation is initiated to a target location in a target invocation. This transfer of control also results in the termination of all procedure invocations, including the invocation in which the unwind request was initiated, up to the target procedure invocation. Thread execution then continues at the target location.

Before control is transferred to the unwind target location, the unwind support code invokes all frame-based handlers that were established by procedure invocations that are being terminated, plus the handler for the target invocation. These handlers are invoked with an indication that an unwind is in progress. The exception record passed to the target invocation's handler also has EXCEPTION_TARGET_UNWIND set to 1. This gives each procedure invocation the chance to perform cleanup processing before its context is lost.

Once all the relevant frame-based handlers have been called and the appropriate frames have been removed from existence, the target invocation's saved context is restored and execution is resumed at the specified location.

The results of attempting an unwind operation to any invocation previous to the top-level procedure of a thread is undefined by this standard.

Unwinding does not require an exception handler to be active; languages may use it to implement nonlocal GOTO.


5.2.2 Types of Unwind

There are two types of unwind requests; general unwind and exit unwind:

General Unwind

A general unwind transfers control to a specified location in a specified procedure invocation.

The target procedure invocation is specified by a frame pointer.

The target location is specified with an absolute PC value.

When a general unwind is completed, the registers are updated from the invocation context for the target frame. Register R0 obtains its value from the ReturnValue argument to unwind, allowing a status to be returned to the target of the unwind.

Exit Unwind

It is valuable for a thread that is terminating execution to be able to clean up its use of shared resources. In a single-threaded process, these might be global resources shared among processes, such as files, locks, or shared memory. For multithreaded processes, global resources plus processwide resources like a heap might need to be restored to a known state.

Because of this, user mode thread exit may be accomplished only by unwinding. A special case form of unwind, termed exit unwind, invokes all established frame-based handlers with an exception record specifying that an exit unwind is in progress, terminates all procedure invocations up to the beginning of the call chain, and terminates execution of the thread. Threads that use any other mechanism are not considered standard and their behavior is undefined.


5.2.3 Unwind Invocation Types

There are two cases under which an unwind may be invoked. Those initiated while an exception is active and those initiated while no exception is active.

Unwind with No Active Exception

An unwind that is initiated when no exception is active is usually done to perform a nonlocal GOTO; that is, to transfer control directly to some code location that is not part of the currently executing procedure or is not statically known. Even this type of operation must provide a mechanism to allow cleanup operations of terminated invocations (including restoring a consistent set of register values) to be performed. The unwind mechanism is used to support this type of operation.

Unwind During an Active Exception

The handler, or any descendant procedure called directly or indirectly by the handler, can continue execution of the thread at a different location than that at which the exception was raised by initiating an unwind operation.

An unwind operation specifies a target invocation in the procedure invocation chain and a location in that procedure. The operation terminates all invocations up to the target invocation, and continues thread execution at the specified location in that procedure.

Before control is transferred to the target location, the unwind operation invokes each frame-based handler that was established by any procedure invocations being terminated, plus the handler for the target invocation.


5.2.4 Unwind Initiation

Initiating a General Unwind

A thread may initiate a general unwind operation by calling one of two system library routines. The routines differ only in how their first argument specifies the target frame: as a virtual frame pointer or a real frame pointer. These routines are defined as follows:

RtlUnwind( VirtualTargetFrame, TargetPC, ExceptionRecord, ReturnValue )

RtlUnwindRfp( RealTargetFrame, TargetPC, ExceptionRecord, ReturnValue )

Arguments

VirtualTargetFrame

If nonzero, the virtual frame pointer of the target procedure invocation to which the unwind should be done. If zero, an exit unwind is initiated. In addition, the EXCEPTION_EXIT_UNWIND flag is set to 1 in the exception record.

RealTargetFrame

If nonzero, the real frame pointer of the target procedure invocation to which the unwind should be done. If zero, an exit unwind is initiated. In addition, the EXCEPTION_EXIT_UNWIND flag is set to 1 in the exception record.

TargetPC

The address within the target invocation at which to continue execution. If TargetFrame is zero, then TargetPC is ignored.

ExceptionRecord

If nonzero, the address of a primary exception record. If zero, specifies that a default exception record should be supplied (see below).

ReturnValue

The value to use as the return value (contents of R0) at the completion of the unwind.

Function Value

None.

Remarks

If the ExceptionRecord argument is zero, then RtlUnwind() or RtlUnwindRfp() supplies a default exception record that specifies exactly one exception record in which ExceptionCode is STATUS_UNWIND if a nonzero TargetFrame is specified, and STATUS_EXIT_UNWIND otherwise. For either an explicit or a default exception record, the EXCEPTION_UNWINDING flag is set to 1, and the EXCEPTION_EXIT_UNWIND flag is set to 1 if a null TargetFrame is specified. ExceptionAddress is set to the address of the call to RtlUnwind() or RtlUnwindRfp().

If the ExceptionRecord argument is specified when the unwind is initiated, then all other properties of the exception record are determined by ExceptionRecord. If RtlUnwind() or RtlUnwindRfp() detects that a specified exception record is not a valid unwind record, it will raise the exception of STATUS_INVALID_EXCEPTION. If the TargetFrame cannot be found, then the last-chance handler will be called since all procedures have been terminated.

Once an unwind is initiated, control never returns from the call.


5.2.5 Multiply Active Unwind Operations

During an unwind operation, another unwind operation may be initiated. This may occur, for example, if a handler that is invoked for the original unwind initiates another unwind, or if an exception is raised in the context of such a handler and a handler invoked for that exception initiates another unwind operation.

An unwind that is initiated while a previous unwind is active is either a nested unwind or a colliding unwind.

Nested Unwind

A nested unwind is an unwind that is initiated while a previous unwind is active, and whose target invocation in the procedure invocation chain is not a predecessor of the most current active unwind handler. That is, a nested unwind is one that does not terminate any procedure invocation that would have been terminated by the previously active unwind.

When a nested unwind is initiated, no special rules apply. The nested unwind operation proceeds as a normal unwind operation, and when execution resumes at the target location of the nested unwind, the nested unwind is complete and the previous unwind is once again the most current unwind operation.

Colliding Unwind

A colliding unwind is an unwind that is initiated while a previous unwind is active, and whose target invocation in the procedure invocation chain is a predecessor of the most current active unwind handler. That is, a colliding unwind is one that terminates one or more procedure invocations that would have been terminated by the previously active unwind.

A colliding unwind is detected when the most current active unwind handler is terminated. This detection of a colliding unwind is termed a collision.

When a collision occurs, the second (more recent) takes precedence and the prior unwind is abandoned.

The next action is to reinvoke the most current active unwind handler, since its establisher has not been unwound. The EXCEPTION_COLLIDED_UNWIND flag will be set in the exception record to indicate this situation to the handler.


5.2.6 Unwind Completion

When an unwind is completed, the following conditions are true:

  • The target procedure invocation is the most current invocation in the procedure invocation chain.

  • The environment of the target invocation is restored to the state when that invocation was last current, except for the contents of scratch registers.

  • Register GP contains a pointer to the shared global storage region for a target procedure that is contained in the main image.

  • Register R0 contains the return value that was passed by the routine that invoked the unwind.

  • Execution continues at the target location.


5.2.7 Unwinding Coexistence with setjmp/longjmp

The procedure invocation unwinding facility defined by this standard can coexist and interoperate with setjmp/longjmp facilities. It is sufficient for the jmp_buf array to consist of just the frame pointer and program counter values that are needed as arguments to RtlUnwind or RtlUnwindRfp. A null pointer can be provided for the ExceptionRecord argument and the value of the longjmp expression can be provided for the ReturnValue argument.

Any environment that conforms to this standard must implement nonlocal GOTOs by using RtlUnwind or RtlUnwindRfp (or an equivalent means) to allow all procedures being terminated to clean up any local or global state as appropriate.


5.2.8 Exceptions Raised During Unwinding

During an unwind operation, an exception may be raised, causing the invocation of handlers. This may occur, for example, if a handler that is invoked for the original unwind causes an exception to be raised in this context.

Unwinding may be implemented in several fashions, including removing each stack frame after its procedure is processed, and removing all of the procedures' frames only when the target invocation is reached. For this reason, if an exception is raised during unwinding, it is undefined whether handlers for unwound procedures are invoked for this exception.


Chapter 6 Multithreaded Environment Conventions

This chapter defines the conventions to support the execution of multiple threads in the multilanguage Windows NT for Alpha Systems environment. Specifically, it defines how compiled code must perform stack limit checking. While this calling standard is compatible with a multithreaded execution environment, the detailed mechanisms, data structures, and procedures that support this capability are not specified here.

For a multithreaded environment, the following characteristics are assumed:

  • There can be one or more threads executing within a single process.

  • The state of a thread is represented in a thread environment block (TEB).

  • The TEB of a thread contains information that determines a stack limit, below which the stack pointer must not be decremented by the executing code (except for code that implements the multithread mechanism itself).

  • Exception handling is fully reentrant and multithreaded.

  • The correct way to terminate a thread is by returning from the initial procedure in which the thread began execution, or by a call to RtlUnwind specifying a null target environment or some other procedure that includes this effect. That is, correct thread termination involves unwind processing for all of the active frames of that thread.


6.1 Stack Limit Checking

A program that is otherwise correct can fail because of stack overflow. Stack overflow occurs when extension of the stack (by decrementing SP) allocates addresses not currently reserved for the current thread's stack.

Detection of a stack overflow situation is important. Without it, a thread, writing into what it considered to be stack storage, could modify data allocated in that memory for some other purpose. This would most likely produce unpredictable and undesirable results and/or unreproducible application failures.

The requirements for procedures that can execute in a multithread environment include checking for stack overflow. This section defines the conventions for stack limit checking in a multithreaded environment.

In the following sections, the term new stack region refers to the region of the stack from the old value of SP - 1 to the new value of SP.


6.1.1 Stack Guard Region

In a multithreaded environment, the memory beyond the limit of each thread's stack is protected by contiguous guard pages, which form the stack's guard region.


6.1.2 Stack Reserve Region

In some cases it is desirable to maintain a stack reserve region, which is a minimum-sized region that is immediately above a thread's guard region. A reserve region may be desirable to ensure that exceptions or asynchronous signals have stack space to execute on a thread's stack or to ensure that the exception dispatcher and any exception handler that it might call have stack space to execute after an invalid attempt to extend the stack has been detected.

This standard does not require a reserve region.


6.1.3 Methods for Stack Limit Checking

Because there may be accessible memory at addresses lower than those occupied by the guard region, compilers must generate code such that the stack is never extended past the guard pages into accessible memory not allocated to the thread's stack.

The general strategy is to access each page of memory down to and possibly including the page corresponding to the intended new value for the stack pointer SP. If the stack is to be extended by an amount that is larger than the size of a memory page, then a series of accesses is required that works from higher to lower addressed pages. If any access results in a memory access violation, then the code has made an invalid attempt to extend the stack of the current thread.

Note An access can be performed using either a load or a store operation. However, care must be taken to use an instruction that is guaranteed to make an access to memory. For example, do not use a LDQ R31,* instruction because the Alpha architecture allows it to result in no memory access at all rather than a memory read access whose result is discarded as a result of the R31 destination.

There are two methods for stack limit checking: implicit and explicit.

Implicit Stack Limit Checking

Two mutually exclusive strategies for implicit stack limit checking are of interest:

  • If the lowest addressed byte of the new stack region is guaranteed to be accessed prior to any further stack extension, then the stack can be extended by an increment that is equal in size to the guard region (without any further accesses).

  • If some byte (not necessarily the lowest) of the new stack region is guaranteed to be accessed prior to any further stack extension, then the stack can be extended by an increment that is equal in size to one-half the guard region (without any further accesses).

The stack frame layout (see Section 3.1.2, Stack Frame Procedure) and entry code rules (see Section 3.2.6) generally do not make it feasible to guarantee access to the lowest address of a new stack region without introducing an extra access solely for that purpose. Consequently, this calling standard uses the second strategy. While the amount of implicit stack extension that can be achieved is smaller, the check is achieved at no additional cost.

This calling standard requires that the minimum guard region size is 8192 bytes, the size of the smallest memory protection granularity allowed by the Alpha architecture.

These considerations lead to the following rule:

If the stack is being extended by an amount less than or equal to 4096 and no reserve region is required, then no explicit stack limit checking is required.

However, because asynchronous interrupts and calls to other procedures may also cause stack extension without explicit stack limit checking, stack extension with implicit limit checking must follow a strict set of conventions:

  • Explicit stack limit checking must be performed unless the amount by which the SP is decremented is known to be less than or equal to 4096 and no reserve region is required.

  • Some byte in the new stack region must be accessed before SP can be decremented for a subsequent stack extension.

  • This access can be performed either before or after the SP is decremented for this stack extension, but it must be done before it can be decremented again.

  • No standard procedure call can be made before some byte in the new stack region is accessed.

  • The system exception dispatcher ensures that the lowest addressed byte in the new stack region is accessed if any kind of asynchronous interrupt occurs after SP is decremented but before the access in the new stack region occurs.

These conventions ensure that the stack pointer will not be decremented such that it points to accessible storage beyond the stack limit without this error being detected (by either the guard region being accessed by the thread or an explicit stack limit check failure).

As a matter of practice, the system can provide multiple guard pages in the guard region. When a stack overflow is detected as a result of access to the guard region, one or more guard pages can be unprotected for use by the exception-handling facility, and one or more guard pages can remain protected to provide implicit stack limit checking during exception processing. However, the size of the guard region and the number of guard pages is system defined, and is not defined by this standard.

Explicit Stack Limit Checking

If the stack is being extended by an amount of unknown size or known size greater than the maximum implicit check size (4096), then a code sequence that follows the rules for implicit stack limit checking can be executed in a loop to access the new stack region incrementally in segments smaller than or equal to the minimum page size (8192 bytes). At least one access must occur in each such segment. The first access must occur between SP and SP - 4096 because, in the absence of more specific information, the previous guaranteed access relative to the current stack pointer may be as much as 4096 bytes greater than the current stack pointer address. The last access must be within 4096 bytes of the intended new value of the stack pointer. These accesses must occur in order, starting with the highest addressed segment and working toward the lowest addressed segment.

Note A simple algorithm that is consistent with this requirement (but makes up to twice the minimum number of accesses) is to perform a sequence of accesses in a loop starting with the previous value of SP, decrementing by the minimum no check extension size (4096), to but not including the first value that is less than the new value for the stack pointer.

The stack must not be extended incrementally in procedure prologues. A procedure prologue that needs to extend the stack by an amount of unknown size or known size greater than the minimum implicit check size must test new stack segments as just described in a loop that does not modify SP, and then update the stack with one instruction that copies the new stack pointer value into SP.

Note An explicit stack limit check may be performed either by inline code that is part of a prologue or by a run-time support routine that is specially tailored to be called from a procedure prologue.

Stack Reserve Region Checking

The size of the reserve region, if any, must be included in the increment size used for stack limit checks, after which it is not included in the amount by which the stack is actually extended. (Depending on the size of the reserve region, this may partially or even completely eliminate the ability to use implicit stack limit checking.)


6.1.4 Stack Overflow Handling

If a stack overflow is detected, the result will be one of the following:

  • Exception STATUS_STACK_OVERFLOW may be raised.

  • The system may transparently extend the thread's stack, reset the TEB stack limit value appropriately, and continue execution of the thread.

Note that, if a transparent stack extension is performed, a stack overflow that occurs in a called procedure might cause the stack to be extended. Because of this, the TEB stack limit value must be considered volatile and potentially modified by external procedure calls and by handling of exceptions.


Chapter 7 Procedure Invocations and Call Chains

Mechanisms for each of the following three functions are needed to support procedure call tracing:

  • Mechanism to refer to a given procedure invocation

  • Mechanism to provide the context of a procedure invocation

  • Mechanism to traverse (walk) the procedure call chain

This chapter describes the data structures and procedures that support these functions.


7.1 Referring to a Procedure Invocation

When referring to a specific procedure invocation at run-time, either the virtual frame pointer or the real frame pointer for that invocation can be used. The virtual frame pointer of a procedure invocation is the contents of the stack pointer at entry to the procedure. The real frame pointer of a procedure is the contents of the stack pointer after the size of the fixed part of the stack frame has been subtracted from the virtual frame pointer. Note that the virtual frame pointer of an invocation is not the value used for addressing by the procedure itself. The contents of the SP register are modified in the procedure prologue and the resulting real frame pointer value then possibly copied into a FP register (in the case of a variable-size stack frame). The resulting real frame pointer is then used for addressing local storage throughout the remainder of the procedure.

The real frame pointer is not, of itself, sufficient to unambiguously identify all possible procedure invocations. For example, a null frame procedure has the same real frame pointer as its caller because the null frame procedure allocates no stack storage. This ambiguity is of no consequence for the purposes of this calling standard because the real frame pointer value is always used in combination with a program counter value that identifies an instruction within a particular procedure.

The static link used in calling nested procedures in languages such as Pascal and Ada is usually either the virtual frame pointer or the real frame pointer value. The actual choice is implementation-dependent and can vary from language to language and release to release.


7.2 Invocation Context Block

The full context of a specific procedure invocation is provided through the use of a data structure called an invocation context block. An invocation context block is identical in structure to the system-defined CONTEXT structure.


7.3 Getting a Procedure Invocation Context with a Routine

A thread can obtain its own context by calling a system library function defined as follows:

RtlCaptureContext( ContextRecord )

Arguments

ContextRecord

Address of an invocation context block into which the procedure context of the caller is written.

Function Value

None.

Remarks

The context corresponds to that of the called RtlCaptureContext() procedure. However, that context is the same as that of the caller except for the contents of R16, R26, and R28. The contents of R26 in the caller prior to the call can be obtained using RtlVirtualUnwind() (see below).

A thread can obtain the invocation context of the procedure preceding another procedure context by calling a system library function defined as:

RtlVirtualUnwind( ControlPc, FunctionEntry, ContextRecord, InFunction, EstablisherFrame, ContextPointers )

Arguments

ControlPc

Address where control left the function.

FunctionEntry

Address of the function table entry (which may be either a primary or secondary procedure descriptor, see Section 8.1) for the function. Note that the appropriate function table entry can be obtained using the function RtlLookupFunctionEntry() (see Section 8.2).

ContextRecord

Address of an invocation context block. The given context block is updated to represent the context of the previous (calling) frame.

InFunction

Address of an unsigned byte (type Boolean) where the value 0 or 1 is written. Zero indicates that the ControlPc value is in the prologue, the epilogue, or in a null frame-like region (see Section 8.1, Procedure Descriptor Representation) of the function. A value of 1 indicates that the ControlPc is in the body of the function; that is, that the function is current for exception-handling purposes (see Section 3.2.6, Entry and Exit Code Sequences).

EstablisherFrame

Pointer to a structure where both the real frame pointer value and the virtual frame pointer value of the context is written. The real frame pointer value is defined if and only if InFunction is 1.

ContextPointers

Address of a context pointer’s record.

Function Value

PreviousControlPc

Address where control left the previous frame.

Remarks

This procedure takes an invocation context block together with its associated procedure descriptor and updates the context to reflect the state of the caller at the point where it made the call.

For exception and interrupt frames, the ControlPc is obtained from the trap frame continuation address as follows. For faults, ControlPc is both the last instruction that executed in the previous frame and the next instruction to execute if control were to resume in that frame. For synchronous or asynchronous traps, the ControlPc is the continuation address. For normal call frames, the ControlPc "where control left the function" is the address of the call instruction and not the return address for that call.

If the given context corresponds to a leaf procedure, then the ControlPc can be obtained from the saved PC value found in the context record. Otherwise, the ControlPc should be the result of a previous call of this procedure for the previous context.

If a context pointer’s record is specified (non-null), then the address where each register is restored from is recorded in the appropriate field of the record. This record can then be used to modify the state of some currently active context when control returns to that context. See the Windows NT for Alpha Systems documentation for full details.

The operation of RtlVirtualUnwind(), which is also a building block for exception dispatching and unwinding, is based both on the procedure descriptor and the code of the procedure associated with the ControlPc.

If the ControlPc points to a reserved RET instruction (see Section 3.2.6), then the context of the caller is the same as the given context and the ControlPc of the caller is taken as the contents of the register holding the return address (which need not be R26) minus 4.

If the ControlPc points to a stack adjustment instruction that is part of a reserved return sequence, then the context of the caller is formed by adjusting the value of the SP and the ControlPc of the caller is taken from the contents of the register holding the return address minus 4.

Otherwise, the context of the caller is recreated by restoring saved registers and by incrementing the stack pointer if the function decremented it. This is done by reverse execution of instructions in the prologue. Instructions are processed in the reverse of the normal order and their normal effect is reversed: informally speaking, a store becomes a load, a subtract becomes an add, a move-to becomes a move-from, and so on.

If the ControlPc is within the prologue, then reverse execution begins at the last completely executed instruction: if the instruction at ControlPc caused a fault, then reverse execution begins at ControlPc minus 4; otherwise, at ControlPc. If the ControlPc is not within the prologue, then reverse execution begins with the last instruction of the prologue.

The instructions in the prologue are processed backwards sequentially. When reverse execution begins, and before the value of SP is used, it is assumed that either SP has a valid value or that FP has a valid value and a "MOV SP, FP" instruction will be encountered before SP is used.

Basically, the operations performed are:

  • If a register was saved on the stack, its value is restored from that location on the stack.

  • If a register was copied to another register, its value is restored from that same register.

  • If the stack pointer was decremented by a value, it is incremented by that same value.

  • Any other instructions contained in the prologue are allowed, but ignored — they are assumed to have no effect on the context.

  • The ControlPc of the caller is determined as follows: If the function prologue contains more than one instruction that reads the return address register (R26), then the frame is considered to be an exception frame and the ControlPc of the caller is obtained from the saved PC value in the exception frame part of the stack (see the Alpha System Reference Manual for details). Otherwise, the ControlPc of the caller is the value of register R26 at the conclusion of the reverse execution minus 4.

See Section 3.2.6, Entry and Exit Code Sequences for a detailed description of the instructions that take part in reverse execution.


7.4 Walking the Call Chain

During the course of program execution, it is sometimes necessary to navigate the call chain. Frame-based exception handling is one case where this is done. Call chain navigation is only possible in the reverse direction (latest to earliest or top to bottom procedure).

The steps to perform for call chain navigation are:

  1. Build an invocation context block when given a program state (which contains a register set).

    For the current routine, an initial invocation context block can be obtained by calling RtlCaptureContext().

  2. Repeatedly call RtlVirtualUnwind() and RtlLookupFunctionEntry() until the end of the chain (ControlPc== 0) has been reached.

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.

No assumptions should be made 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.


7.5 Updating an Invocation Context

A given procedure's invocation context fields can be updated with new register contents using the ContextPointers optional parameter of RtlVirtualUnwind() (see Section 7.3).


Chapter 8 Procedure Descriptors

Procedure descriptors serve these functions:

  • They provide the means to map from an arbitrary program counter value to the descriptive information associated with the code at that address.

  • They provide information about a procedure (what registers are saved and where, how long the prologue is, and so on) that is needed for call chain walking in general and exception handling in particular.

  • They provide the means for link-time, post-link-time, and execution-time tools to identify all of the instructions that together make up a complete procedure, which provides information about the availability of general register R28 for use by those tools (see the description of R28 in Table 2-1).

  • They provide the means for link-time and post-link-time tools to distinguish code from data.

Every procedure must have an associated procedure descriptor, with the exception of certain null frame procedures as described below. Most commonly, there will be one procedure descriptor for each procedure; however, there may be more than one procedure descriptor used to describe a procedure. In addition, it is valid for a contiguous group of null frame procedures to be described by a single procedure descriptor.

A static procedure descriptor is a procedure descriptor that is created during compilation and included with the code it describes as part of the same executable image. A static procedure descriptor becomes active when its containing image is loaded. A dynamic procedure descriptor is a procedure descriptor that is created during program execution. A dynamic procedure descriptor becomes active (and inactive) as described in Section 8.3, Run-Time Generated Code.

Each instruction of a program must be described by at most one active procedure descriptor. (Equivalently, all of the active procedure descriptors of a program must describe mutually disjoint sets of instructions.)

When there is more than one procedure descriptor for a procedure, there is a particular procedure descriptor of the set, called the primary procedure descriptor, that can be efficiently determined from any of the others, called secondary procedure descriptors. (The means for doing this is based on use of the PrologEndAddress field of the procedure descriptor.) The primary procedure descriptor need not correspond to any particular portion of the procedure's code (for example, it need not describe the portion of the code at the lowest addresses).

Two instructions are part of the same procedure if and only if they have the same primary procedure descriptor or neither is described by a procedure descriptor.

Procedure descriptors are required for null frame procedures except for the following:

  • Transfer vectors created by the linker

  • Null frame procedures created at run time


8.1 Procedure Descriptor Representation

In describing the representation and use of procedure descriptors, it is necessary to consider the requirements of procedures that have more than one entry. For that purpose, we define the following terms: The primary entry of a non-null frame procedure is the entry whose prologue is described by the primary procedure descriptor. A secondary entry is any entry other than the primary entry. Note that due to the effects of optimization, the primary entry of a procedure does not necessarily correspond to the lexically first entry to be declared in the program source or to the entry with the lowest address in the code.

Procedure descriptors (also known as function entries) on Windows NT for Alpha Systems consist of a structure as shown in Figure 8-1. The fields of this structure are defined in as follows.

Figure 8-1 Procedure Descriptor

RUNTIME_FUNCTION                                  longword-aligned
31                                                         210
+---------------+--------------+---------------+---------------+
|                         BeginAddress                     |RD*| :0
|                                                          |   |
+---------------+--------------+---------------+---------------+
|                          EndAddress                      |RD*| :4
|                                                          |   |
+---------------+--------------+---------------+---------------+
|                       ExceptionHandler                   |EM*| :8
|                                                          |RD*|
+---------------+--------------+---------------+---------------+
|                          HandlerData                     |DT*| :12
|                                                          |   |
+---------------+--------------+---------------+-----------+---+
|                       PrologEndAddress                   |EM*| :16
|                                                          |   |
+---------------+--------------+---------------+-----------+---+
sizeof(RUNTIME_FUNCTION) == 20

* EM is an abbreviation for ExceptionMode
  RD is an abbreviation for "Reserved to Digital"
  DT is an abbreviation for DescriptorType

BeginAddress

The address of the first instruction of the range of instructions that is described by the procedure descriptor. In common simple cases, this is the address of the first instruction and entry point of a procedure.

Note that BeginAddress is a longword address of an instruction, in which the two low-order bits are reserved for future use (see below).

EndAddress

The address of the first instruction following the range of instructions that is described by the procedure descriptor. In common simple cases, this is the address of the instruction immediately following the last instruction of the procedure.

Note EndAddress is a longword address of an instruction, in which the two low-order bits are reserved for future use (see below).

ExceptionHandler

The address of the exception handler for the range of instructions described by this procedure descriptor. If there is no associated handler, then this field contains null (0).

Note ExceptionHandler is a longword address. Bit 0 of the longword that contains this field is part of the ExceptionMode field; bit 1 is reserved.

This field (exclusive of the reserved bit) must be null in a secondary procedure descriptor.

HandlerData

The longword address or the immediate value of associated data for use by the handler for this procedure. If ExceptionHandler is null, then the low-order two bits of the longword that contains this field are used for the DescriptorType field and the remaining bits must be zero. This field (exclusive of the DescriptorType bits) must be zero in a secondary procedure descriptor.

PrologEndAddress

Serves to describe a portion of the code between BeginAddress and EndAddress that is prologue code and/or to relate one procedure descriptor to another when both correspond to the same procedure.

If BeginAddress ≤≤ PrologEndAddress < EndAddress, then PrologEndAddress is the address of the first instruction following a (the) prologue of the procedure. (In a procedure that has no prologue, PrologEndAddress = BeginAddress.)

Such a procedure descriptor is always the primary procedure descriptor for the procedure.

Otherwise, PrologEndAddress contains the address of the primary procedure descriptor associated with this descriptor. In this case, note that the ExceptionHandler, HandlerData (exclusive of the DescriptorType bits), and ExceptionMode fields must be zero. The specific characteristics of this procedure descriptor are further determined by the contents of the DescriptorType field as follows:

  • If the DescriptorType is equal to zero (0), then the descriptor corresponds to code that is within the execution context of the procedure described by the primary descriptor (where the procedure is current). In particular, the prologue described by the primary descriptor must be used for purposes of unwinding.

    This case is typically used for code that follows the prologue associated with a secondary entry (as described below) or for code that is not contiguous with the code that contains the (an) entry point of a procedure.

  • If DescriptorType is equal to one (1), then the procedure descriptor corresponds to prologue code of the parent procedure that must be reverse executed for purposes of unwinding. EndAddress is the address of the first instruction following the prologue code for this entry. (That is, this descriptor must describe the complete prologue for the entry.)

    This case is typically used to describe the prologue code associated with a secondary entry of a procedure that has multiple entry points (as can occur, for example, in Fortran or assembler). Note that if a secondary entry for a procedure has no prologue code, then a separate procedure descriptor for the prologue is omitted (rather than using a procedure descriptor in which BeginAddress is the same as EndAddress).

  • If the DescriptorType is equal to two (2), then the procedure descriptor corresponds to code that is not within the execution context of the associated procedure. Such code is said to be in a null frame-like region of the procedure.

    This case is typically used when, for optimization, it is desirable to have an initial entry sequence that is not subject to unwinding prior to the beginning of the prologue (such as for so-called shrink-wrapping) or to have a final epilogue sequence that is longer than and/or different from that described in subsection "Reserved Instruction Sequence for Procedure Exit" of Section 3.2.6. In both cases, the procedure descriptor describes code that can be considered to execute in a null procedure context (see Section 3.1.4, Null Frame Procedure) but that is also part of a larger procedure.

    Note All other combinations are reserved for future use.

It follows from these rules that, given a procedure descriptor, it is always possible to find the unique primary procedure descriptor for the procedure in, at most, one step without any further lookups or searches.

If there is more than one combination of procedure descriptors that can correctly describe the code of a procedure, then any of them may be used; however, one that uses the smallest number of procedure descriptors is strongly recommended. Recall that every such combination must include exactly one primary procedure descriptor.

Note PrologEndAddress is a longword address. The low two bits of the longword that contains this field are used for the ExceptionMode field.

ExceptionMode

Encodes the caller's desired exception reporting behavior when calling certain mathematically oriented library routines. The 3-bit integer value is formed from bit 0 of the ExceptionHandler field (the MSB) and bits 0 (the LSB) and 1 of the PrologEndAddress field.

The possible values for this field are as follows:

Name | Meaning
EXCEPTION_MODE_SILENT | Raise no exceptions; create only finite values (no infinities, denormals nor NaNs). In this mode, either the function result or the C language errno variable must be examined for any error indication. This is the default mode.
EXCEPTION_MODE_SIGNAL | Raise exceptions for all error conditions except for underflow, which results in a zero result.
EXCEPTION_MODE_SIGNAL_ALL | Raise exceptions for all error conditions (including underflow).
EXCEPTION_MODE_FULL_IEEE | Raise no exceptions except as controlled by separate IEEE exception enable bits; create infinite, denormal and NaN values according to the IEEE floating-point standard.
EXCEPTION_MODE_CALLER | Perform the exception mode behavior specified by this procedure's caller.

This field must be zero in a secondary procedure descriptor.

For a more complete description of these exception modes and how they are used, see the Windows NT for Alpha Systems system documentation.

DescriptorType

Encodes the specific kind of descriptor present for secondary procedure descriptors. See PrologEndAddress above for details.

The set of procedure descriptors of an executable image are all collected together in a single contiguous array and sorted according to the values of the BeginAddress field.

Use of Fields with Low-Bit Conventions

Several of the fields of a procedure descriptor are defined as longword addresses (of type void *); however, the low-order two bits of each of those fields is reserved for other purposes. It is necessary to mask off these two bits prior to use for its normal purpose.

For example, a C macro to obtain the BeginAddress value given a pointer to a procedure descriptor might be written as follows:

#define BEGIN_ADDRESS(PD) (((PD)->BeginAddress) & (~3))

Properties of Procedures

For purposes of call chain tracing (see Chapter 7, Procedure Invocations and Call Chains) and unwinding (see Section 5.2, Unwinding), the properties of a procedure are determined in part by the values in the procedure descriptor as defined above and in part by reverse execution of the procedure prologue.

These properties are determined as follows.

REGISTER _FRAME

Value of 1 if there is no instruction in the prologue that stores into the stack.

BASE _REG _IS _FP

Value of 1 if the last instruction in the prologue is an instruction that copies the contents of the stack pointer (SP) to the frame pointer (FP).

HANDLER _VALID

Value of 1 if the ExceptionHandler field of the procedure descriptor is non-null.

EXCEPTION _MODE

Specified in the ExceptionMode field of the procedure descriptor.

SP _SET

The unsigned offset in instructions (longwords) from the entry address of the procedure given in the BeginAddress field of the descriptor to the one and only one instruction in the procedure prologue that modifies the stack pointer. (This offset is assumed to be zero when there is no such instruction because the procedure has a FRAME_SIZE of 0.)

ENTRY _LENGTH

The unsigned offset in instructions (longwords) formed by taking the PrologEndAddress less the BeginAddress and dividing by 4.

FRAME _SIZE

The unsigned size in quadwords of the fixed portion of the stack frame for this procedure. This value is found in the displacement field (divided by 8) of the instruction that modifies the stack pointer, or is zero if there is no such instruction.

Note If a procedure requires a frame size that is too large to be represented using the displacement field of an instruction, then the size must be loaded from memory or otherwise materialized into a register and then subtracted from the stack pointer using a SUBQ*,*,SP instruction.

BEGIN _ADDRESS

Specified in the BeginAddress field of the procedure descriptor.

END _ADDRESS

Specified in the EndAddress field of the procedure descriptor.

PROLOG _END _ADDRESS

Specified in the PrologEndAddress field of the procedure descriptor.

HANDLER _ADDRESS

Specified in the ExceptionHandler field of the procedure descriptor.

HANDLER _DATA

Specified in the HandlerData field of the procedure descriptor.

ENTRY _RA

Always register R26.


8.2 Procedure Descriptor Access Routines

A thread can obtain information from the descriptor of any procedure in the thread's virtual address space by calling system library functions.


8.2.1 Current Procedure

In the course of running and debugging a program there are times when there must be a way to identify which procedure is currently executing. During normal thread execution, the current procedure must be determined any time an exception arises so that the proper handlers will be invoked. In addition, a debugger must know which procedure invocation is currently executing to enable it to find information about the current state of the execution environment.

In order to determine completely the current execution context, not only must the currently executing procedure be determined but also which instance of that procedure. This context of the current procedure together with a specific instance of that procedure invocation is called the current procedure invocation (which is often shortened to current procedure). At any point in the execution of a thread there is always exactly one procedure that is considered the current procedure.

In the Windows NT for Alpha Systems Calling Standard the value in the PC is used to indicate the current procedure by means of a lookup table (see Section 8.1, Procedure Descriptor Representation).

The following system-supplied functions may be used to obtain the address of a procedure descriptor that corresponds with any given PC within the current address space. Note that RtlLookupFunctionEntry() always returns a primary procedure descriptor while RtlLookupDirectFunctionEntry() returns the primary or secondary procedure descriptor that directly contains the given address. (A procedure descriptor directly contains an address if and only if the address is greater than or equal to the contents of the BeginAddress field and less than the contents of the EndAddress field.)

Also note that any two addresses, say PC1 and PC2, in the same address space are part of the same procedure (as defined in the introduction to Chapter 8) if and only if RtlLookupFunctionEntry(PC1) == RtlLookupFunctionEntry(PC2)

RtlLookupFunctionEntry( ControlPc )

Arguments

ControlPc

A PC value in the current address space for which the primary procedure descriptor is to be returned.

Function Value

ProcDesc

Address of the primary procedure descriptor for the procedure that corresponds to the requested PC. If the return value is null, then the PC is not currently mapped by any active procedure descriptor.

RtlLookupDirectFunctionEntry( ControlPc )

Arguments

ControlPc

A PC value in the current address space for which the direct primary or secondary procedure descriptor is to be returned.

Function Value

ProcDesc

Address of the primary or secondary procedure descriptor that directly contains the requested PC. If the return value is null, then the PC is not currently mapped by any active procedure descriptor.


8.3 Run-Time Generated Code

Code generated at run time is important for applications that include:

  • Interactive languages

  • Software bit block transfers (for efficient support of graphic devices that do not provide hardware bit block transfers)

  • String pattern matching

  • Sorting

  • Interpretive execution and instruction stream modification by programming and debugging tools

To maintain stack traceability and to support exception handling for code that is generated at run time, procedure descriptors must be provided for that code. Such procedure descriptors must describe correctly the characteristics of the code and the environment within which that code executes.

Before run-time generated code can be executed, the following system library function must be called to activate its corresponding procedure descriptors:

RtlAddFunctionTable( FunctionTable, EntryCount )

Arguments

FunctionTable

Specifies the address of an array of procedure descriptors, where each element is of type RUNTIME_FUNCTION.

EntryCount

Specifies the number of procedure descriptors in the array.

Function value

Success

TRUE (=1) indicates the function completed successfully; FALSE (=0) indicates the function did not finish successfully.

Remarks

A function table must not be modified in any way while the table is active; if any changes are required, the table must be deactivated (using RtlDeleteFunctionTable(), see below), modified, and then reactivated using RtlAddFunctionTable().

Note that active dynamic procedure descriptors are included in the searches performed by RtlLookupFunctionEntry() or RtlLookupDirectFunctionEntry().

When procedure information is no longer valid or the code will not be executed again, the following system library function should be called to deactivate the procedure descriptor information:

RtlDeleteFunctionTable( FunctionTable )

Arguments

FunctionTable

Specifies the address of an array of procedure descriptors that was passed in a previous call to RtlAddFunctionTable().

Function Value

Success

TRUE (=1) indicates the function completed successfully; FALSE (=0) indicates the function did not complete successfully.

Steps for Run-Time Generation of Code

The following steps show how run-time code should be constructed and destroyed:

  1. Allocate memory for the code and procedure descriptor(s).

  2. Write the code and procedure descriptor(s) to memory.

  3. Call RtlAddFunctionTable() to activate the function table.

    Note The execution environment may analyze a function table when it is made active and cache certain summary information to facilitate later searching. For example, a table that is sorted in increasing order by its BeginAddress field might be later searched using a binary rather than a linear search.

  4. Invoke an IMB (instruction memory barrier) operation as required by the Alpha architecture.

  5. Execute the code.

  6. Call RtlDeleteFunctionTable() to deactivate the function table.

  7. Deallocate the memory containing the code and procedure descriptor(s).