The following sections describe:
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.
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.
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. |
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.
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-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.
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.
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.
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:
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. Note
This behavior is required so that an unwind routine can properly restore the floating-point registers without more complete type information.
Figure 3-3 shows the layout of the register save area.
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.
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.
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. Note
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.
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.)
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.
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.
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.
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 [plusmn] 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:
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).
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
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)
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
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.)
Except as noted, the exact instruction sequences are not specified; any instruction sequence that produces the defined effects is legal.
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. Note
When a procedure is called, the code at the entry address must do the following:
These actions involve the following steps, performed in the specified order:
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.
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:
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.
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.
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 next sections provide the following information:
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
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 is ret or jsr_coroutine. Note
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. The ret instructions can be used for other purposes, provided they contain a usage hint value of 0000. Those ret 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.
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:
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.
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.
After any necessary calculations, this step must be completed by exactly one instruction, as described in Section 3.2.6.1.
After any necessary calculations, this step must be completed by exactly one instruction, as described in Section 3.2.6.1.
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.
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.
lda SP,SIZE(SP) #Restore the SP. ret $31,($26),0001 #Return to the caller with the usage hint.