OpenVMS MACRO-32 Porting and User's Guide
1.4 Identifying Nonportable VAX MACRO Coding Practices
When examining a VAX MACRO module that you intend to compile to OpenVMS
Alpha object code, look for any of the following coding constructs. The
occurrence of these in a module can make porting take longer than it
would otherwise. Although the compiler can identify many of these
practices, recognizing them yourself will speed up the porting effort.
For more information about nonportable MACRO coding practices,
including some that occur less frequently and are less easy to detect,
see Chapter 3.
- Removing the return address from the stack to return to the
caller's caller, such as the following (see Section 3.3.4):
TSTL (SP)+ ; remove return address
RSB ; return to caller's caller
- Temporarily removing the return address from the stack to allocate
space on the stack using mechanisms such as the following (see
POPL R0 ; remove the return address
SUBL #structure_size,SP ; allocate stack space for caller
PUSHL R0 ; replace the return address
POPL R1 ; hold return address
PUSHL structure ; build structure for caller
; code continues
JMP (R1) ; return to caller
- Pushing a label onto the stack, as in the following examples, often
as an attempt to construct a return address for an RSB instruction (see
- Modifying the frame pointer (FP) (see Section 3.1.1). VAX MACRO code
typically modifies the frame pointer for one of two reasons:
- To manually assemble a call frame on the stack.
- To use the frame pointer to reference local storage allocated in a
- Constructing an REI target, as in the following examples (see
MOVAL target_label,-(SP) ; all three
MOVAL target_label,-(SP) ; force AST delivery only
; code continues
- Branching to a destination that consists of a label plus an offset
as in the following example. The
appearance of this practice in VAX MACRO code may indicate a branch
past some data in the code stream, such as the register save mask at
the top of a .CALL_ENTRY routine (see Section 3.2.1). Alternatively, it
may be a sign that the code is familiar with and dependent upon the
size of VAX instructions (see Section 3.2.3).
- Moving an opcode to a location, usually the stack or a data area,
as shown in the following example. This
practice indicates either generated or self-modifying code and will
require redesign as indicated in Section 3.2.2.
- Jumping across modules. Because of architectural requirements, the
compiler must handle jumps across modules as JSBs. Therefore, external
branch targets as in the following example must be declared with the
.JSB_ENTRY directive (see Section 2.2.3).
1.5 Establishing Useful Coding Conventions
Section 1.3 describes a recommended process for porting VAX MACRO code
to OpenVMS Alpha. Although this process may provide a mechanism for
porting code efficiently, it cannot by itself guarantee that the
porting effort will be consistent among the engineers who are
performing the porting, or will be intelligible to the engineers who
will later need to debug and test ported code. To ensure that the
porting effort proceeds uniformly and that its effects on source code
are well documented, an engineering group should establish coding
conventions that are relevant to the goals of the effort.
Naturally, any methodology an engineering group may adopt should be
shaped by that group's development environment, including those
procedures the group follows for tool management, source code control,
common code, and testing. The coding conventions an engineering group
should evaluate include:
- Establishing VAX MACRO source modules that are common to VAX and
Alpha systems, and conditionalizing architecture specific code (see
- Providing clear and consistent register declarations in the
compiler's entry-point directives (see Section 2.2)
1.6 Maintaining Common Sources for VAX and Alpha Systems
When designing a VAX MACRO code porting effort, consider the benefits
of maintaining common sources for VAX and Alpha systems. It is
advantageous to an engineering group to have only one copy of its
sources to maintain and enhance, and common sources help ensure common
user interfaces. However, if you find that you are conditionalizing so
much source code that it is no longer intelligible, take steps to
restructure the code into architecture-specific and
architecture-independent pieces. If the number of these pieces becomes
unmanageable, create separate architecture-specific modules.
1.6.1 Including Compiler Directive Definitions
A successful compilation does not preclude VAX MACRO code in a source
file from also processing successfully under the VAX MACRO assembler.
If you added any compiler directives to your code, they will be
resolved by the library SYS$LIBRARY:STARLET.MLB when the code is
assembled. The assembler automatically searches this library for any
undefined macros. After finding these definitions, the assembler will
ignore occurrences of the compiler directives.
However, if you are using OpenVMS VAX Version 6.0 or earlier, you must
first extract the directive definitions from ALPHA$LIBRARY:STARLET.MLB
on Alpha and insert them into your SYS$LIBRARY:STARLET.MLB on OpenVMS
VAX. For example:
LIB/INSERT SYS$LIBRARY:STARLET.MLB JSB_ENTRY.MAR
Note that many of the definitions of the compiler directives refer to
other macros. Make sure to extract not only the definitions of all the
compiler directives used in your code but also all the associated
macros and insert them into SYS$LIBRARY:STARLET.MLB on your OpenVMS VAX
1.6.2 Removing VAX Dependencies
If you must make changes to source files because they contain certain
coding practices that cannot be directly compiled into Alpha code, you
may still be able to generate images for both VAX and Alpha systems
from common VAX MACRO sources.
Removing such VAX dependencies so that the same code can run on both
VAX and Alpha systems can yield great benefits during the porting
process. You can debug single modules or groups of modules of the
ported code by building and testing the modules in the VAX environment.
This can greatly speed your debugging process.
In some cases, you must define and implement procedures for
conditionalizing the source files (as described in Section 1.6.3) or for
providing separate sources for VAX and Alpha systems.
If the code runs in an inner mode, it is unlikely that an effort to
generate VAX and Alpha images from common VAX MACRO sources will be
fully successful. Because inner-mode code interoperates with the
executive, it is vulnerable to the differences between VAX and Alpha
system interfaces and executive data structures. However, user-mode
code is generally immune from architectural dependencies and may more
easily serve as the basis for common code.
1.6.3 Using Architecture-Specific Symbols
Conditionalizing VAX MACRO code requires the use of two ARCH_DEFS.MAR
files with architecture-specific symbols, one to be assembled with the
VAX MACRO source code on a VAX processor, the other to be compiled with
the VAX MACRO source code on an Alpha processor.
If you choose to make code in a common source module conditional on
architecture type, include ARCH_DEFS.MAR in your assembly and
compilation and use .IF DF,VAX or .IF DF,ALPHA.
An ARCH_DEFS.MAR file is provided with OpenVMS Alpha. You need to
create a corresponding file for OpenVMS VAX.
The following is an example of such a file:
; This is the VAX version of ARCH_DEFS.MAR, which contains
; architectural definitions for compiling sources for
; VAX systems.
VAX = 1
VAXPAGE = 1
ADDRESSBITS = 32
The Alpha version exists in SYS$LIBRARY and contains definitions for
the following symbols:
- ALPHA---to indicate the source code is Alpha architecture specific
- BIGPAGE---to indicate the source code assumes variable memory page
Any other symbols in ARCH_DEFS.MAR on OpenVMS Alpha are specific to
OpenVMS Alpha source code and are not guaranteed to be included from
release to release.
1.7 Using the MACRO-32 Compiler
Compaq recommends that you use the current version of the MACRO-32
compiler. Make sure that the version of SYS$LIBRARY:STARLET.MLB that
ships with the compiler version you are using is installed on your
system and that the logical name points to the correct directory.
For compiling code to run on AlphaServer systems with the Alpha
processor 21264 (or later), you must use the following version of the
- Version 3.1 or later for OpenVMS Alpha Version 7.1-2
- Version 4.1 or later for OpenVMS Alpha Version 7.2
- Shipping version of the compiler for later versions of OpenVMS Alpha
Directions for invoking the compiler are provided in Appendix A.
How to Use the MACRO-32 Compiler
The MACRO-32 Compiler for OpenVMS Alpha has been designed to help you
port VAX MACRO source code from OpenVMS VAX to OpenVMS Alpha. When
operating on VAX MACRO code that complies with the restrictions and
guidelines described in this manual, the compiler produces a valid
OpenVMS Alpha object module that preserves the semantics of the
original VAX MACRO source and adheres to the OpenVMS calling standard.
This chapter describes the following topics:
As discussed in Chapter 1, the compiler cannot
transparently convert all VAX MACRO code. There are many coding
practices that cannot be directly compiled into OpenVMS Alpha code.
They must be removed or modified to guarantee a successful compilation.
The compiler can detect and report many of these coding practices.
2.1 Using Alpha Registers
Alpha computers employ 32 integer registers, R0 through R31. Because
code generated by the compiler uses Alpha registers R0 through R12 as
if they were VAX registers, VAX MACRO code usage of these registers
(for instance, as input to JSB routines) does not have to change to
achieve a correct compilation. VAX MACRO instructions (such as MOVL and
ADDL) use the lower 32 bits of the Alpha register involved in the
operation. The compiler maintains a sign-extended 64-bit form of this
value in the register.
Registers R13 and above are also available to VAX MACRO code that will
be compiled to OpenVMS Alpha object format. If you decide to use these
registers, review the following constraints:
- Generally, existing VAX MACRO code uses only registers R0 through
R11, R12 through R14 being specially defined as the argument pointer
(AP), frame pointer (FP), and stack pointer (SP), respectively. The
compiler will compile legal uses of AP, FP, and SP as references to the
Alpha registers that have functions similar to the VAX registers. If a
VAX MACRO source is referencing AP as a scratch register, the compiler
converts this reference to a reference to R12. If this is not
desirable, you should change the code to use a different scratch
- If the compiler detects a reference to R12, R13, and R14 in a VAX
MACRO source, it will not convert it to a reference to the
Alpha equivalent of AP, FP, or SP. Rather, it will consider the
reference to be to the corresponding Alpha integer register.
- Code that uses R13 and above cannot be assembled by the VAX MACRO
assembler. Therefore, it should be conditionalized for OpenVMS Alpha or
appear in a module that is specific to OpenVMS Alpha.
- The compiler allows you to access the full 64 bits of Alpha
registers by using the EVAX_LDQ and EVAX_STQ built-ins, as described in
- You should take special care when referencing registers with
specific architected usage, as defined in the OpenVMS Calling Standard.
- Registers 16 and above are scratch registers, as defined in the
OpenVMS Calling Standard. You cannot expect their values to survive across routine
- The compiler may use registers R13 and above as temporary registers
if they are not used in the source code for a routine. R13 through R15
are saved and restored if they are used.
2.2 Routine Calls and Declarations
The OpenVMS Calling Standard specifies very different calling conventions for VAX
and Alpha systems.
On a VAX system, there are two different call formats, CALL and JSB,
with five different instructions: CALLS, CALLG, JSB, BSBW, and BSBB.
CALL instructions create a frame on the stack which contains return
information, saved registers, and other routine information. Parameters
to CALL instructions are passed in consecutive longword memory
locations, either on the stack (CALLS) or elsewhere (CALLG). JSB
instructions have the return address stored on the stack. For both call
formats, the hardware processing of the calling instruction provides
all of these functions.
On an Alpha system, there is only one call format and only one
subroutine call instruction, JSR. For routines that expect parameters,
the first six parameters are passed in R16 through R21, with the
parameter count in R25 and subsequent parameters in quadwords on the
stack. The hardware execution of the JSR instruction simply passes
control to the subroutine and places the return address in a designated
register. It neither allocates stack space, nor creates a call frame,
nor provides any parameter manipulations. All of these functions must
be done by the calling routine or the called routine.
The compiler must generate code that conforms to the OpenVMS Calling Standard yet
emulates the function of the different VAX MACRO instructions. This
requires special code both at the calling instruction to manipulate the
input parameters and at the entry point of the routine itself to create
a stack frame and other context.
The compiler generates code only for source instructions that are part
of a declared routine. For the compiler to generate the proper linkage
information and proper routine code, you must insert a compiler
directive at the entry points in the VAX MACRO source.
2.2.1 Linkage Section
On Alpha systems, all external (out-of-module) references are made
through a linkage section. The linkage section is a program
section (psect) containing:
- Addresses of external variables
- Constants too large to fit directly in the code stream
- Linkage pairs
- Procedure descriptors
A linkage pair is a data structure used when making a call to
an external module. The linkage pair contains the address of the
callee's procedure descriptor, and the entry point address. The linkage
pair resides in the caller's linkage section; therefore, there may be
several linkage pairs for a particular routine, one in each caller's
linkage section. Linkage pairs are not used for module-local calls,
since the compiler can refer to the callee's frame descriptor directly.
A procedure descriptor is a data structure that provides basic
information about a routine, such as register and stack usage. Each
routine has one unique procedure descriptor, and it is located in its
linkage section. The procedure descriptor is normally not accessed at
run time. Its primary use is for interpreting the frame in case of
exceptions, by the debugger, for example.
For more information about these and other Alpha data structures, see
the OpenVMS Calling Standard.
2.2.2 Prologue and Epilogue Code
To mimic the behavior of VAX subroutines, the compiler must generate
code at the entry and exit of each routine. This code emulates the
functions that are done by the VAX hardware, and is called the prologue
and epilogue code.
In the prologue code, the compiler must allocate stack space to save
any necessary context. This includes saving any registers that will be
preserved by the routine and saving the return address. If this is a
CALL routine, it also includes establishing a new stack frame and
changing the frame pointer (FP), and may include further manipulation
and storage of the input parameters (see Section 2.3).
In the epilogue code, the compiler must restore any necessary
registers, stack frame information, and the return address, and trim
the stack back to its original position.
2.2.3 When to Declare Entry Points
Any code label that is a possible target of a CALLS, CALLG, JSB, BSBW,
or BSBB instruction must be declared as an entry point. In addition,
any code label must be declared as an entry point using a .JSB_ENTRY or
.JSB32_ENTRY directive if:
- The label can be the target of a global (cross-module) JMP, BRB, or
- The label can be the target of an indeterminate branch (such as BRB
@(R10)), where the address of the label has been stored in R10, even if
the reference and the label are within the same module.
- The address of the label is stored in a register or memory
location, even if it is never accessed by the current module.
The OpenVMS calling standard for Alpha computers does not provide a way
to access indeterminate code addresses directly. All such accesses are
accomplished using a procedure descriptor to describe the routine and
the code address. When a code label address is stored, the compiler
does not know if that address will be referenced only by the current
module, or whether it may be accessed by another MACRO module or
another module written in another language. Whenever a source
instruction stores a code address, the MACRO-32 compiler instead stores
the procedure descriptor address for that code address so that other
code can access it correctly. For a procedure descriptor to exist, the
label must be declared as an entry point.
Likewise, when a stored address is used as a branch destination, the
compiler does not know where that address came from, so it always
assumes that the stored address is the address of a procedure
descriptor and uses that descriptor to pass control to the routine.
2.2.4 Directives for Designating Routine Entry Points
Macros in STARLET.MLB generate directives for designating the entry
points to routines. (Appendix B describes the format of each of these
macros.) When assembled for VAX systems, the macros are null, except
for .CALL_ENTRY; when compiled for Alpha systems, the macros expand to
verify the arguments and generate the compiler directives. Therefore,
you can use the following macros in common source modules:
- The .CALL_ENTRY directive logically replaces the .ENTRY directive in
MACRO-32 code. However, you do not need to replace the .ENTRY directive
unless you want to use one of the .CALL_ENTRY clauses. If you replace
the .ENTRY directives, note that you cannot do a massive replace with
an editor because the arguments do not match.
Since the .ENTRY directive is converted to an Alpha .CALL_ENTRY
directive, any registers modified in the routine declared by .ENTRY
will be automatically preserved. This is different from VAX MACRO
behavior. See Section 2.3.2.
- The .JSB_ENTRY directive indicates a routine that is executed as a
result of a JSB, BSBB, or BSBW instruction. The .JSB32_ENTRY directive
is used for specialized environments that do not require the
preservation of the upper 32 bits of Alpha register values.
.JSB_ENTRY directive should also be used to declare entry points which
are the targets of JMP or branch instructions from other modules, since
such cross-module branches are implemented as JSBs by the compiler.
Branches to stored code addresses, such as JMP (R0), are also
treated as JSB instructions. Hence, potential targets must be declared
with .JSB_ENTRY. The compiler warns you of this when you attempt to
store a code label or when such a branch is used.
- The .EXCEPTION_ENTRY directive designates
exception service routines.