HP OpenVMS Systems Documentation

Content starts here

OpenVMS MACRO-32 Porting and User's Guide


Previous Contents Index

2.2.5 Code Generation for Routine Calls

The Alpha code generated for VAX JSB, BSBW, and BSBB instructions is very simple because no parameter manipulation must be done. However, the code for CALLS and CALLG instructions is more complex because the compiler must translate the parameter handling of the VAX calling standard to the Alpha calling standard. When processing a CALLS instruction with a fixed argument count, the compiler automatically detects any pushes onto the stack that are actually passing parameters to a routine, and generates code that moves the parameters directly to the registers used by the Alpha calling standard.

Consider the following VAX call:


        PUSHL   R2
        PUSHL   #1
        CALLS   #2,XYZ

This VAX call is compiled as follows:


        LDQ     R26, 32(R13)            ; Get routine address
        LDQ     R27, 40(R13)            ; Get routine linkage pointer
        SEXTL   R2, R17                 ; R2 is second parameter
        BIS     R31, 1, R16             ; #1 is first parameter
        BIS     R31, 2, R25             ; 2 parameters
        JSR     R26, R26                ; call the routine

For a CALLS instruction with a variable argument count or a CALLG instruction, the compiler must call an emulation routine which unpacks the argument list into the Alpha format. This has two side effects. On VAX systems, if part of the argument list pointed to by a CALLG instruction is inaccessible, but the called routine does not access that portion of the argument list, the routine will complete successfully without incurring an access violation. On Alpha systems, since the entire argument list is processed before calling the routine, the CALLG will always incur an access violation if any portion of the argument list is inaccessible. Also, if either the argument count of a CALLG instruction or a CALLS instruction with a variable argument count exceeds 255, the emulated call returns with an error status without ever invoking the target routine.

2.3 Declaring CALL Entry Points

Because of the differences between the VAX and the Alpha calling standards (see Section 2.2), the compiler must translate all VAX parameter references to AP in the following ways:

  • It converts parameter references off AP in the called routine into direct register and stack references to the new Alpha parameter locations.
  • It detects references to AP that may result in aliased references to the parameter list, or that use variable offsets which cannot be resolved at compile time. The compiler mimics VAX argument lists by packing the quadword register and stack arguments into a longword argument list on the stack. Two arguments to the .CALL_ENTRY directive have been created for this purpose (see Section 2.3.1).

In the latter case, the practice is known as homing the argument list. The resulting homed argument list is in the fixed position of the stack frame, so all references are FP-based.

2.3.1 Homed Argument Lists

Examples of AP references for which the compiler automatically homes the arguments include:

  • Storing AP or an address based on AP in another register.
  • Passing AP or an address based on AP as a parameter.
  • Adding a variable offset to AP to reference a parameter.
  • Using variable indexing off AP to reference a parameter.
  • Using a non-longword aligned offset into the arglist, such as 6(AP).

The compiler sets up the homed argument list in the fixed temporary region of the procedure frame on the stack.

Although not mandatory, you can specify homing with two arguments to the .CALL_ENTRY directive, home_arg=TRUE and max_args=n.

The argument max_args=n represents the maximum number of longwords the compiler should allocate in the fixed temporary region. The main reason for using max_args=n is if your program receives more arguments than the number explicitly used in the source code. A common reason for specifying home_arg=true in a routine, which does not in itself require it, is the invocation of a JSB routine that references AP. For such references, the compiler assumes that the argument list was homed in the last .CALL_ENTRY routine, and uses FP-based references. Because this may not always be a valid assumption, the compiler reports an informational message when AP is used inside .JSB_ENTRY routines.

If you want to suppress the informational messages that the compiler reports, use both arguments.

If you omit one or both arguments and the compiler detects a use that requires homing, messages are issued by the compiler, informing you of what the compiler did. You can then make the appropriate changes to your code, such as adding one or both arguments or changing the value of the max_args argument.

If you only specify home_args=TRUE, the following message is issued:

AMAC-W-MAXARGEXC, MAX_ARGS exceeded in routine routine_name, using n

