HP OpenVMS Systems Documentation

Content starts here

OpenVMS Alpha Guide to 64-Bit Addressing and VLM Features


Previous Contents Index

11.4.4 Functions Restricted to 32-Bit Pointers

Some functions in the DEC C RTL do not support 64-bit pointers. If you try to pass a 64-bit pointer to one of these functions, the compiler generates a %CC-W-MAYLOSEDATA warning. Applications compiled with /POINTER_SIZE=64 might need to be modified to avoid passing 64-bit pointers to these functions.

Table 11-2 shows the functions restricted to using 32-bit pointers. The DEC C RTL offers no 64-bit support for these functions. You must ensure that only 32-bit pointers are used with these functions.

Table 11-2 Functions Restricted to 32-Bit Pointers
atexit getopt modf setstate
execve iconv recvmsg setvbuf
execvp initstate sendmsg  
frexp ioctl setbuf  

Table 11-3 shows functions that make callbacks to user-supplied functions as part of processing that function call. The callback procedures are not passed 64-bit pointers.

Table 11-3 Callbacks that Pass only 32-Bit Pointers
from_vms to_vms
ftw tputs

11.5 Reading Header Files

This section introduces the pointer-size manipulations used in the DEC C RTL header files. Use the following examples to become more comfortable reading these header files and to help modify your own header files.


Examples

#1

:
#if INITIAL__POINTER_SIZE (1)
#   if (VMS__VER < 70000000) || !defined __ALPHA (2)
#      error " Pointer size usage not permitted before OpenVMS Alpha V7.0"
#   endif
#   pragma __pointer_size __save (3)
#   pragma __pointer_size 32 (4)
#endif
:
:
#if __INITIAL_POINTER__SIZE (5)
#   pragma __pointer_size 64
#endif
:
:
#if __INITIAL_POINTER_SIZE (6)
#   pragma __pointer_size __restore
#endif
:


      

All DEC C compilers that support the /POINTER_SIZE qualifier predefine the macro __INITIAL_POINTER_SIZE. The DEC C RTL header files take advantage of the ANSI rule that if a macro is not defined, it has an implicit value of 0.

The macro is defined as 32 or 64 when the /POINTER_SIZE qualifier is used. It is defined as 0 if the qualifier is not used. The statement shown as (1) can be read as "if the user has specified either /POINTER_SIZE=32 or /POINTER_SIZE=64 on the command line."

DEC C Version 5.2 and higher is supported on many OpenVMS platforms. The lines shown as (2) generate an error message if the target of the compilation is one that does not support 64-bit pointers.

A header file cannot assume anything about the actual pointer-size context in effect at the time the header file is included. Furthermore, the DEC C compiler offers only the __INITIAL_POINTER_SIZE macro and a mechanism to change the pointer size, but no way to determine the current pointer size.

All header files that have a dependency on pointer sizes are responsible for saving (3), initializing (4), altering (5), and restoring (6) the pointer-size context.

#2

:
#ifndef __CHAR_PTR32 (1)
#   define __CHAR_PTR32 1
    typedef char * __char_ptr32;
    typedef const char * __const_char_ptr32;
#endif
:
:
#if __INITIAL_POINTER__SIZE
#   pragma __pointer_size 64
#endif
:
:
#ifndef __CHAR_PTR64 (2)
#   define __CHAR_PTR64 1
    typedef char *__ char_ptr64;
    typedef const char * __const_char_ptr64;
#endif
:


      

Some function prototypes need to refer to a 32-bit pointer when in a 64-bit pointer-size context. Other function prototypes need to refer to a 64-bit pointer when in a 32-bit pointer-size context.

DEC C binds the pointer size used in a typedef at the time the typedef is made. The typedef declaration of __char_ptr32 (1) is made in a 32-bit context. The typedef declaration of __char_ptr64 (2) is made in a 64-bit context.

#3

:
#if INITIAL__POINTER_SIZE
#   if (__VMS_VER < 70000000) || !defined __ALPHA
#      error " Pointer size usage not permitted before OpenVMS Alpha V7.0"
#   endif
#   pragma __pointer_size save
#   pragma __pointer_size 32
#endif
:
(1)
:
#if __INITIAL_POINTER_SIZE (2)
#   pragma __pointer_size 64
#endif
:
(3)
:
int abs (int __j); (4)
:
__char_ptr32 strerror (int __errnum); (5)
:


      

