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.
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.
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.
.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.
Other examples can be obtained in the Alpha assembler manuals or by compiling suitable C-programs with the option -S.