Introduction to Alpha

Sama suomeksi

This document describes the subset of Alpha needed by a VICC compiler. The whole description of Alpha assembler is given by a Digital's manual, but it is needed only if you intend to support a debugger, use floating point numbers, etc.

Registers

Alpha has 32 64-bit registers, the last one of which is hard-wired to the value zero.

Name Typical use
v0 ($0) Return values in functions.
t0-t11 ($1-$8,$22-$25) Temporary variables, caller-saved.
s0-s6 ($9-$15) Callee-saved variables.
a0-a5 ($16-$21) Argument passing
ra ($26) Return address of the function
AT ($28 tai $at) Reserved by the assembler
gp ($29 tai $gp) Pointer to global data
sp ($30 tai $sp) Stack pointer
zero ($31) The value zero (0).

Only callee-saved registers preserve their values over function calls. Therefore, if a function uses one of registers s0-s6, it must first store its value to the stack frame and retrieve it from there before it returns.

gp is a pointer to the constant data of the compilation unit (module), and its value can change when performing function calls from one compilation unit to another. More of this later in this text.

In Alpha the register s6 can also serve as a frame pointer, which is mostly used only for debugging. Otherwise it can be freely used for other purposes. The descriptive names v0, t0, t1 etc. can be used if you issue the line

#include <regdef.h>
in the beginning of the assembler file.

The necessary instructions

Below r0, r1, r2 denote (not necessarily different) registers, C denotes a constant, L some symbol (label) and mem[] corresponds to memory.

Instruction Meaning Implementation
lda r0, L r0 = L; Macro
ldiq r0, C r0 = C; Macro
ldq r0, C(r1) r0 = mem[r1 + C]; Instruction
stq r0, C(r1) mem[r1 + C] = r0; Instruction
addq r0, r1, r2 r2 = r0 + r1; Instruction
addq r0, C, r1 r1 = r0 + C; Instruction
s4addq r0, r1, r2 r2 = 4*r0 + r1; Instruction
s4addq r0, C, r1 r1 = 4*r0 + C; Instruction
s8addq r0, r1, r2 r2 = 8*r0 + r1; Instruction
s8addq r0, C, r1 r1 = 8*r0 + C; Instruction
subq r0, r1, r2 r2 = r0 - r1; Instruction
subq r0, C, r1 r1 = r0 - C; Instruction
s4subq r0, r1, r2 r2 = 4*r0 - r1; Instruction
s4subq r0, C, r1 r1 = 4*r0 - C; Instruction
s8subq r0, r1, r2 r2 = 8*r0 - r1; Instruction
s8subq r0, C, r1 r1 = 8*r0 - C; Instruction
absq r0, r1 r1 = (r0 < 0) : -r0 : r0; Instruction
absq C, r0 r0 = (C < 0) : -C : C; Instruction
negq r0, r1 r1 = -r0; Instruction
negq C, r0 r0 = -C; Instruction
mulq r0, r1, r2 r2 = r0 * r1; Instruction
mulq r0, C, r1 r1 = r0 * C Instruction
divq r0, r1, r2 r2 = r0 / r1; Macro
divq r0, C, r1 r1 = r0 / C; Macro
remq r0, r1, r2 r2 = r0 % r1; Macro
remq r0, C, r1 r1 = r0 % C; Macro
sll r0, r1, r2 r2 = r0 << r1; Instruction
sll r0, C, r1 r1 = r0 << C; Instruction
sra r0, r1, r2 r2 = r0 >> r1; Instruction
sra r0, C, r1 r1 = r0 >> C; Instruction
mov r0, r1 r1 = r0; Instruction
cmpeq r0, r1, r2 r2 = (r0 == r1); Instruction
cmpeq r0, C, r1 r1 = (r0 == C); Instruction
cmplt r0, r1, r2 r2 = (r0 < r1); Instruction
cmplt r0, C, r1 r1 = (r0 < C); Instruction
cmple r0, r1, r2 r2 = (r0 <= r1); Instruction
cmple r0, C, r1 r1 = (r0 <= C); Instruction
br L goto L; Instruction
beq r0, L if (r0 == 0) goto L; Instruction
bne r0, L if (r0 != 0) goto L; Instruction
blt r0, L if (r0 < 0) goto L; Instruction
ble r0, L if (r0 <= 0) goto L; Instruction
bgt r0, L if (r0 > 0) goto L; Instruction
bge r0, L if (r0 >= 0) goto L; Instruction
cmoveq r0, r1, r2 if (r0 == 0) r2 = r1; Instruction
cmoveq r0, C, r2 if (r0 == 0) r2 = C; Instruction
cmovne r0, r1, r2 if (r0 != 0) r2 = r1; Instruction
cmovne r0, C, r2 if (r0 != 0) r2 = C; Instruction
cmovlt r0, r1, r2 if (r0 < 0) r2 = r1; Instruction
cmovlt r0, C, r2 if (r0 < 0) r2 = C; Instruction
cmovle r0, r1, r2 if (r0 <= 0) r2 = r1; Instruction
cmovle r0, C, r2 if (r0 <= 0) r2 = C; Instruction
cmovgt r0, r1, r2 if (r0 > 0) r2 = r1; Instruction
cmovgt r0, C, r2 if (r0 > 0) r2 = C; Instruction
cmovge r0, r1, r2 if (r0 >= 0) r2 = r1; Instruction
cmovge r0, C, r2 if (r0 >= 0) r2 = C; Instruction