Before declaring function prototypes that support 64-bit pointers, the pointer context is changed (2) from 32-bit pointers to 64-bit pointers.

Functions restricted to 32-bit pointers are placed in the 32-bit pointer context section (1) of the header file. All other functions are placed in the 64-bit context section (3) of the header file.

Functions that have no pointer-size impact ((4) and (5)) are located in the 64-bit section. Functions that have no pointer-size impact, except for a 32-bit address return value (5), are also in the 64-bit section, and use the 32-bit specific typedefs previously discussed.

#4

:
#if __INITIAL_POINTER_SIZE
#   pragma __pointer_size 64
#endif
:
:
#if __INITIAL_POINTER_SIZE == 32 (1)
#   pragma __pointer_size 32
#endif
:
char *strcat (char *__s1, __const_char_ptr64 __s2); (2)
:
#if __INITIAL_POINTER_SIZE
#   pragma __pointer_size 32
    :
    char *_strcat32  (char *__s1, __const_char_ptr64 __s2); (3)
    :
#   pragma __pointer_size 64
    :
    char *_strcat64  (char *__s1, const char *__s2); (4)
    :
#endif
:


      

This example shows declarations of functions that have both a 32-bit and 64-bit implementation. These declarations are located in the 64-bit section of the header file.

The normal interface to the function (2) is declared using the pointer size specified on the /POINTER_SIZE qualifier. Because the header file is in 64-bit pointer context and because of the statements at (1), the declaration at (2) is made using the same pointer size context as the /POINTER_SIZE qualifier.

The 32-bit specific interface (3) and the 64-bit specific interface (4) are declared in 32-bit and 64-bit pointer-size context, respectively.


Chapter 12
MACRO--32 Programming Support for 64-Bit Addressing

This chapter describes the new 64-bit addressing support provided by the MACRO--32 compiler and associated components. The changes are primarily for argument passing and receiving and for address computations.

12.1 Guidelines for 64-Bit Addressing

The following guidelines pertain to using 64-bit addressing in VAX MACRO code that is compiled for OpenVMS Alpha:

  • Limit its use to code that you have ported to OpenVMS Alpha.
    For any new development on OpenVMS Alpha, Compaq recommends the use of higher-level languages.
  • Make 64-bit addressing explicit in your code.
    The 64-bit addressing qualifiers, macros, directives, and built-ins produce code that is more reliable and easier to maintain.

12.2 New and Changed Components for 64-Bit Addressing

The new and changed components that provide MACRO--32 programming support for 64-bit addressing are shown in Table 12-1.

Table 12-1 New and Changed Components for 64-Bit Addressing
Component Description
$SETUP_CALL64 New macro that initializes the call sequence.
$PUSH_ARG64 New macro that does the equivalent of argument pushes.
$CALL64 New macro that invokes the target routine.
$IS_32BITS New macro for checking the sign extension of the low 32 bits of a 64-bit value.
$IS_DESC64 New macro for determining if descriptor is a 64-bit format descriptor.
QUAD=NO/YES New parameter for page macros to support 64-bit virtual addresses.
/ENABLE=QUADWORD The QUADWORD parameter was extended to include 64-bit address computations.
.CALL_ENTRY QUAD_ARGS=TRUE|FALSE QUAD_ARGS=TRUE|FALSE is a new parameter that indicates the presence (or absence) of quadword references to the argument list.
.ENABLE QUADWORD/.DISABLE QUADWORD The QUADWORD parameter was extended to include 64-bit address computations.
EVAX_SEXTL New built-in for sign extending the low 32 bits of a 64-bit value into a destination.
EVAX_CALLG_64 New built-in to support 64-bit calls with variable-size argument lists.
$RAB64 and $RAB64_STORE New RMS macros for using buffers in 64-bit address space.

12.3 Passing 64-Bit Values

The method that you use for passing 64-bit values depends on whether the size of the argument list is fixed or variable. These methods are described in the following sections.

12.3.1 Calls with a Fixed-Size Argument List

For calls with a fixed-size argument list, use the new macros shown in Table 12-2.

Table 12-2 Passing 64-Bit Values with a Fixed-Size Argument List
Step Use...
1. Initialize the call sequence $SETUP_CALL64
2. "Push" the call arguments $PUSH_ARG64
3. Invoke the target routine $CALL64