This message is issued because you explicitly asked for homing but did not specify the number of arguments with max_args. If the compiler does not find any explicit argument reference, then it allocates six longwords. If the compiler finds explicit argument references, it allocates the same number of longwords as the highest argument reference found. (This message is also issued if you specify a value for max_args which is less than the number of arguments found by the compiler to be homed.)

If you specify only max_args, and if the compiler detects a reference requiring homing, the compiler issues the following message:

AMAC-I-ARGLISHOME, argument list must be homed in caller

If you specify neither argument and the compiler detects a reference requiring homing, the compiler issues the following message:

AMAC-I-ARGLISHOME, argument list must be homed in caller
AMAC-I-MAXARGUSE, max_args value used for homed arglist is n

where n represents the highest argument referenced, as detected by the compiler.

2.3.2 Saving Modified Registers

A well-behaved VAX MACRO CALL routine passes all parameters via the argument list and saves all necessary registers via the register mask in the .ENTRY declaration. However, some routines do not adhere to these standards and may pass parameters via registers or save and restore the contents of the necessary registers within the routine with instructions such as PUSHL and POPL.

Using PUSHL and POPL to save and restore registers on an Alpha system is insufficient because these instructions save only the low 32 bits of the register. To avoid register corruption, the compiler automatically saves and restores the full 64-bit values of any register modified within the routine (except R0 and R1), in addition to the registers specified in the register save mask. This means that any registers that were intended to pass an output value out of the called routine will no longer do so. You must either pass the output via the standard argument list or declare the register to be output with the output parameter in the .CALL_ENTRY declaration (see Section 2.5).

2.3.3 Modifying the Argument Pointer

If a routine modifies AP, the compiler changes all uses of AP in that routine to R12 and reports all such modifications. Although traversing the argument list in this way is not supported, you can obtain similar results by explicitly moving the address of 0(AP) to R12 and specifying home_args=true in the entry point. R12 will point to a VAX format argument list.

2.3.4 Establishing Dynamic Condition Handlers in Called Routines

To establish a dynamic condition handler in a called routine, it is necessary to store a condition handler address in the frame. The compiler generates a static condition handler for .CALL_ENTRY routines that modify 0(FP). The static handler invokes the dynamic handler or returns if no condition handler address has been stored in the frame.

For performance reasons, the compiler does not automatically insert a TRAPB instruction at the end of every routine that establishes a condition handler. Because of the indeterminate nature of traps on an Alpha system, this could allow traps from instructions near the very end of the routine to be processed after the frame pointer has been changed in the routine epilogue code, with the result that the declared handler would not process the trap. If it is essential that any traps generated in the routine be processed by the declared handler, insert an EVAX_TRAPB built-in immediately before the RET instruction that ends the routine.

2.4 Declaring JSB Routine Entry Points

In assembled VAX MACRO code and compiled Alpha object code alike, JSB routine parameters are typically passed in registers.

A JSB routine that writes to registers sometimes saves and restores the contents of those registers, unless the new contents are intended to be returned to a caller as output. However, because it is fairly common for a VAX MACRO module to save and restore register contents by issuing instructions such as PUSHL and POPL, saving only the low 32 bits of the register contents, the compiler must add 64-bit register saves/restores at routine's entry and exit points.

The compiler can compute the set of used registers, but without complete knowledge of the routine's callers (which may be in other modules), it cannot determine which registers are intended to contain output values. For this reason, the user must add, at each JSB routine entry point, a .JSB_ENTRY or a .JSB32_ENTRY directive that declares the routine's register usage.

2.4.1 Differences Between .JSB_ENTRY and .JSB32_ENTRY

The compiler provides two different ways of declaring a JSB entry point. The .JSB_ENTRY directive is the standard declaration, and the .JSB32_ENTRY directive is provided for cases when you know that the upper 32 bits of the 64-bit register values are never required to be preserved by the routine's callers.

