With the Alpha edition of the compiler, the _ _asm statement can be used to write macros that can be called from within C/C++ source code. The syntax of the _ _asm statement makes such macros possible because of the flexible support for arguments. As illustrated in the following example, the arguments do not have to be fixed, and they can even be specified at the time of macro expansion. However, this macro must be called only from within C/C++ functions.
In this example, the sample macro implements a function named my_mult
, which takes two unsigned 32-bit integer arguments, a
and b
, and multiplies them together. The product is stored at the location specified by the third argument, c
, which is a pointer to a 32-bit argument. The function also determines what the overflow portion of the multiplication is, if any, and returns the overflow portion via its return value.
// C++ only, declare asm statement as intrinsic: //
#ifdef __cplusplus
extern "C" { __int64 __asm(char *,...); };
#pragma intrinsic(asm)
#endif
// long my_mult(long a, long b, long* c) //
// Action: *c <- a*b, return overflow //
#define my_mult(a,b,c)
asm("zapnot %0,15,%0;" // zero-extend a
"zapnot %1,15,%1;" // zero-extend b
"mulq %0,%1,t0;" // multiply 64 bits
"stl t0,0(%2);" // save low 32 bits
"srl t0,32,v0", // and high 32 bits
a,b,c) // pass arguments
The following is a simple driver program that invokes this macro:
void main()
{
long m1 = 0xf0000000, // multiplicands
m2 = 0x00001010,
product,overflow; // results
overflow = my_mult(m1,m2,&product);
printf("%08X x %08X = %08X%08X\n\n",
m1,m2,overflow,product);
}
Note the ways in which the C/C++ code and the assembly code interact in the preceding example:
a
, b
, and c
, using the register specifiers %0
, %1
, and %2
, respectively. These are the first three argument registers; they can also be referred to as %a0, %a1, and %a2.
The Alpha Visual C++ compiler will generate the appropriate sequence of instructions to evaluate the arguments supplied to an _ _asm statement and load the results in the appropriate machine registers before instantiating the user-supplied _ _asm statement code.
In the preceding example, all of the individual instructions in the body of the _ _asm statement are encoded in a single _ _asm statement, with all of the arguments to the _ _asm statement supplied in one place. Note that it is possible to code this example using individual _ _asm statements; this choice is left up to you.
In either case, the compiler generates the code shown in the following example for the invocation of my_mult
inside of main
. Notice that most of the _ _asm statements are assembled as instructions in a one-to-one relationship between those in the body of the _ _asm statement and the instructions listed. However, there are several exceptions to this general one-to-one relationship:
The compiler may reorder the sequence of generated instructions so as to optimize the overall flow of data through the processor. This process, known as “instruction scheduling,” is guaranteed to preserve the logical results of your code, but as can be seen from the code listing below, it will invariably make it harder for you to understand the generated code. Even in the context of a simple function such as main
in our example, this can make reading the code challenging. We have annotated this example to show the purpose of each instruction; this output is not provided by the Alpha Visual C++ compiler. Instructions not annotated concern themselves with the housekeeping details required by the Alpha calling standard.
main
.0(%2)
, the compiler was able to directly convert this target operand into a reference to the automatic variable named result
, stored at 8(sp).
0000 main
245FF000 0000 ldah m1,-4096(zero) // asm setup
223F1010 0004 mov 4112,a1 // asm setup
4841F630 0008 zapnot m1,15,a0 // asm body
4A21F631 000C zapnot a1,15,a1 // asm body
23DEFFF0 0010 lda sp,-16(sp)
4E110401 0014 mulq a0,a1,t0 // asm body
B75E0000 0018 stq ra,(sp)
261F0000 001C ldah a0,h^.data(zero) // printf:
22100000 0020 lda a0,l^.data(a0) // arg 1
47E20411 0024 mov m1,a1 // arg 2
225F1010 0028 mov 4112,a2 // arg 3
B03E0008 002C stl t0,8(sp) // asm body
48241680 0030 srl t0,32,v0 // asm body
47DF081E 0034 xor sp,zero,sp // printf:
A29E0008 0038 ldl a4,8(sp) // arg 5
43E00013 003C sextl v0,a3 // arg 4
D3400000 0040 bsr ra,j^printf // printf()
A75E0000 0044 ldq ra,(sp)
47FF0400 0048 clr v0
23DE0010 004C lda sp,16(sp)
6BFA8001 0050 ret ra