This chapter contains descriptions of those aspects of the calling standard that deal with the flow of control in a program. This chapter does not discuss data manipulation. That topic is discussed in Chapter 4.
The following sections describe:
Procedure types
Transfer of control
This Tru64 UNIX calling standard defines three basic procedures types. A compiler may choose which type to generate based on the requirements of the procedure in question. The standard procedure types are:
A procedure that maintains its caller's context on the stack
A procedure that maintains its caller's context in registers
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.
Simple procedures
do not necessarily maintain any call frame, but instead execute completely
in the context of their caller.
The calling procedure does not need to distinguish
among these cases.
3.1.1 Procedure Descriptor Overview
Every procedure, other than a null procedure described in Section 3.1.4, must have a set of information associated with it that describes which type of procedure it is and what characteristics it has. This set of information, called a procedure descriptor, can be thought of as a single structure, although physically it is implemented by several structures. (See Chapter 8.) The procedure descriptor structure is used to interpret the call chain at any point during a thread's execution. The structure is normally built at compile time and is not generally accessed at run time except in support of exception processing or other rarely used code execution.
Table 3-1
briefly summarizes the properties
of a procedure that can be determined from its associated procedure descriptor.
(Some fields apply to only certain kinds of procedures.) For a complete description
of procedure descriptors, see
Section 8.1.
Table 3-1: Procedure Properties Summary
Procedure Property | Description |
PDSC_FLAGS_REGISTER_FRAME |
Indicates a register (or null) frame procedure rather than a stack frame procedure. |
PDSC_FLAGS_BASE_REG_IS_FP |
Indicates that register $15 is used as a frame pointer rather than as a preserved register. |
PDSC_FLAGS_HANDLER_VALID |
Indicates that there is an associated exception handler. |
PDSC_FLAGS_EXCEPTION_MODE |
Indicates the error-reporting behavior expected from certain called mathematical library routines. |
PDSC_FLAGS_EXCEPTION_FRAME |
Indicates that an operating system exception frame is included in the procedure's frame. |
PDSC_FLAGS_ARITHMETIC_SPECULATION |
Indicates that arithmetic traps (SIGFPE ) occurring in this procedure should not be delivered.
This procedure
was compiled with speculative execution optimization applied to arithmetic
operations.
Some arithmetic instructions have been moved backward, preceding
conditional branches that used to control their effects.
Traps caused by these
moved instructions should be ignored. |
PDSC_CRD_MEMORY_SPECULATION |
Indicates that memory traps (SIGSEGV ,
SIGBUS ) occurring in this procedure should not be delivered.
This procedure was compiled with speculative execution optimization applied
to memory reference operations.
Some memory reference instructions have been
moved backward, preceding conditional branches that used to control their
effects.
Traps caused by these moved instructions should be ignored. |
PDSC_RPD_RSA_OFFSET |
Specifies an offset from the stack pointer (SP) or frame pointer (FP) register to the register save area. |
PDSC_RPD_IMASK |
Indicates a bit mask for the general registers that are saved in the stack. |
PDSC_RPD_FMASK |
Indicates a bit mask for the floating-point registers that are saved in the stack. |
PDSC_RPD_ENTRY_RA |
Indicates the register that contains the return address at the time of a call. |
PDSC_RPD_SAVE_RA |
Indicates the register in which the return address is saved when it is not saved on the stack. |
PDSC_RPD_FRAME_SIZE |
Specifies the size of the fixed part of the stack frame. |
PDSC_RPD_SP_SET |
Specifies the offset from the beginning of the procedure to the instruction that changes the stack pointer. |
PDSC_RPD_ENTRY_LENGTH |
Specifies the length of the procedure prologue. |
PDSC_CRD_BEGIN_ADDRESS |
Specifies the address of the first instruction and entry point of the procedure. |
PDSC_RPD_HANDLER_ADDRESS |
Specifies the address of an associated exception handling procedure. |
PDSC_RPD_HANDLER_DATA |
Specifies supplementary per-procedure data to be passed to an associated exception handler. |
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" because of the cost of storing this context in memory. These procedures 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; for example, when a called routine uses the stack as a means to return certain types of function values. (See Section 4.1.7 for details.)
If such a situation exists, a compiler must choose to use a variable-size
stack frame procedure when compiling the caller so that any unwind operations
can be performed correctly.
3.1.2.1 Stack Frame Format
Even though the exact contents of a stack frame are determined by the compiler, there are certain properties common to all stack frames. There are two basic types of stack frames: fixed-size and variable-size. Some parts of the stack frame are optional and occur only as required by the particular procedure. In the figures, brackets ([ ]) surrounding a field's name indicate that the field is optional.
Figure 3-1
shows the format of the stack frame
for a procedure with a fixed amount of stack.
This format uses SP as the
stack base register
(that is,
PDCS_FLAGS_BASE_REG_IS_FP
is 0).
In this case, $15 is simply another saved register and has no special
significance.
Figure 3-1: Fixed Size Stack Frame Format
Figure 3-2
shows the format of the stack
frame for a procedure with a varying amount of stack.
The format uses FP
as the stack base register; that is,
PDSC_FLAGS_BASE_REG_IS_FP
is 1.
Figure 3-2: Variable Size Stack Frame Format
In both cases, the portion of the stack frame designated by
PDSC_RPD_FRAME_SIZE
must be allocated and initialized by the entry code sequence of a called
procedure with a stack frame.
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. These locations might include, for example, register spill areas, language-specific exception handling contexts (such as language dynamic exception handling information), or fixed temporary locations.
If a compiler chooses, the fixed temporary locations adjacent to the
area pointed to by the frame base register added to the value of
PDSC_RPD_FRAME_SIZE
can be used for a special purpose referred to as 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 can also be used to store arguments that are passed in registers if an address for such an argument must be generated. Generally, 6 or 12 contiguous quadwords of stack storage are allocated by the called procedure for this kind of storage. (See Section 4.1.3 for details.)
The
register save area
is a set of consecutive quadwords where the
current procedure saves and restores registers.
The register save area begins
at the location pointed to by the frame base register
(as indicated
by
PDSC_FLAGS_BASE_REG_IS_FP
) added to the value of the contents
of
PDSC_RPD_RSA_OFFSET
.
The result must be a quadword-aligned address.
The set of registers saved in this area contains the return address followed
by the registers specified in the procedure descriptor by
PDSC_FLAGS_IMASK
and
PDSC_FLAGS_FMASK
.
The details of how to lay out and
populate the register save area are described in
Section 3.1.2.2.
A compiler can use the stack temporary area for storage of fixed local variables, such as constant-sized data items, program state information, and dynamically sized local variables. The stack temporary area can also be used for dynamically sized items with a limited lifetime, such as a dynamically sized function result or string concatenation that cannot 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 that base in order to reclaim storage used by temporaries.
The high-address end of the stack frame is defined by the value stored
in
PDSC_RPD_FRAME_SIZE
added to the contents of the SP or FP register,
as indicated by
PDSC_FLAGS_BASE_REG_IS_FP
.
The high-address end
is used to determine the value of the SP register for the predecessor procedure
in the call chain.
3.1.2.2 Register Save Area
The layout of the frame of a stack frame procedure contains a substructure called the register save area. This section describes how this area is defined and populated.
All registers saved in the variable portion of the register save area must have the corresponding bit set to 1 in the appropriate procedure descriptor register save mask. This bit must be set to 1 even if the register is not a member of the set of registers required to be saved across a standard call. If the bit is not set to 1, the offsets within the save area cannot be calculated correctly.
The algorithm for packing saved registers in the quadword-aligned register save area is as follows:
The return address is saved at the lowest address of the register save area, offset 0.
All saved integer registers
,as indicated by the corresponding bit
in
PDSC_RPD_IMASK
being set to 1, are stored, in register-number
order, in consecutive quadwords beginning at offset 8 of the register save
area.
All saved floating-point registers
, as indicated by the corresponding
bit in
PDSC_RPD_FMASK
being set to 1, are stored, in register-number
order, in consecutive quadwords following the saved integer registers.
Note
A floating-point register saved in the stack is stored as a 64-bit exact image of the register; that is, no bit reordering is done in the process of moving the data to or from memory. Compilers must use an
stt
instruction to store the register regardless of the floating-point type.This behavior is required so that an unwind routine can properly restore the floating-point registers without more complete type information.
A standard-conforming procedure that utilizes a register save area must save the return address register at offset 0 in the register save area. There is no corresponding bit in the register save mask for this register slot.
Figure 3-3
shows the layout of
the register save area.
Figure 3-3: Register Save Area Layout
RSA.SAVED_RETURN
is the contents of the return
address register.
For example, if registers $10, $11, $14, $22, $f2, and $f3 are saved
by a standard-conforming procedure, the
PDSC_RPD_IMASK
value is
00404C00 (hex) and the
PDSC_RPD_FMASK
is 0000000C (hex).
The register
save area for such a procedure is packed as shown in
Figure 3-4.
Figure 3-4: Register Save Area Example
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" because of the relatively fast way it saves 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-conforming 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 might not necessarily do so.
Note
Lightweight procedures have more freedom than might be apparent. By the use of appropriate agreements with callers of the lightweight procedure as well as with procedures that the lightweight procedure calls, and by the use of unwind handlers, a lightweight procedure can modify nonscratch registers and can call other procedures.
Agreements such as these can be made by convention (as in the case of language-support routines in the run-time library) or by interprocedural analysis. Calls employing such agreements are, however, not standard calls, and might not be fully supported by a debugger because it might not be able to find the contents of the preserved registers, for example.
Because such agreements must be permanent for upward compatibility of object code, lightweight procedures should, in general, follow the normal restrictions.
A null frame procedure is a simple case of a register frame procedure. The null frame procedure has the following characteristics:
The entry return address register is $26 (PDSC_RPD_ENTRY_RA
= 26).
The return address is not saved in any other register (PDSC_RPD_SAVE_RA
=
PDSC_RDP_ENTRY_RA
).
No stack space is allocated (PDSC_RPD_SP_SET
=
0 and
PDSC_RPD_FRAME_SIZE
= 0).
As a result of these characteristics, the prologue requires
no instructions (PDSC_RPD_ENTRY_LENGTH
= 0).
There is no associated exception handler (PDSC_RPD_HANDLER_ADDRESS
= 0).
This special case of a register frame procedure is of interest because
it has an associated special-case procedure descriptor representation.
(See
Section 8.1
for information about procedure descriptor representation.)
3.2 Transfer of Control
A standard-conforming procedure call can use any sequence of instructions that presents the called routine with the required environment. (See the standard call definition in Section 1.5.) However, the majority of standard-conforming external calls is performed with a common sequence of instructions and conventions. This common set of call conventions is so pervasive that it is included as part of this standard in Section 3.2.1.
This calling standard has been designed so that the same instruction
sequence can be used to call each different type of procedure; that is, the
caller does not have to know which type of procedure is being called.
3.2.1 Call Conventions
The following call conventions describe the rules and methods used to communicate certain information between the caller and the called procedure during invocation and return:
The calling procedure must pass the procedure value of the called procedure to the called procedure. This value can be a statically or dynamically bound procedure value. To pass this value, the calling procedure must load $27 with the procedure value before control is transferred to the called procedure. (See Section 2.2 for a description of procedure values.)
When a target routine is not loaded in memory at the beginning of execution of a main program or shared image, the procedure value used by a caller of that routine generally addresses some kind of stub or jacket routine. The purpose of a stub or jacket routine is to perform the loading of the actual target routine. The call is completed after this load operation.
When control actually reaches the target routine entry point, $27 must contain the actual procedure value of the newly loaded routine as if no intermediate processing had occurred. Subject to these constraints, the PV register can be used freely by the stub/jacket code as a temporary register during its own execution.
The calling procedure must pass the address to which control must be returned to the called procedure 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. A standard-conforming procedure must treat this register as preserved. For a standard call, the return address is passed and returned in the return address register, $26.
The argument list is an ordered set of zero or more argument items, which together include a logically contiguous structure known as an argument item sequence. In practice, this logically contiguous sequence is mapped to registers and memory in a fashion that produces a physically discontiguous argument list. In a standard call, the first six items are passed in registers $16 - $21 and/or registers $f16 - $f21. (See Section 4.1.2 for details of argument-to-register correspondence.) 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).
If a standard-conforming procedure is a function and the function result is returned in a register, the result is returned in $0, $f0, or $f0 - $f1. Otherwise, the function result is returned using the first argument item or else dynamically, as defined in Section 4.1.7.
The stack pointer (SP) must at all times denote an address that has octaword alignment. (This restriction has the side effect that the in-memory portion of the argument list, if any, will start on an octaword boundary.) Note that the stack grows toward lower addresses. During a procedure invocation, SP can never be set to a value that is higher than the value of SP at entry to that procedure invocation.
The contents of the stack, located above the portion of the argument list (if any) that is passed in memory, belong to the calling procedure. Because they are part of the calling procedure, they should not be read or written by the called procedure, except as specified by indirect arguments or language-controlled up-level references.
The SP value might be used by the hardware when 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 Architecture Reference Manual, and as a result of asynchronous software actions.
When a standard procedure is called, the caller must provide the procedure value (code address) of the called procedure in $27 so the called procedure can compute the address of the global offset table (GOT) segment. (See Section 2.5 for information about the global offset table.)
If access to a GOT segment is required, two instructions in the called
procedure's prologue
will compute
the GOT pointer value (GP register contents) using
ldah
and
lda
instructions together with the passed $27 value.
Because the
ldah/lda
pair can generate addresses only within 2 GB of $27, the code
and GOT must be within ± 2 GB of each other.
Typically these instructions
are the first two in the procedure.
(In certain cases, at link time these
two instructions may be replaced by
nop
instructions, skipped,
or removed if the linker is able to determine that they are redundant.) The
resultant pointer into the GOT segment is placed in the GP register.
This
pointer can then be used by the called procedure as a base register to address
locations in the GOT.
Because a standard-conforming calling procedure must assume that the GP register value is destroyed across a call but must itself return it with the correct value, the code following the call must reestablish its value before further accesses to the GOT or by the time it returns from the procedure. (See Section 2.3.1 for information on integer registers.)
The following list describes some ways to reestablish the GP register value:
The caller can use an
ldah/lda
sequence to compute
the correct GP value from $26 because a standard call returns with $26 pointing
at a known code address (the return address).
The caller can save the GP value in a register that is preserved across the call and move it back into the GP register after the call.
The caller can save the GP value in its stack frame and reload it after the call.
In summary, a standard-conforming call provides the procedure value
(code address) to the called procedure (in case it needs to compute a new
GP value) and provides its own GP value to the called procedure (in case it
shares the GOT segment).
Furthermore, upon return from the called procedure,
the GP value must be restored (in case the called procedure did not share
the same GOT segment).
3.2.3 Link-Time Optimization
The design of this calling standard assumes and expects that the normal call conventions described here will be improved by certain optimizations performed at link time in response to compiler-provided control information. However, the specified calling conventions will behave correctly even in the absence of link-time optimization.
For many calls, the called procedure shares the same GOT
segment with the caller.
In these cases, the GP register is already valid for the called procedure
at the time of the call.
Several optimizations are possible in these important
cases.
When one procedure calls another that shares the same GOT segment
and the first two instructions of the called procedure establish the GP, the
ldah/lda
pair can be skipped.
Because the procedure's code address has
no other use, the caller does not need to provide it in $27.
If the called
procedure's GP value that is returned in the GP register is shared with the
calling procedure, the caller does not need to reestablish the GP register
contents.
The following code fragment shows a typical standard call:
ldq $27,target_ptr(GP) #Load procedure value (entry address) jsr $26,($27) #Call with return address in $26 ldah GP,fix_hi($26) #Reload GP value lda GP,fix_lo(GP)
Note that other instructions may be scheduled among the ones shown here.
If the linker optimizes the call, it can be transformed to look like the following code fragment:
bsr $26,target+8 #Make the call
The instructions that are no longer needed can be replaced by
nop
instructions, or deleted and compressed out.
Depending on the optimizations
performed and whether the
ldah/lda
pair at the target is moved,
removed, or reduced to just
lda
, the call may or may not load $27,
may execute a
jsr
or a
bsr
, and may go to
target
or
target+8
.
3.2.4 Calling Computed Addresses
Most calls are made to a fixed address whose value is already 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 these cases, the procedure value representing the procedure to be called will be computed in a register.
For example, suppose $4 contains such a computed procedure value (simple or bound). The following code fragment calls the procedure that $4 describes:
mov $4,$27 #Move the procedure value to $27. jsr $26,($4) #Call the entry address. ldah GP,fix_hi($26) #Reload the GP register. lda GP,fix_lo(GP)
There are two distinct classes of procedures:
Simple procedures
Bound procedures
A simple procedure is one that does not need direct access to the stack of its execution environment. A bound procedure is one that must have direct access to the stack of its execution environment, typically so that it can reference an up-level variable or perform a nonlocal goto. Simple procedures and bound procedures have associated procedure descriptors, as described earlier in this chapter.
Bound procedure values are designed for multilanguage use. They 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 to allow it to reference its up-level environment. Typically, such a pointer is the frame pointer for that environment, though many variations are possible. When the caller is itself executing within that outer environment, it usually can 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 the nested procedure can be called in the same way as 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 establishes the proper environment for the bound procedure and then transfers control to that procedure.
One direct scheme for constructing a jacket
to a bound procedure so it can be called
like a simple procedure is to allocate a sequence of instructions and data
on the stack and use the address of those instructions as the procedure value.
For example, suppose that a bound procedure named
proc
expects
its static link to be passed in $1.
The following code fragment shows a suitable
sequence of instructions for the call:
ldq $1,24($27) #Fetch the up-level pointer to $1 ldq $27,16($27) #Fetch the address of the bound # procedure code jmp ($27) #Transfer to the bound procedure nop #Include a filler to align following # data quadword-holding-procedure-code-address quadword-holding-static-link
Note that this sequence can only be created by code that is executing within the context of the containing procedure so that the appropriate frame pointer value is known and can easily be incorporated in 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 jacket instructions, it is necessary to execute an
imb
instruction before executing them to assure instruction cache coherence,
as described in the
Alpha Architecture Reference Manual.
It might also be necessary to make the stack segment executable, for example,
by using the
mprotect()
system call.
(See the
mprotect
(2)
reference
page.)
3.2.6 Entry and Exit Code Sequences
The following sections describe 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.
Except as noted, the exact instruction sequences are not specified;
any instruction sequence that produces the defined effects is legal.
3.2.6.1 Entry Code Sequence
Because the value of
the PC defines the currently executing procedure, all properties of the environment
specified by a procedure's descriptor must be valid before the first instruction
after the procedure prologue
(as defined by
PDSC_RPD_ENTRY_LENGTH
) is executed.
In addition, none of the properties specified in the
calling procedure's descriptor may be invalidated before the called procedure
becomes current.
Thus, until the procedure becomes current,
all entry code must adhere to the following rules:
All registers specified by this standard as saved across a standard-conforming call must contain their original (at entry) contents.
The register designated by
PDSC_RPD_ENTRY_RA
($26
in a standard call) must contain its original (at entry) contents.
This requirement
also applies to nonstandard procedures to allow for proper unwinding.
No standard calls can be made.
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 because the procedure is not yet current. Thus, when a procedure has an exception handler, compilers must not move into the procedure prologue any code that might cause an exception which would be handled by that same handler.
When a procedure is called, the code at the entry address must do the following:
Synchronize, as needed, any pending exceptions caused by instructions issued by the caller.
Save the caller's context.
Make the called procedure current by executing the last instruction of the procedure prologue.
These actions involve the following steps, performed in the specified order:
Compute and load the procedure's GP value using the passed-in-procedure (code address) value in $27. (This code can appear elsewhere in the prologue, but provides more opportunities for linker optimizations if it appears first.)
If stack space is allocated (PDSC_RPD_FRAME_SIZE
is not 0), set register SP to SP -
PDSC_RPD_FRAME_SIZE
.
After any necessary calculations and stack limit checks, this step must
be completed in exactly one instruction that modifies SP.
This instruction
must be the one specified by
PDSC_RPD_SP_SET
.
For a stack frame procedure (PDSC_FLAGS_REGISTER_FRAME
is 0), do both of these steps.
(There is no requirement as to which
step occurs first.)
Store the registers specified by
PDSC_RPD_IMASK
and
PDSC_RDP_FMASK
in the register save area based on
PDSC_RPD_RSA_OFFSET
.
Store the return address in the register save area.
For a register frame procedure (PDSC_FLAGS_REGISTER_FRAME
is 1), copy the return address to the register specified by
PDSC_RPD_SAVE_RA
if the value is not already there.
Execute
trapb
, if required.
(See
Section 5.1.12
for details.)
For a variable-size stack frame procedure (PDSC_FLAGS_BASE_REG_IS_FP
is 1), copy the SP value to register FP.
This step must be completed in exactly one instruction that modifies the FP and that instruction must be the last instruction in the prologue.
When these 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.
The remainder of this section contains the following:
A description of prologue length
A description of frame pointer conventions
An example of entry code for a stack frame
An example of entry code for a register frame
As a general rule, it is
valid to include instructions in the prologue (in addition to those that are
required) in order to take advantage of available processor cycles that are
not otherwise used.
However, any such additional instructions must not cause
an exception that would need to be handled by that procedure if that exception
would be raised after the procedure became current.
(An exception is considered
to not be handled by a procedure if it is known that the handler will always
resignal that exception.)
3.2.6.1.2 Frame Pointer Conventions
After the procedure prologue
is completed, the register indicated by
PDSC_FLAGS_BASE_REG_IS_FP
must contain the frame pointer of the stack frame.
The
frame pointer
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
PDSC_RPD_FRAME_SIZE
subtracted from the value of the stack pointer
upon procedure
entry.
For fixed frame procedures, the frame pointer is the stack pointer.
In these cases, the stack pointer is not modified by that procedure after
the instruction in that procedure prologue specified by
PDSC_RPD_SP_SET
.
3.2.6.1.3 Entry Code Example for a Stack Frame Procedure
This section contains an entry code example for a stack frame procedure. The example assumes the following:
Registers $9 - $11 and $f2 - $f3 are saved and restored.
PDSC_RPD_RSA_OFFSET
is equal to 16 bytes.
The procedure has a static exception handler that does not re-raise arithmetic traps.
The procedure uses a fixed amount of stack (PDSC_FLAGS_BASE_REG_IS_FP
is 0 ).
The following example illustrates a Stack Frame Procedure.
ldah GP, off_hi($27) #Compute the correct GP value. lda GP, off_lo(GP) lda SP,-SIZE(SP) #Allocate space for a new stack frame. stq $26,16(SP) #Save the return address. stq $9,24(SP) #Save the first integer register. stq $10,32(SP) #Save the next integer register. stq $11,40(SP) #Save the next integer register. stt $f2,48(SP) #Save the first floating-point register. stt $f3,56(SP) #Save the last floating-point register. trapb #Force any pending hardware exceptions # to be raised. #The called procedure is now the current procedure.
3.2.6.1.4 Entry Code Example for a Register Frame Procedure
This section contains an entry code example for a register frame procedure. The example assumes the following:
The called procedure has no static exception handler.
PDSC_RPD_SAVE_RA
and
PDSC_RPD_ENTRY_RA
specify $26.
The procedure utilizes a fixed amount of stack storage (PDSC_FLAGS_BASE_REG_IS_FP
is 0).
The following example illustrates a Register Frame Procedure.
ldah GP, off_hi($27) #Compute the correct GP value. lda GP, off_lo(GP) lda SP,-SIZE(SP) #Allocate space for a new stack frame. #The called procedure is now the current procedure.
The end of procedure
entry code can be determined easily by using a PC value together with the
PDSC_RPD_ENTRY_LENGTH
value.
Because there can be multiple return points
from a procedure, detecting that a procedure exit sequence is being executed
is not as straightforward.
Unwind support routines must be able to detect
if the stack pointer
has been reset and if not, know how to reset it.
The exit sequence can
be detected by requiring a reserved instruction sequence.
The next sections provide the following information:
A discussion of the reserved instruction sequence
The steps involved in an exit code sequence
An example of exit code for a stack frame
An example of exit code for a register frame
3.2.6.2.1 Reserved Instruction Sequence for a 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 can be used in any other way.
The following reserved instruction must appear at every exit point from
any procedure that uses the stack (PDSC_RPD_FRAME_SIZE
is not 0):
ret $31,($n),0001 #Return to the caller with usage hint 0001
Note
The term usage hint, shown in the comment in the previous example, refers to the value of the branch prediction bits encoded in the
ret
instruction. The section on control instructions in the Alpha Architecture Reference Manual documents that these bits, <13:0> of the instruction longword, are reserved to software when the instruction isret
orjsr_coroutine
.This calling standard further requires that these bits contain the value 0001 (hex) for procedure returns and 0000 otherwise. The occurrence of the usage hint value 0001 identifies a
ret
instruction as one that is reserved for use only as described in the Alpha Architecture Reference Manual. Theret
instructions can be used for other purposes, provided they contain a usage hint value of 0000. Thoseret
instructions will not be recognized and treated in a special way for the purposes of exception handling or unwinding.In almost all cases, the return address register ($n) used will be $26, because it is required to be reloaded prior to the procedure return.
For any such procedure that does not return a value on the stack, the
ret
instruction must be immediately preceded by either of the two reserved
stack resetting instructions.
The following examples show the two different
reserved stack-resetting instructions:
lda SP,* #Reset the stack. ret $31,(*),0001 #Return to the caller with usage hint. addq *,*,SP #Reset the stack. ret $31,(*),0001 #Return to the caller with usage hint.
Thus, 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 it is immediately followed
by the reserved procedure return instruction.
A stack-resetting instruction might not be present in the case of a
procedure that returns a result on the top of the stack.
However, if such
an instruction is present, it will immediately preced the reserved
ret
instruction.
Furthermore, for any such procedure that has
PDSC_FLAGS_BASE_REG_IS_FP
set to 1, the resulting sequence must immediately follow the FP reloading
instruction as in the following example:
ldq FP,* #Restore the FP. lda SP,* #Or addq *,*,SP to reset the stack. ret $31,(*),0001 #Return to the caller with usage hint.
Thus, any
ldq
instruction whose destination is the FP register
($15) 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 that is in turn immediately followed by the reserved procedure
return instruction.
Procedures that do not use the stack do not need to use these reserved instruction sequences.
The unwind support code uses the exit code sequences to make the following assumptions about an interrupted PC value:
If the PC points within the prologue, the registers still
have their original contents.
Only SP must be reset if the PC is beyond
PDSC_RPD_SP_SET
.
Then the unwind can proceed.
If the PC points at a
ret $31,(*),0001
instruction,
SP has already been reset and the registers have already been restored, so
the unwind can proceed.
If the PC points to an
lda SP,*
(or an
addq
*,*,SP)
instruction that is immediately followed by the instruction
described previously, the registers have already been restored.
But SP must
be incremented by
PDSC_RPD_FRAME_SIZE
before the unwind can proceed.
If the PC points to an
ldq FP,*
instruction that
is immediately followed by either of the instructions described previously,
PDSC_FLAGS_REGISTER_FRAME
is 0 and
PDSC_FLAGS_BASE_REG_IS_FP
is 1, all registers other than FP have been restored.
FP still retains the
frame base pointer, which should be copied to SP.
Then FP must be restored
and SP incremented by
PDSC_RPD_FRAME_SIZE
for the unwind to proceed.
In all other cases, the registers must be restored and SP reset for the unwind to proceed.
When a procedure has executed the first instruction of one of these
reserved sequences, 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 within that procedure.
3.2.6.2.2 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. The following list contains the exit code sequence steps. The program performs step 1, followed by steps 2 through 5 in any order, followed by steps 6 through 8 in exact order.
If the GP register has been modified or a call has been made, restore the GP register to the GOT segment pointer of the current procedure.
For a variable-size stack frame procedure that does not return
a value on the top of stack (PDSC_FLAGS_BASE_REG_IS_FP
is 1), copy
FP to SP.
For a stack frame procedure (PDSC_FLAGS_REGISTER_FRAME
is 0), reload any saved registers from the register save area as specified
by
PDSC_RPD_RSA_OFFSET
.
Note that, for a variable-size stack frame
procedure (PDSC_FLAGS_BASE_REG_IS_FP
is 1), FP is not reloaded
in this step.
For a fixed-size stack frame procedure (PDSC_FLAGS_BASE_REG_IS_FP
is 0), $15 is reloaded if it was saved on entry.
Reload the register that held the return address on entry with the saved return address, if necessary.
For a stack frame procedure (PDSC_FLAGS_REGISTER_FRAME
is
0), load the register designated by
PDSC_RPD_ENTRY_RA
($26 in a
standard call) with the return address from the register save area as specified
by
PDSC_RPD_RSA_OFFSET
.
For a register frame procedure (PDSC_FLAGS_REGISTER_FRAME
is 1), copy the return address from the register specified by
PDSC_RPD_SAVE_RA
to the register designated by
PDSC_RPD_ENTRY_RA
.
Execute
trapb
, if required.
(See
Section 5.1.12
for details.)
For a variable-size stack frame procedure (PDSC_FLAGS_REGISTER_FRAME
is 0 and
PDSC_FLAGS_BASE_REG_IS_FP
is 1), reload $15 (FP)
as would be done for any other saved register.
After any necessary calculations, this step must be completed by exactly one instruction, as described in Section 3.2.6.1.
If a function value is not being returned on the stack, restore
SP to the value it had at procedure entry by adding the value in
PDSC_RDP_FRAME_SIZE
to SP.
In some cases, the returning procedure leaves SP pointing to
a lower stack address than it had on entry to the procedure, as specified
in
Section 4.1.7.
After any necessary calculations, this step must be completed by exactly one instruction, as described in Section 3.2.6.1.
Execute the
ret $31,($n),0001
instruction, as described
in
Section 3.2.6.2.1, to return control to the calling
procedure.
In almost all cases the $n used will be $26 (the return address
register) because its value must be restored before the call returns.
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 can choose to defer removal of arguments because of optimizations or
other considerations.
3.2.6.2.3 Exit Code Example for a Stack Frame Procedure
The following example shows the return code sequence for the stack frame procedure example in Section 3.2.6.1.3. This code fragment assumes that the computed GP value was saved in the preserved register $11:
mov $11,GP #Restore this routine's GP value. ldq $26,16(SP) #Get the return address. ldq $9,24(SP) #Restore the first integer register. ldq $10,32(SP) #Restore the next integer register. ldq $11,40(SP) #Restore the next integer register. ldt $f2,48(SP) #Restore the first floating-point register. ldt $f3,56(SP) #Restore the last floating-point register. trapb #Force any pending hardware exceptions to be # raised. lda SP,SIZE(SP) #Restore the SP. ret $31,($26),0001 #Return to the caller with the usage hint.
3.2.6.2.4 Exit Code Example for a Register Frame Procedure
The following example shows the return code sequence for the register frame procedure example in Section 3.2.6.1.4.
lda SP,SIZE(SP) #Restore the SP. ret $31,($26),0001 #Return to the caller with the usage hint.