In routines declared with the .JSB_ENTRY directive, the compiler, by default, saves at routine entry and restores at routine exit the full 64-bit contents of any register (except R0 and R1) that is modified by the routine. If a register is not explicitly modified by the routine, the compiler will not preserve it across the routine call. You can override the compiler's default behavior by means of register arguments that can be specified with the .JSB_ENTRY directive, as described in Section 2.5.

When .JSB32_ENTRY is used, the compiler does not automatically save or restore any registers, thus leaving the current 32-bit operation untouched. The compiler will only save and restore the full 64-bit value of registers you explicitly specified in the PRESERVE argument.

If the routine you are porting is in an environment where you know that no caller ever needs to have the upper 32 bits of a register preserved, Compaq recommends that you use the .JSB32_ENTRY directive. It can be much faster for porting code because you do not have to examine the register usage of each routine. You can specify the .JSB32_ENTRY directive with no arguments, and the existing 32-bit register push/pop code will be sufficient to save any necessary register values.

Note

The Alpha compilers for other languages may use 64-bit values. Therefore, any routine which is called from another language --- or which is called from another MACRO-32 routine that is in turn called from another language --- cannot use .JSB32_ENTRY.

2.4.2 Two General Cases for Using .JSB32_ENTRY

There are two general cases where you can use .JSB32_ENTRY:

  • If a user-mode application or self-contained subsystem is written entirely in MACRO-32 you can use .JSB32_ENTRY throughout the application.
  • If you have one major MACRO-32 routine that is called from another language or from any source which requires 64-bit register preservation, and that routine calls several other MACRO-32 routines, you can set a barrier of 64-bit preservation. You can do this by using .JSB_ENTRY or .CALL_ENTRY on the major routine and preserving all registers that are not explicit outputs. The internal subroutines can then use the .JSB32_ENTRY directive.
    Note that it is not sufficient to just use .JSB_ENTRY or .CALL_ENTRY for the barrier without explicitly preserving all registers, since by default the compiler will only save and restore the registers that are explicitly modified in the major routine. The internal subroutines that use .JSB32_ENTRY may then modify registers that are not being preserved. You must also make sure that none of the internal subroutines can be called in any way that bypasses the barrier.

Warning

The .JSB32_ENTRY directive can be a great time-saver if you are sure that you can use it. If you use .JSB32_ENTRY in a situation where the upper 32 bits of a register are being used, it may cause very obscure and difficult-to-track bugs by corrupting a 64-bit value that may be several calling levels above the offending routine.

.JSB32_ENTRY should never be used in an AST routine, condition handler, or any other code that can be executed asynchronously.

2.4.3 PUSHR and POPR Instructions Within JSB Routines

There will be cases when you add a .JSB_ENTRY directive to a routine which already saves/restores some registers via PUSHR/POPR. Depending on routine usage, some registers may end up being saved/restored twice, once by the compiler and again by the PUSHR/POPR. Do not attempt to optimize this unless the code is extremely performance sensitive. The compiler attempts to detect this and eliminate the redundant save/restores.

2.4.4 Establishing Dynamic Condition Handlers in JSB Routines

The compiler will flag, as illegal, any code in a .JSB_ENTRY routine that attempts to modify 0(FP).

2.5 Declaring a Routine's Register Use

The compiler provides four register declaration arguments: input, output, scratch, and preserve, that you can specify in a .CALL_ENTRY, .JSB_ENTRY, or .JSB32_ENTRY entry-point directive. These register arguments are used to describe the usage of registers in the routine and to instruct the compiler how to compile the routine. You can use the register arguments to:

  • Override the compiler's default preservation behavior (see Section 2.3.2 and Section 2.4.1).
  • Indicate the nonavailability of registers for temporary compiler usage.
  • Document routine register usage.

Note

If you specify /OPTIMIZE=VAXREGS to use VAX registers as temporary registers, you must declare all implicit register uses with the input and output clauses to prevent their use as temporary registers. When this optimization is enabled, the compiler can use as temporary registers any registers that are not explicitly declared.

2.5.1 Input Argument for Entry Point Register Declaration

The input argument indicates those registers from which the routine receives input values. In some cases, the routine itself does not use the contents of the register as an input value, but rather calls a routine that does. In the latter case, known as the pass-through input technique, the other routine should also declare the register as input in its routine entry mask.

