This preview shows page 1. Sign up to view the full content.
Unformatted text preview: MIPS examples We’ve learned all of the important features of the MIPS instruction set
architecture, so now it’s time for some examples!
— First we’ll see a nested function, which calls another function.
— Next up is a demonstration of recursion.
— Finally we’ll work with some Cstyle strings.
This week’s sections will discuss how structures and objects can be stored
and manipulated in MIPS. You’ll also get some debugging practice, which
will be useful for the assignment that will appear later today.
February 5, 2003 ©20012003 Howard Huang 1 Combinations
Writing a function to compute combinations based on the fact code from
last time will illustrate the implementation of nested functions.
A mathematical definition of combinations is:
n
k n!
=
k! (n—k)! The corresponding C or Java code is shown below.
int comb(int n, int k)
{
return fact(n) / fact(k) / fact(nk);
} Looks easy, right? February 5, 2003 MIPS examples 2 Look before you leap!
int comb(int n, int k)
{
return fact(n) / fact(k) / fact(nk);
} We use MIPS registers to represent the argument and return values.
— Arguments n and k are passed in callersaved registers $a0 and $a1.
— The return value (an integer) is placed in $v0.
The arguments n and k are used multiple times—in particular, after the
function calls fact(n) and fact(k). We’ll have to save the original values of
$a0 and $a1, to ensure they aren’t overwritten by fact.
comb is a nested function, which means it also acts as a callee. We must
also preserve calleesaved registers such as $ra properly.
The return expression involves two divisions and requires one temporary
register in MIPS. We’ll use $s0 for illustrative purposes.
February 5, 2003 MIPS examples 3 Calleesaved registers
To summarize, comb will need to preserve
and restore four registers:
$ra $s0 $a0 $a1 comb is the callee in relation to whoever
calls it (e.g., main).
— Thus, comb is responsible for preserving
the calleesaved registers $ra and $s0,
which it will modify.
— It’s easiest to save them at the beginning
of the function, and to restore them right
before returning. February 5, 2003 MIPS examples comb:
sub
sw
sw $sp, $sp, 8
$ra, 0($sp)
$s0, 4($sp) ... main body ...
lw
lw
addi
jr $ra, 0($sp)
$s0, 4($sp)
$sp, $sp, 8
$ra 4 Callersaved registers
However, comb is the caller in relation to
the fact function.
— This means comb must store the callersaved registers $a0 and $a1 before it
calls fact, and restore them afterwards.
— We’ll allocate two additional words on
the stack for $a0 and $a1. They’ll be
restored as necessary in the function.
Is it possible to preserve $a0 and $a1 in any
of the other registers, instead of the stack? February 5, 2003 MIPS examples comb:
sub
sw
sw
sw
sw $sp,
$ra,
$s0,
$a0,
$a1, $sp, 16
0($sp)
4($sp)
8($sp)
12($sp) ... main body ...
lw
lw
addi
jr $ra, 0($sp)
$s0, 4($sp)
$sp, $sp, 16
$ra 5 The rest of comb
The call fact(n) is easy.
— The value n is also the first argument of
comb, so it is already in $a0.
— The result is saved in register $s0. # fact(n)
jal
fact
move $s0, $v0 For fact(k):
— We have to load k from memory, in case
fact(n) overwrote $a1.
— $s0 is updated with fact(n) / fact(k). # fact(k)
lw
$a0, 12($sp)
jal
fact
div
$s0, $s0, $v0 For fact(nk):
— We have to restore both n and k.
— The final result of comb ends up in $s0. # fact(nk)
lw
$a0, 8($sp)
lw
$a1, 12($sp)
sub
$a0, $a0, $a1
jal
fact
div
$s0, $s0, $v0 February 5, 2003 MIPS examples 6 The whole kit and caboodle
The whole function is shown on the right.
Don’t forget to return the result! We must
move $s0 to $v0 before restoring registers
and returning.
That’s a lot of work for a simple oneline C
function.
Compilers
are good,
mmkay? February 5, 2003 MIPS examples comb:
sub
sw
sw
sw
sw
jal
move
lw
jal
div
lw
lw
sub
jal
div
move
lw
lw
addi
jr $sp,
$ra,
$s0,
$a0,
$a1,
fact
$s0,
$a0,
fact
$s0,
$a0,
$a1,
$a0,
fact
$s0,
$v0,
$ra,
$s0,
$sp,
$ra $sp, 16
0($sp)
4($sp)
8($sp)
12($sp)
$v0
12($sp)
$s0, $v0
8($sp)
12($sp)
$a0, $a1
$s0, $v0
$s0
0($sp)
4($sp)
$sp, 16 7 Famous Fibonacci Function
The Fibonacci sequence is often expressed recursively: fib(n) = 0
1
fib(n1) + fib(n2) if n = 0
if n=1
otherwise This is easy to convert into a C program.
int fib(int n)
{
if (n <= 1)
return n;
else
return fib(n1) + fib(n2);
} The translation to MIPS is not bad if you understood the first example. February 5, 2003 MIPS examples 8 Some observations about fib
This function is similar to comb in some ways.
— fib calls another function (itself), so we will have to save $ra.
— We need to save the argument n across the first recursive call.
— We need a temporary register for the result of the first call.
A recursive function also acts as both caller and callee.
— Calling the same function guarantees that the same registers will be
used, and overwritten if we’re not careful.
— So to be careful, we have to save every register that is used. February 5, 2003 MIPS examples 9 The base case
The base case of the recursion
is easy.
If $a0 is less than 1, then we
just return it. (We won’t worry
about testing for valid inputs
here.)
This part of the code does not
involve any function calls, so
there’s no need to preserve any
registers. int fib(int n)
{
if (n <= 1)
return n;
else
return fib(n1) + fib(n2);
} fib:
bgt
$a0, 1,recurse
move $v0, $a0
jr
$ra February 5, 2003 MIPS examples 10 Doing the recursion
First save $ra and the argument $a0. An
extra word is allocated on the stack to
save the result of fib(n1).
The argument n is already in $a0, so we
can decrement it and then “jal fib” to
implement the fib(n1) call. The result is
put into the stack. recurse:
sub
$sp, $sp, 12
sw
$ra, 0($sp)
sw
$a0, 4($sp)
addi $a0, $a0, 1
jal
fib
sw
$v0, 8($sp) Retrieve n, and then call fib(n2). lw
$a0, 4($sp)
addi $a0, $a0, 2
jal
fib The results are summed and put in $v0. lw
add We only need to restore $ra before
popping our frame and saying byebye.
February 5, 2003 MIPS examples $v1, 8($sp)
$v0, $v0, $v1 lw
$ra, 0($sp)
addi $sp, $sp, 12
jr
$ra
11 The complete fib
fib:
bgt $a0, 1, recurse
move $v0, $a0
jr
$ra
recurse:
sub $sp, $sp, 12
sw
$ra, 0($sp)
sw
$a0, 4($sp)
addi
jal
sw
lw
addi
jal
lw
add $a0,
fib
$v0,
$a0,
$a0,
fib
$v1,
$v0, $a0, 1
8($sp)
4($sp)
$a0, 2
8($sp)
$v0, $v1 lw
$ra, 0($sp)
addi $sp, $sp, 12
jr
$ra
February 5, 2003 MIPS examples 12 Representing strings
A Cstyle string is represented by an array of bytes.
— Elements are onebyte ASCII codes for each character.
— A 0 value marks the end of the array.
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47 space
!
”
#
$
%
&
’
(
)
*
+
,
.
/ February 5, 2003 48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63 0
1
2
3
4
5
6
7
8
9
:
;
<
=
>
? 64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79 @
A
B
C
D
E
F
G
H
I
J
K
L
M
N
O 80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95 P
Q
R
S
T
U
V
W
X
Y
Z
[
\
^
_ MIPS examples 96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111 `
a
b
c
d
e
f
g
h
I
j
k
l
m
n
o 112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127 p
q
r
s
t
u
v
w
x
y
z
{

}
~
del
13 String manipulation
For example, “Harry Potter” can be stored as a 13byte array.
72 97 H a 114 114 121
r r y 32 80
P 111 116 116 101 114
o t t e r 0
\0 We can convert a string to uppercase by manipulating the ASCII values.
— Lowercase letters az have ASCII codes from 97 to 122.
— Uppercase letters AZ range from 65 to 90.
— A lowercase letter can be converted to uppercase by subtracting 32
from the ASCII code (e.g., 97 – 32 = 65). February 5, 2003 MIPS examples 14 Two versions of ToUpper
Both of these loop through a
string, subtracting 32 from
lowercase letters, until they
reach the terminating 0.
The first one accesses letters
by indexing the array str, and
incrementing the index on each
loop iteration.
The second one uses a pointer
that produces a letter when it’s
dereferenced. The pointer is
incremented by one on every
loop iteration. February 5, 2003 void ToUpper(char str)
{
int i = 0;
while (str[i] != 0) {
if (str[i] >= 97 && str[i] <= 122)
str[i] = str[i]  32;
i++;
}
} void ToUpper(char *str)
{
char *s = str;
while (*s != 0) {
if (*s >= 97 && *s <= 122)
*s = *s  32;
s++;
}
} MIPS examples 15 Array version
A direct translation of the array version means each iteration of the loop
must recompute the address of str[i]—requiring two additions.
toupper:
li
$t0,
loop:
add $t1,
lb
$t2,
beq $t2,
blt $t2,
bgt $t2,
sub $t2,
sb
$t2,
next:
addi $t0,
j
loop
exit:
jr
$ra 0 # $t0 = i $t0, $a0
0($t1)
$0, exit
97, next
122, next
$t2, 32
0($t1) #
#
#
#
#
#
# $t0, 1 # i++ $t1 = &str[i]
$t2 = str[i]
$t2 = 0?
$t2 < 97?
$t2 > 122?
convert
and store back Here we work with bytes, but if the array contained integers, this address
computation would require a multiplication as well.
February 5, 2003 MIPS examples 16 Pointer version
Instead, we could use a register to hold the exact address of the current
element. Each time through the loop, we’ll increment this register to
point to the next element.
toupper:
lb
$t2, 0($a0)
beq $t2, $0, exit
blt $t2, 97, next
bgt $t2, 122, next
sub $t2, $t2, 32
sb
$t2, 0($a0)
next:
addi $a0, $a0, 1
j
toupper
exit:
jr
$ra #
#
#
#
#
# $t2 = *s
$t2 = 0?
$t2 < 97?
$t2 > 122?
convert
and store back # s++ With an array of words, we would have to increment the pointer by 4 on
each iteration. But then we would only need one addition, instead of two
additions and a multiplication.
February 5, 2003 MIPS examples 17 Side by side
toupper:
li
$t0,
loop:
add $t1,
lb
$t2,
beq $t2,
blt $t2,
bgt $t2,
sub $t2,
sb
$t2,
next:
addi $t0,
j
loop
exit:
jr
$ra toupper:
0
$t0, $a0
0($t1)
$0, exit
97, next
122, next
$t2, 32
0($t1) lb
beq
blt
bgt
sub
sb
next:
addi
j
exit:
jr $t0, 1 $t2, 0($a0)
$t2, $0, exit
$t2, 97, next
$t2, 122, next
$t2 $t2, 32
$t2, 0($a0)
$a0, $a0, 1
toupper
$ra Another similar example is in Section 3.11 of Hennessy and Patterson. February 5, 2003 MIPS examples 18 Summary
These three examples demonstrate that writing large, modular programs
in assembly language is difficult!
— It’s hard to figure out how to best use the limited number of registers.
(Register allocation is an important problem in writing compilers.)
— We must always follow the MIPS function call conventions regarding
the passing and returning of values and preservation of registers.
— There is only one addressing mode that must be used for all memory
accesses, whether to arguments or stackallocated space.
You’ll get some good programming practice on the machine problem. February 5, 2003 MIPS examples 19 ...
View
Full
Document
 Spring '11
 dg

Click to edit the document details