HP OpenVMS Systems Documentation
HP OpenVMS Programming Concepts Manual
18.10 Function Value Return
A function is a routine that returns a single value to the calling routine. The function value represents the value of the expression in the return statement. As specified by the calling standard, a function value may be returned as an actual value in R0.
On VAX systems, if the actual function value returned is greater than 32 bits, then both R0 and R1 should be used.
On Alpha systems, if the actual function returned is a floating-point value, the floating-point value is returned either in F0 or in both F0 and F1.
A standard function must return its function value by one of the following mechanisms:
These mechanisms are the standard return convention because they support the language-independent data types. For information about condition values returned in R0, see Section 18.11.
For I64, values up to 128 bits are returned directly in the registers, according to the rules in Table 18-15.
Integer, enumeration, record, and set values (bit vectors) smaller than 64 bits must be zero-filled (unsigned integers, enumerations, records, sets) or sign-extended (signed integrals) to a full 64 bits. However, for unsigned 32-bit integers, bit 31 is replicated in bits 32--63.
When floating-point values are returned in floating-point registers, they are returned in the register format, rounded to the appropriate precision. When they are returned in the general registers (for example, as part of a record), they are returned in their memory format.
OpenVMS does not support a general notion of homogeneous floating-point aggregates. However, the special case of two single-precision or double-precision floating-point values implementing values of a complex type are handled in an analogous manner.
The rules in Table 18-15 are expressed in more detail in Table 18-11. F_floating and F_floating complex values in the general registers are zero-extended (Zero64), because this most closely approximates the effect of using the Alpha register format.
Return values other than those covered by Table 18-15 are returned in
a buffer allocated by the caller. A pointer to the buffer is passed to
the called procedure as a hidden first parameter, and all normal
parameters are shifted one slot to make this possible. The return
buffer must be aligned at a 16-byte boundary.
An OpenVMS system routine can indicate success or failure to the calling program by returning a condition value. In addition, an error condition to the calling program can return as a condition value in R0 (R8, R9 for I64) or by error signaling.
A condition value in R0 (R8, R9 for I64), also called a return status or completion code, is either a success (bit 0 = 1) value or an error condition (bit 0 = 0) value. In an error condition value, the low-order 3 bits specify the severity of the error (see Figure 18-16). Bits <27:16> contain the facility number, and bits <15:3> indicate the particular condition. Bits <31:28> are the control field. When the called procedure returns a condition value, the calling program can test R0 and choose a recovery path. A general guideline to follow when testing for success or failure is that all success codes have odd values and all error codes have even values.
Figure 18-16 Condition Value Format
When the completion code is signaled, the calling program must
establish a handler to get control and take appropriate action. (See
Chapter 9 or the HP OpenVMS Calling Standard for a description of signaling and
condition handling and for more information about the condition value.)
Because the I64 calling standard diverges from the Alpha and VAX calling standards regarding the use of registers and register mapping, and because Macro-32 assumes that registers are preserved across calls, the MACRO compiler maps registers to allow existing code to compile unmodified.
If you use OpenVMS high-level languages, the register and register mapping differences in the calling standards are handled by the compilers and are not exposed to your code. However, if your code uses Macro-32, C #pragma linkages, or BLISS linkages, your code might have to take into account the differences in register mapping.
This section describes I64 register usage and mapping.
OpenVMS I64 systems employ 32 integer registers, R0 through R31, with R0 being a read-only register that contains 0. This is different from OpenVMS Alpha, where R31 is a read-write register that contains 0.
In addition, the I64 calling standard has been written to be highly compatible with the Intel calling standard, and is quite different from the OpenVMS Alpha calling standard. For example, the standard return registers on I64 are R8/R9, not R0/R1 as on Alpha. The I64 calling standard reserves R1 as the GP (global pointer), does not include a standardized FP (frame pointer), and only has R4 through R7 as preserved across calls, not R2 through R15 as on Alpha.
I64 register usage differs from that of Alpha and VAX in the following key ways:
22.214.171.124 I64 Register Mapping in MACRO Compiler
The OpenVMS MACRO compiler compiles Macro-32 source code written for OpenVMS VAX systems (the VAX MACRO assembler) into machine code that runs on OpenVMS Alpha and OpenVMS I64 systems. Because Macro-32 source code is written with the VAX and Alpha calling standards in mind, the compiler performs several transformations to allow existing code to compile unmodified with the I64 compiler.
The MACRO compiler maps the registers in Macro-32 source programs to I64 registers on your behalf, as shown in Table 18-16, to minimize source changes. This allows existing programs to use "MOVL SS$_NORMAL, R0" and have the generated code return the value in R8 as prescribed by the calling standard. The mapping to an actual I64 register is totally transparent to the Macro-32 source code (and most of the compiler).
The register mapping was carefully chosen based on which registers were preserved across calls, which registers may be modified across calls, and which registers are volatile and do not even survive into or out of a call.
As on Alpha, Macro-32 references to AP are mapped by the compiler to the appropriate location depending on whether the arguments have been saved to the stack. To support references to FP, the compiler creates an FP value where needed. The compiler supports references to 0(FP) to establish condition handlers just like on VAX and Alpha.
The compiler does not provide any syntax for accessing I64 registers directly without going through the mapping table.
The automatic register mapping done by the compiler allows many Macro-32 programs (including those that access Alpha registers R16-R31) to compile without modificiations.
Note, however, that use of registers R16-R21 as routine parameters on Alpha is not portable to I64. Use PUSHL to pass parameters to a CALL, and use 4(AP), 8(AP), and so forth in the called routine to refer to them. The compiler will generate the correct register references instead of the stack references implied by the VAX operands.
On I64 systems, the compiler continues to recognize many of the EVAX_*
builtins that provide direct access to Alpha instructions on Alpha
systems. These built-ins will generate one or more I64 instructions to
perform the same logical operation. See the HP OpenVMS MACRO Compiler Porting and User's Guide for a
complete list of which EVAX_* built-ins are also supported on I64.
For I64 systems, add linkage directives (.CALL_LINKAGE, .DEFINE_LINKAGE, or .USE_LINKAGE) to mark VAX CALLS or CALLG instructions that call routines that return values in registers other than R0 or R1, or to JSB to routines written in a language other than Macro-32. These directives look similar to the .CALL_ENTRY directive and specify input, output, preserved, and scratch masks. In addition, they also have a language keyword to provide an alternative quick specification.
The .CALL_LINKAGE directive associates a named or anonymous linkage with a routine name. When the compiler sees a CALLS, CALLG, JSB, BSBB, or BSBW instruction with the routine name as the target, it will use the associated linkage to decide which registers need to be saved and restored around the call.
The .USE_LINKAGE directive establishes a temporary named or anonymous linkage that will be used by the compiler for the next CALLS, CALLG, JSB, BSBB, or BSBW instruction processed in lexical order. This directive is used when the target of the next CALLS, CALLG, JSB, BSBB, or BSBW instruction is not a name, but a run-time value (for example, CALLS #0, (R6)). When the compiler sees the next CALLS, CALLG, JSB, BSBB, or BSBW instruction, it will use the associated linkage to decide which registers need to be saved and restored around the call. After the instruction is processed, the temporary linkage is reset to null.
The .DEFINE_LINKAGE directive defines a named linkage that can be used with subsequent .CALL_LINKAGE or .USE_LINKAGE directives.
If your Macro-32 code uses a CALLS or CALLG instruction to access routines that return values in registers other than R0 or R1, the contents of the saved and restored registers may not be what you expect. Existing Macro-32 code traditionally assumes that registers R2-R11 and R15 are preserved and returned across calls. For CALLS and CALLG instructions, the MACRO compiler automatically saves and restores registers R2-R3 and R8-R15 in case the target of the call is not Macro-32. However, this means that changes made to these registers by the routine call are undone. This can cause problems if the routine return values were in registers other than R0-R1.
In the following example, m1.mar saves and preserve registers R2, R3, and R9 and undoes the changes made to these registers by the routine call.
To avoid this problem, add a .CALL_LINKAGE directive to m1.mar (or to a common prefix file or macro):
For JSB instructions, the MACRO compiler assumes that the target is also Macro-32 and does not save and restore anything. The compiler assumes that all registers flow in and out of the target routine. Alpha high-level language compilers would have preserved registers R2-R15. However, I64 high-level language compilers preserve only registers R4-R7.
In the following example m1.mar assumes that registers R0-R15 are returned or preserved by the target BLISS routine. On Alpha, BLISS would have done that. On I64, it preserves only registers R4-R7:
To avoid this problem, add a .CALL_LINKAGE directive to m1.mar:
Indirect calls with mismatched registers are not detected by the linker since it does not know what routine is being called. An indirect JSB to a BLISS or C routine requires a .USE_LINKAGE directive:
If the routine returns a register other than R0/R1:
See the HP OpenVMS MACRO Compiler Porting and User's Guide for additional information.
If you use OpenVMS high-level languages, the register and register mapping differences in the calling standards are handled by the compilers and are not exposed to your code. However, if your code uses C #pragma linkages or BLISS linkages to interface with Macro-32 source code, your code might have to take into account the differences in register mapping.
BLISS added a new qualifier and source level switch to enable register mapping for register numbers in linkage and register declarations. It is off by default. BLISS also has additional support for linkages that reference arguments. The C compiler changed the #pragma linkage to map the registers by default, along with additional support for linkages that reference arguments or floating registers. There are new pragmas to get unmapped linkages.