An example of using these macros follows. Note that the arguments are pushed in reverse order, which is the same way a 32-bit PUSHL instruction is used.


MOVL           8(AP), R5         ; fetch a longword to be passed
$SETUP_CALL64  3                 ; Specify three arguments in call
$PUSH_ARG64    8(R0)             ; Push argument #3
$PUSH_ARG64    R5                ; Push argument #2
$PUSH_ARG64    #8                ; Push argument #1
$CALL64        some_routine      ; Call the routine

The $SETUP_CALL64 macro initializes the state for a 64-bit call. It is required before $PUSH_ARG64 or $CALL64 can be used. If the number of arguments is greater than six, this macro creates a local JSB routine, which is invoked to perform the call. Otherwise, the argument loads and call are inline and very efficient. Note that the argument count specified in the $SETUP_CALL64 does not include a pound sign (#). (The standard call sequence requires octaword alignment of the stack with its arguments at the top. The JSB routine facilitates this alignment.)

The inline option can be used to force a call with greater than six arguments to be done without a local JSB routine. However, there are restrictions on its use (see Appendix B).

The $PUSH_ARG64 macro moves the argument directly to the correct argument register or stack location. It is not actually a stack push, but it is the analog of the PUSHL instructions used in a 32-bit call.

The $CALL64 macro sets up the argument count register and invokes the target routine. If a JSB routine was created, it ends the routine. It reports an error if the number of arguments pushed does not match the count specified in $SETUP_CALL64. Both $CALL64 and $PUSH_ARG64 check that $SETUP_CALL64 has been invoked prior to their use.

12.3.1.1 Usage Notes for $SETUP_CALL64, $PUSH_ARG64, and $CALL64

Keep these points in mind when using $SETUP_CALL64, $PUSH_ARG64, and $CALL64:

  • The arguments are read as aligned quadwords. To pass a longword from memory, move it to a register first, and then use that register in $PUSH_ARG64, as shown in the example in Section 12.3.1. Similarly, if you know the quadword you want to pass is unaligned, move the value to a register first. Also, keep in mind that indexed operands, such as (R4)[R0], will be evaluated using quadword indexing when used in $PUSH_ARG64.
  • If the number of arguments is greater than six, so that a local JSB routine is created, no SP or AP references are allowed between the $SETUP_CALL64 and $CALL64. The $PUSH_ARG64 and $CALL64 macros do report uses of these registers in operands, but they are not allowed in other instructions in this range and cannot be flagged. To pass an AP- or SP-based argument in this case, move it to a register before the $SETUP_CALL64 invocation.
  • If the number of arguments is greater than six, do not rely on values in registers above R15 surviving the $SETUP_CALL64 invocation. Use a nonscratch register as a temporary register instead. For example, suppose you want to pass a value from a stack location, and the call has more than six arguments. In this case, you need to move the value to a register. Rather than using a scratch register such as R28, use a VAX register, such as R0. If all the VAX registers are in use, use R13, R14, or R15.
  • It is safe to use the scratch registers above R16 within the range between the $SETUP_CALL64 and the $CALL64. However, you must be careful not to use an argument register that has already been loaded. The argument registers are loaded in downward order, from R21 through R16. So, suppose a call passes six arguments. It is not safe to use R21 after the first $PUSH_ARG64, because that has loaded R21. The $PUSH_ARG64 macro checks for operands that refer to argument registers that have already been loaded. If any are found, the compiler reports a warning. The safest approach is to use registers R22 through R28 when a temporary register is required.

Note

The $SETUP_CALL64, $PUSH_ARG64, and $CALL64 macros are intended to be used in an inline sequence. That is, you cannot branch into the middle of a $SETUP_CALL64/$PUSH_ARG64/$CALL64 sequence, nor can you branch around $PUSH_ARG64 macros or branch out of the sequence to avoid the $CALL64.

For more information about $SETUP_CALL64, $PUSH_ARG64, and $CALL64, see Appendix B.

12.3.2 Calls with a Variable-Size Argument List

For calls with a variable-size argument list, use the new EVAX_CALLG_64 built-in, as shown in the following steps:

  1. Create an in-memory argument list.
  2. Call a routine, passing the in-memory argument list. For example:


    EVAX_CALLG_64 (Rn), routine
    

The argument list in the EVAX_CALLG_64 built-in is read as a series of quadwords, beginning with a quadword argument count.

12.4 Declaring 64-Bit Arguments

You can use QUAD_ARGS=TRUE, a new .CALL_ENTRY parameter, to declare the use of quadword arguments in a routine's argument list. With the presence of the QUAD_ARGS parameter, the compiler behaves differently when a quadword reference to the argument list occurs. First, it does not force argument-list homing, which such a reference normally requires. (An argument list containing a quadword value cannot be homed because homing, by definition, packs the arguments into longword slots.) Second, unaligned memory reference will not be reported on these quadword references to the argument list.

Note that the actual code generated for the argument-list reference itself is not changed by the presence of the QUAD_ARGS clause, except when the reference is in a VAX quadword instruction, such as MOVQ. For the most part, QUAD_ARGS only prevents argument-list homing due to a quadword reference and suppresses needless alignment messages. This suppression applies to both EVAX_ built-ins and VAX quadword instructions such as MOVQ.

For VAX quadword instructions, the QUAD_ARGS clause causes the compiler to read the quadword argument as it does for EVAX_ built-ins---as an actual quadword. Consider the following example:


MOVQ    4(AP), 8(R2)

If the QUAD_ARGS clause is specified, MOVQ stores the entire 64 bits of argument 1 into the quadword at 8(R2). If the QUAD_ARGS clause is not specified, MOVQ stores the low longwords of arguments 1 and 2 into the quadword at 8(R2).

QUAD_ARGS also affects the code generated for deferred mode operands that are AP-based. If the effective address must be loaded from an argument in memory, it will be read as a quadword, rather than a longword, if QUAD_ARGS is in effect.

12.4.1 Usage Notes for QUAD_ARGS

Keep these points in mind when using QUAD_ARGS:

  • AP-based quadword argument-list references look strange because they appear to overlap. You can improve this situation by defining symbolic names for the argument-list offsets, for example, FIRST_ARG, SECOND_ARG, and so forth. Users are encouraged to define meaningful symbolic names that describe the uses of the arguments to make the source code more readable. Alternatively, you can still use direct argument register references to refer to the first six arguments. Either way, it is useful to declare QUAD_ARGS to ensure that the argument list is not homed.
  • Routines that share code must have the same setting for QUAD_ARGS. If they do not, the compiler will report a warning message.
  • JSB routines cannot refer to their caller's argument list if the caller has QUAD_ARGS. References to AP within JSB routines require that the last CALL_ENTRY have its argument list homed. HOME_ARGS and QUAD_ARGS are mutually exclusive.
  • QUAD_ARGS causes the $ARGn symbols, which the compiler places in the debug symbol table, to be defined as quadwords rather than longwords. These symbols allow easy access to received argument values and can be used in place of register numbers or stack offsets when debugging with the symbolic debugger.

12.5 Specifying 64-Bit Address Arithmetic

There are no explicit pointer-type declarations in MACRO--32. You can create a 64-bit pointer value in a register in a variety of ways. The most common are the EVAX_LDQ built-in for loading an address stored in memory and the MOVAx for getting the address of the specified operand.

After a 64-bit pointer value is in a register, an ordinary instruction will access the 64-bit address. The amount of data read from that address depends on the instruction used. Consider the following example:


MOVL     4(R1), R0

The MOVL instruction reads the longword at offset 4 from R1, regardless of whether R1 contains a 32- or 64-bit pointer.

However, certain addressing modes require the generation of arithmetic instructions to compute the effective address. For VAX compatibility, the compiler computes these as longword operations. For example, 4 + <1@33> yields the value 4 because the shifted value exceeds 32 bits. If quadword mode is enabled, the upper bit will not be lost.

In compilers shipping with previous versions of OpenVMS Alpha, the /ENABLE=QUADWORD qualifier (and the corresponding .ENABLE QUADWORD and .DISABLE QUADWORD directives) only affected the mode in which constant expression evaluations were performed. For OpenVMS Alpha Version 7.0, these have been extended to affect address computations. They will result in addresses being computed with quadword instructions, such as SxADDQ and ADDQ.

To have quadword operations used throughout a module, specify /ENABLE=QUADWORD on the command line. If you want quadword operations applied only to certain sections, use the .ENABLE QUADWORD and .DISABLE QUADWORD directives to enclose those sections.

There is no performance penalty when using /ENABLE=QUADWORD.


Previous Next Contents Index