• 沒有找到結果。

An Example to Put It All Together: swap

在文檔中 Survey of Instruction (頁 73-77)

To see programming in VAX assembly language, we translate two C procedures, swap and sort. The C code for swap is reproduced in Figure K.60. The next sec-tion covers sort.

We describe the swap procedure in three general steps of assembly language programming:

1. Allocate registers to program variables.

2. Produce code for the body of the procedure.

3. Preserve registers across the procedure invocation.

The VAX code for these procedures is based on code produced by the VMS C compiler using optimization.

Register Allocation for swap

In contrast to MIPS, VAX parameters are normally allocated to memory, so this step of assembly language programming is more properly called “variable alloca-tion.” The standard VAX convention on parameter passing is to use the stack. The two parameters, v[] and k, can be accessed using register ap, the argument pointer:

The address 4(ap) corresponds to v[] and 8(ap) corresponds to k. Remember that with byte addressing the address of sequential 4-byte words differs by 4. The only other variable is temp, which we associate with register r3.

Code for the Body of the Procedure swap

The remaining lines of C code in swap are temp = v[k];

v[k] = v[k + 1];

v[k + 1] = temp;

Since this program uses v[] and k several times, to make the programs run faster the VAX compiler first moves both parameters into registers:

movl r2, 4(ap) ;r2 = v[]

movl r1, 8(ap) ;r1 = k

Instruction type Example Instruction meaning

Data transfers Move data between byte, half-word, word, or double-word operands; * is data type

mov* Move between two operands

movzb* Move a byte to a half word or word, extending it with zeros mova* Move the 32-bit address of an operand; data type is last

push* Push operand onto stack

Arithmetic/logical Operations on integer or logical bytes, half words (16 bits), words (32 bits); * is data type

add*_ Add with 2 or 3 operands cmp* Compare and set condition codes tst* Compare to zero and set condition codes

ash* Arithmetic shift

clr* Clear

cvtb* Sign-extend byte to size of data type Control Conditional and unconditional branches

beql, bneq Branch equal, branch not equal

bleq, bgeq Branch less than or equal, branch greater than or equal brb, brw Unconditional branch with an 8-bit or 16-bit address jmp Jump using any addressing mode to specify target aobleq Add one to operand; branch if resultsecond operand case_ Jump based on case selector

Procedure Call/return from procedure

calls Call procedure with arguments on stack (see “A Longer Example: sort” on page K-76)

callg Call procedure with FORTRAN-style parameter list jsb Jump to subroutine, saving return address (like MIPS jal)

ret Return from procedure call

Floating point Floating-point operations on D, F, G, and H formats

addd_ Add double-precision D-format floating numbers subd_ Subtract double-precision D-format floating numbers mulf_ Multiply single-precision F-format floating point

polyf Evaluate a polynomial using table of coefficients in F format

Other Special operations

crc Calculate cyclic redundancy check insque Insert a queue entry into a queue

Figure K.59 Classes of VAX instructions with examples. The asterisk stands for multiple data types: b, w, l, d, f, g, h, and q. The underline, as in addd_, means there are 2-operand (addd2) and 3-operand (addd3) forms of this instruction.

Note that we follow the VAX convention of using a semicolon to start a com-ment; the MIPS comment symbol # represents a constant operand in VAX assem-bly language.

The VAX has indexed addressing, so we can use index k without converting it to a byte address. The VAX code is then straightforward:

movl r3, (r2)[r1] ;r3 (temp) = v[k]

addl3 r0, #1,8(ap) ;r0 = k + 1

movl (r2)[r1],(r2)[r0] ;v[k] = v[r0] (v[k + 1])

movl (r2)[r0],r3 ;v[k + 1] = r3 (temp)

Unlike the MIPS code, which is basically two loads and two stores, the key VAX code is one memory-to-register move, one memory-to-memory move, and one register-to-memory move. Note that the addl3 instruction shows the flexibility of the VAX addressing modes: It adds the constant 1 to a memory operand and places the result in a register.

Now we have allocated storage and written the code to perform the operations of the procedure. The only missing item is the code that preserves registers across the routine that calls swap.

Preserving Registers across Procedure Invocation of swap

The VAX has a pair of instructions that preserve registers, calls and ret. This example shows how they work.

The VAX C compiler uses a form of callee convention. Examining the code above, we see that the values in registers r0, r1, r2, and r3 must be saved so that they can later be restored. The calls instruction expects a 16-bit mask at the beginning of the procedure to determine which registers are saved: if bit i is set in the mask, then register i is saved on the stack by the calls instruction. In addi-tion, calls saves this mask on the stack to allow the return instruction (ret) to restore the proper registers. Thus, the calls executed by the caller does the sav-ing, but the callee sets the call mask to indicate what should be saved.

One of the operands for calls gives the number of parameters being passed, so that calls can adjust the pointers associated with the stack: the argument swap(int v[], int k)

{

int temp;

temp = v[k];

v[k] = v[k + 1];

v[k + 1] = temp;

}

Figure K.60 A C procedure that swaps two locations in memory. This procedure will be used in the sorting example in the next section.

pointer (ap), frame pointer (fp), and stack pointer (sp). Of course, calls also saves the program counter so that the procedure can return!

Thus, to preserve these four registers for swap, we just add the mask at the beginning of the procedure, letting the calls instruction in the caller do all the work:

.word ^m<r0,r1,r2,r3> ;set bits in mask for 0,1,2,3

This directive tells the assembler to place a 16-bit constant with the proper bits set to save registers r0 through r3.

The return instruction undoes the work of calls. When finished, ret sets the stack pointer from the current frame pointer to pop everything calls placed on the stack. Along the way, it restores the register values saved by calls, including those marked by the mask and old values of the fp, ap, and pc.

To complete the procedure swap, we just add one instruction:

ret ;restore registers and return

The Full Procedure swap

We are now ready for the whole routine. Figure K.61 identifies each block of code with its purpose in the procedure, with the MIPS code on the left and the VAX code on the right. This example shows the advantage of the scaled indexed

MIPS versus VAX Saving register swap: addi $29,$29, –12

sw $2, 0($29)

sw $15, 4($29)

sw $16, 8($29)

swap: .word ^m<r0,r1,r2,r3>

Procedure body muli $2, $5,4

add $2, $4,$2 lw $15, 0($2) lw $16, 4($2) sw $16, 0($2) sw $15, 4($2)

movl r2, 4(a) movl r1, 8(a) movl r3, (r2)[r1]

addl3 r0, #1,8(ap) movl (r2)[r1],(r2)[r0]

movl (r2)[r0],r3 Restoring registers

lw $2, 0($29)

lw $15, 4($29)

lw $16, 8($29)

addi $29,$29, 12

Procedure return

jr $31 ret

Figure K.61 MIPS versus VAX assembly code of the procedure swap in Figure K.60 on page K-74.

addressing and the sophisticated call and return instructions of the VAX in reduc-ing the number of lines of code. The 17 lines of MIPS assembly code became 8 lines of VAX assembly code. It also shows that passing parameters in memory results in extra memory accesses.

Keep in mind that the number of instructions executed is not the same as per-formance; the fallacy on page K-81 makes this point.

Note that VAX software follows a convention of treating registers r0 and r1 as temporaries that are not saved across a procedure call, so the VMS C compiler does include registers r0 and r1 in the register saving mask. Also, the C compiler should have used r1 instead of 8(ap) in the addl3 instruction; such examples inspire computer architects to try to write compilers!

在文檔中 Survey of Instruction (頁 73-77)