• 沒有找到結果。

We show the longer example of the sort procedure. Figure K.55 shows the C ver-sion of the program. Once again we present this procedure in several steps, con-cluding with a side-by-side comparison to MIPS code.

Register Allocation forsort

The two parameters of the procedure sort, v and n, are found in the stack in loca-tions 4(ap) and 8(ap), respectively. The two local variables are assigned to regis-ters: i to r6 and j to r4. Because the two parameters are referenced frequently in the code, the VMS C compiler copies the address of these parameters into registers upon entering the procedure:

moval r7,8(ap) ;move address of n into r7

moval r5,4(ap) ;move address of v into r5

It would seem that moving the value of the operand to a register would be more useful than its address, but once again we bow to the decision of the VMS C com-piler. Apparently the compiler cannot be sure that v and n don’t overlap in memory.

Code for the Body of the sort Procedure

The procedure body consists of two nested for loops and a call to swap, which includes parameters. Let’s unwrap the code from the outside to the middle.

The Outer Loop

The first translation step is the first for loop:

for (i = 0; i< n; i = i + 1) {

Recall that the C for statement has three parts: initialization, loop test, and iteration increment. It takes just one instruction to initialize i to 0, the first part of the for statement:

clrl r6 ;i = 0

sort (int v[], int n) {

int i, j;

for (i = 0; i < n; i = i + 1) {

for (j = i – 1; j >= 0 && v[j] > v[j + 1]; j = j – 1) { swap(v,j);

} } }

Figure K.55 A C procedure that performs a bubble sort on the array v.

It also takes just one instruction to increment i, the last part of the for:

incl r6 ;i = i + 1

The loop should be exited if i< n is false, or said another way, exit the loop if i n. This test takes two instructions:

for1tst: cmpl r6,(r7) ;compare r6 and memory[r7] (i:n) bgeq exit1 ;go to exit1 if r6 mem[r7] (i  n) Note that cmpl sets the condition codes for use by the conditional branch instruction bgeq.

The bottom of the loop just jumps back to the loop test:

brb for1tst ;branch to test of outer loop exit1:

The skeleton code of the first for loop is then

clrl r6 ;i = 0

for1tst: cmpl r6,(r7) ;compare r6 and memory[r7] (i:n) bgeq exit1 ;go to exit1 if r6 mem[r7] (i  n)

...

(body of first for loop) ...

incl r6 ;i = i + 1

brb for1tst ;branch to test of outer loop exit1:

The Inner Loop The second for loop is

for (j = i– 1; j >= 0 && v[j] > v[j + 1]; j = j – 1) { The initialization portion of this loop is again one instruction:

subl3 r4,r6,#1 ;j = i– 1

The decrement of j is also one instruction:

decl r4 ;j = j– 1

The loop test has two parts. We exit the loop if either condition fails, so the first test must exit the loop if it fails (j< 0):

for2tst:blss exit2 ;go to exit2 if r4< 0 (j < 0) Notice that there is no explicit comparison. The lack of comparison is a benefit of condition codes, with the conditions being set as a side effect of the prior instruc-tion. This branch skips over the second condition test.

The second test exits if v[j]> v[j + 1] is false, or exits if v[j]  v[j + 1].

First we load v and put j + 1 into registers:

movl r3,(r5) ;r3 = Memory[r5] (r3 = v) addl3 r2,r4,#1 ;r2 = r4 + 1 (r2 = j + 1)

Register indirect addressing is used to get the operand pointed to by r5.

Once again the index addressing mode means we can use indices without con-verting to the byte address, so the two instructions for v[j] v[j + 1] are

cmpl (r3)[r4],(r3)[r2] ;v[r4] : v[r2] (v[j]:v[j + 1])

bleq exit2 ;go to exit2 if v[j] v[j + 1]

The bottom of the loop jumps back to the full loop test:

brb for2tst # jump to test of inner loop

Combining the pieces, the second for loop looks like this:

subl3 r4,r6, #1 ;j = i– 1

for2tst: blss exit2 ;go to exit2 if r4< 0 (j < 0) movl r3,(r5) ;r3 = Memory[r5] (r3 = v) addl3 r2,r4,#1 ;r2 = r4 + 1 (r2 = j + 1) cmpl (r3)[r4],(r3)[r2];v[r4] : v[r2]

bleq exit2 ;go to exit2 if v[j] ð [j+1]

...

(body of second for loop) ...

decl r4 ;j = j– 1

brb for2tst ;jump to test of inner loop

exit2:

Notice that the instruction blss (at the top of the loop) is testing the condition codes based on the new value of r4 (j), set either by the subl3 before entering the loop or by the decl at the bottom of the loop.

The Procedure Call

The next step is the body of the second for loop:

swap(v,j);

Calling swap is easy enough:

calls #2,swap

The constant 2 indicates the number of parameters pushed on the stack.

Passing Parameters

The C compiler passes variables on the stack, so we pass the parameters to swap with these two instructions:

pushl (r5) ;first swap parameter is v

pushl r4 ;second swap parameter is j

Register indirect addressing is used to get the operand of the first instruction.

Preserving Registers across Procedure Invocation of sort

The only remaining code is the saving and restoring of registers using the callee save convention. This procedure uses registers r2 through r7, so we add a mask with those bits set:

.word ^m<r2,r3,r4,r5,r6,r7>; set mask for registers 2-7 Since ret will undo all the operations, we just tack it on the end of the procedure.

The Full Procedure sort

Now we put all the pieces together in Figure K.56. To make the code easier to fol-low, once again we identify each block of code with its purpose in the procedure and list the MIPS and VAX code side by side. In this example, 11 lines of the sort procedure in C become the 44 lines in the MIPS assembly language and 20 lines in VAX assembly language. The biggest VAX advantages are in register saving and restoring and indexed addressing.