The input argument has no effect on the compiler's default register preservation behavior. Registers specified only in the input argument will still be treated by the compiler exactly as described in Section 2.3.2 and Section 2.4.1. If a register is used as an input and you want to change the default preservation behavior, you should specify the register in the output, preserve, or scratch arguments in addition to the input argument.

The input argument informs the compiler that the registers specified have meaningful values at routine entry and are unavailable for use as temporary registers even before the first compiler-detected use of the registers. Since the compiler does not normally use any of the VAX registers (R2 through R12) as temporary registers, specifying registers in the input argument affects compiler temporary register usage in two cases:

  • If you are using the VAXREGS optimization switch. This optimization allows the compiler to use as temporary registers any of the VAX registers which are not explicitly being used by the VAX MACRO code. Note that for .JSB32_ENTRY directives, the compiler always assumes that all the VAX registers are used as input when using the VAXREGS optimization, so it is not necessary to specify VAX registers in the input argument to prevent their use as compiler temporary registers.
  • If you are explicitly using any of the Alpha registers (R13 and above).

In either of these cases, if you do not specify a register that is being used as input in the input argument, the compiler may use the register as a temporary register, corrupting the input value.

If you are not using the VAXREGS optimization switch or any of the Alpha registers, the input mask is used only to document your routine.

2.5.2 Output Argument for Entry Point Register Declaration

The output argument indicates those registers to which the routine assigns values that are returned to the routine's caller. In many cases, the routine itself modifies the register; in others, the routine calls another routine that deposits the output value. In the latter case, known as the pass-through output technique, the other routine must also declare the register as output in its routine entry register set.

The use of this argument prevents the automatic preservation of registers that are modified during the routine. Any register included in this argument will not be preserved by a .CALL_ENTRY or .JSB_ENTRY routine, even if it is modified by the routine.

The output argument informs the compiler that the registers specified have meaningful values at routine exit and are unavailable for use as temporary registers even after the last compiler-detected use of the registers. Since the compiler does not normally use any of the VAX registers (R2 through R12) as temporary registers, specifying registers in the output argument only affects compiler temporary register usage in two cases:

  • If you are using the VAXREGS optimization switch. This optimization allows the compiler to use as temporary registers any of the VAX registers which are not explicitly being used by the VAX MACRO code. Note that for .JSB32_ENTRY directives, the compiler always assumes that all the VAX registers are used as output when using the VAXREGS optimization, so it is not necessary to specify VAX registers in the output argument to prevent their use as compiler temporary registers.
  • If you are explicitly using any of the Alpha registers (R13 and above).

In either of these cases, if you do not specify a register that is being used as output in the output argument, the compiler may use the register as a temporary register, corrupting the output value.

For .JSB32_ENTRY routines, since no registers are preserved by default and all registers are assumed to be outputs by the VAXREGS optimization, the output argument is used only to document your code.

2.5.3 Scratch Argument for Entry Point Register Declaration

The scratch argument indicates those registers that are used within the routine but should not be saved and restored at routine entry and exit. The caller of the routine does not expect to receive output values in these registers nor does it expect the registers to be preserved.

The use of this argument prevents the automatic preservation of registers that are modified during the routine. Any register included in this argument will not be preserved, even if it is modified by the routine.

The scratch argument also pertains to the compiler's temporary register usage. The compiler may use registers R13 and above as temporary registers if they are unused in the routine source code. Since R13 through R15 must be preserved, if modified, according to the OpenVMS Alpha calling standard, the compiler preserves those registers if it uses them.

However, if they appear in the scratch register set declaration, the compiler will not preserve them if it uses them as temporary registers. As a result, these registers may be scratched at routine exit, even if they were not used in the routine source but are in the scratch set. If the VAXREGS optimization is used, this applies to registers R2 through R12, as well.

For .JSB32_ENTRY routines, since R2 through R12 are not preserved by default, their inclusion in the scratch declaration is for documentation purposes only.


Previous Next Contents Index