Example 1: Alpha Multiplication Function with Overflow

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:

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.