Alpha can read from memory only from addresses divisible by eight, otherwise it raises an alignment error. The same applies to memory writes.

The instructions addq, subq, negq and mulq can be replaced by addqv, subqv, negqv and mulqv, if you wish to check for overflows.

Macros can correspond to many Alpha instructions. The instructions divq ja remq use registers t9-t12. In reality many other instructions can expand to several instructions, particularly if their constant argument is over eight bits. Such instructions use the register AT, which should not be used by you.

A very eager optimizer might use other instructions as well, for example bit manipulation instructions and the fetch instruction. The full documentation of these is given in the Alpha assembler manual.

Functions and Variables

The implementation of the function fun begins as follows:
        .text
        .align 3
        .globl fun
        .ent fun
fun:
        ldgp gp,0(pv)
fun..ng:

In Alpha the stack (usually) grows downwards. Because all function call arguments in VICC fit in the six argument registers, there should be no need to pass arguments on stack. Therefore it suffices to reserve stack space only for local variables and the spills of bcallee-saved registers and ra. If the function does not call other functions, doesn't use callee-saved registers, and all its variables fit in temporary registers, there's no need to allocate a stack frame at all. If this is not the case, the stack pointer must be changed by the size of the stack frame (e.g. 32 bytes):

        lda sp, -32(sp)

All the callee-saved registers used in the function must be stored in the stack frame:

        stq s0, 8(sp)
        stq s1, 16(sp)

If the function calls other functions, it must store also the current return address in the stack frame:

        stq ra, 0(sp)

When the function wants to call another function, it must first store its own temporary variables (if they contain information valuable after the call) and move the arguments to the argument registers. Suppose the called function is the VICC-function callee, the actual call would then be simply

        bsr callee..ng

Note that VICC does not have a module system, so gp will be preserved. If the callee is a library function (in another compilation unit), the call is

        jsr callee
        ldgp gp, 0(ra)

In addition to temporary registers also the values of the argument registers can change during the call. The return value is in register v0, in which also fun must leave its own return value.

When the function ends, the stack frame must be freed, and the possibly changed ra and callee-saved registers read back:

        ldq ra, 0(sp)
        ldq s0, 8(sp)
        ldq s1, 16(sp)
        lda sp, 32(sp)

Finally the return is

        ret zero,(ra),1
        .end fun

Outside of function definitions you can declare variables and constants. E.g., the string constant foo and array bar : ARRAY [2] OF Int

        .data
foo:
        .ascii "...\0"
        .align 3
bar:
        .comm bar 16

An array as a local variable uses space allocated from the stack.

The hash-mark # comments away the rest of the line. But since the assembly code is run through the C preprocessor, the hash-mark should not be the first character in the line.

Examples

One example of Alpha assembler is an implementation of the individual assignment, the simple translator, which translates directly to (rather suboptimal) Alpha assembler instead of stack machine code. The example can be obtained by anonymous ftp from sauna.cs.hut.fi as the shar-file pub/tik76.149/kotialpha.shr.

Other examples can be obtained in the Alpha assembler manuals or by compiling suitable C-programs with the option -S.

Some machine-specific optimizations