Professional Documents
Culture Documents
Instruction Set Architecture MIPS PDF
Instruction Set Architecture MIPS PDF
1
What Is ISA?
• Instruction Set Architecture (ISA): Description of the computer by its
software/hardware interface (Blauuw’s definition) without emphasizing
any implementation detail.
• Thus, in this chapter we will not “open” the computer and discuss its
components. Rather we will focus on how to use it for programming
tasks.
• What we need to know are the principles of Von Neumann.
• In later chapters we will look inside the different components one by one,
and see how the ISA is actually implemented.
• Fundamental for understanding any ISA is the instruction set.
• Instruction Set (IS): Defines syntax and semantics of the language of the
computer.
• Actually, as per 7th Von Neumann principle, any instruction should be in
binary format. However, we will use a mnemonic notation, too.
2
Different Languages of the Computer
High-level swap(int v[], int k)
High level language (HHL): most abstract, language {int temp;
omits details, helps concentrate on program
(in C)
temp = v[k];
v[k] = v[k+1];
the problem to be solved, less }
v[k+1] = temp;
5
MIPS Sequential Instructions
• Subtraction is very similar to addition.
• Subtraction:
• Syntax: sub a, b, c
• Semantics: a = b – c;
• Example: f = (g+h) – (i+j);
à Need temporary variables to store g+h and i+j
• Last example, needed temporary variables (i.e. operands) . This leads to
the question, what are temporary variables and variables in general in
MIPS?
• Rule 2: MIPS arithmetic operations accept only register operands.
• A MIPS register is 32 bits long. A MIPS word = 32 bits. Thus, a MIPS
register is 1 word long.
• MIPS has 32 registers (visible to the programmer).
àWhy only 32? Design principle 2 : Smaller is faster
• The preceding rule (Rule 2) says that prior to issuing an arithmetic
operation, we have to make sure that its operands are in some of the
available 32 registers.
6
MIPS Sequential Instructions
• How to designate a register in a MIPS program?
• Use the symbols $s0, $s1, … for program variables.
• Use the symbols $t0, $t1, … for temporary variables.
• Back to last example: f = (g+h) – (i+j)
For example use: f à $s0, g à $s1, h à $s2, i à $s3, j à $s4 and {$t0 $t1}
for temporary variables.
• Suppose your operands are in memory. What do you have to do in order
to add them? The first thing (because of Rule 2) is to transfer them into
registers.
How? à Using data transfer instructions.
• Data transfer instructions:
• Load instruction:
Syntax: lw register mem # lw stands for “load word”
Semantics: register = value stored in memory location mem;
7
MIPS Sequential Instructions
• Data transfer instructions (cont’d):
• Store instruction:
Syntax: sw register mem # sw stands for “store word”
Semantics: memory location mem = value stored in register;
• Thus, mainly we have two data transfer instructions that operate word-
wise.
• We still don’t know how to specify mem in both instructions!
• In both instructions:
• Syntax of mem: constant(register) (where constant = c*4 for some integer c)
• Semantics: specified memory address = constant + value in register.
• Why *4? Because our word size is 32 bits = 4 bytes and memories are
addressable byte-wise (what if we had a different word size?).
• Maybe it is convenient to mention here that MIPS is a big endian
architecture (like SUN machines). What is that? Let word = byte 3 + byte
2 + byte 1 + byte 0 (where byte 3/0 is the most/least significant one).
• Big endian: address(word) = address(byte 3)
• Little endian: address(word) = address(byte 0) (e.g. Intel CPUs)
8
MIPS Sequential Instructions
• Terminology:
• Offset: the constant in mem.
• Base register (or index register): the register between parentheses in mem.
• Rule 3: Alignment restriction
Address(any word in memory) = 0 mod 4. (What are the 2 least significant
bits of word addresses then?)
• Load & Store more precisely:
Load: lw register offset(base) # register = Memory[offset+base]
Store: sw register offset(base) # Memory[offset+base] = register
(Attention: offset = 4*c for some integer c should be done explicitly in MIPS)
• Examples for load & store:
Assume we have the register association (done e.g. by compiler):
hà$s2, base address of array A à $s3, ià$s4
Give the equivalent MIPS code for the following C statements:
a) i = h + A[5];
b) A[10] = h + A[5];
c) A[10] = h + A[i]; 9
MIPS Sequential Instructions
• Register Scheduling/Spilling:
Compilers do their best to use the small number (32) of registers
efficiently. The assignment of (the many) program variables to registers is
called register scheduling. The process of putting less commonly used
variables in memory is called spilling registers (compare memory
hierarchy of Chapter 1).
• Attention:
We have discussed so far two arithmetic instructions and two transfer
instructions (add, sub, lw, and sw). Our discussion however did not use
any binary code! In fact, what we discussed is the representation of these
instructions in assembly code. But don’t worry, there is only a very tiny
step to do in order to understand how the machine actually works with
our Assembly notation. And this is our next topic.
10
MIPS Sequential Instructions
• The alphabet of the computer is {0, 1} (compare: English alphabet is {a,
…, z})
• Anything in memory is a binary “number” be it originally a number,
instruction, address, register, … (that was the stored program concept!).
• Let’s begin with the binary code of some registers in MIPS:
• {$s0, …, $s7} à {16, …, 23}
• {$t0, …, $t7} à {8, …, 15}
• Example:
add $t0, $s1, $s2 will be coded in binary as:
0 17 18 8 0 32
14
MIPS Conditional Instructions
• Like any programming language MIPS includes instructions for decision
making.
• Branch instructions (2 instructions):
• Syntax: beq register1, register2, L1
Semantics: if(register1 == register2) go to L1;
• Syntax: bne register1, register2, L1
Semantics: if(register1 != register2) go to L1;
In both instructions L1 is a label embedded in the code in the form:
L1: Any MIPS instruction
• Example:
Translate the following C code into MIPS assembly language:
if(i == j) go to L1;
f = g + h;
L1: f = f – i;
Answer: Assume {f, …, j} corresponds to {$s0, …, $s4}.
15
MIPS Conditional Instructions
• Obviously, we need for if-then-else statements to skip some part of the
code. And here is the MIPS instruction for doing that.
• Jump instruction:
Syntax: j Label # unconditional jump to Label
Semantics: go to Label;
Now, we are prepared for the next example.
• Example:
How is the following C code fragment translated?
if(i == j) f = g + h; else f = g – h;
Answer:
Similar to last example. You only need to know how to use the Jump
instruction correctly.
16
MIPS Iterative Instructions
• Again, like any programming language MIPS includes looping
instructions.
• However, we do not need any new instruction for loops, we could just use
beq/bne appropriately to construct loops.
• Example:
Translate the following C code into MIPS assembly language:
Loop: g = g + A[i];
i = i + j;
if (i != h) go to Loop;
Partial solution: Assume {g, h, i, j} à {$s1, …, $s4} and
base address of A[] is already in $s5
Loop: add $t1, $s3, ? # $t1 = 2*i
? $t1, ?, ? # $t1 = 4*i (or 2*$t1)
add $t1, ?, ? # $t1 = address of A[i]
lw $t0, ?($t1) # $t0 = A[i]
? $s1, ?, ? # g = g + A[i]
add ?, $s3, ? #i=i+j
? ?, ?, Loop # go to Loop if i != h
17
MIPS Iterative Instructions
• In passing: Code fragments like the preceding that begin with a label and
end with a branch instruction (in other words: that only include simple
sequential instructions) bear the name basic block.
• Now let’s be more realistic and try to translate a while-loop.
• Example:
Translate the following C while-loop into MIPS assembly language:
while (save[i] == k) i = i + j;
Assume: {i, j, k} à {$s3, $s4, $s5}, and base of save[] is in $s6.
Partial solution:
Loop: add $t1, $s3, ? # $t1 = 2*i
? $t1, ?, ? # $t1 = 4*i (or 2*$t1)
add $t1, ?, ? # $t1 = address of save[i]
lw $t0, ?($t1) # $t0 = save[i]
? ?, ?, Exit # go to Exit if save[i] != k
add ?, $s3, ? #i=i+j
??? # go to Loop
Exit:
18
MIPS Iterative Instructions
• For counting loops it’s more convenient to work with the following
instruction.
• Set On Less Than Instruction:
Syntax: slt register1 register2 register3
Semantics: resgister1 = (register2 < resigter3? 1 : 0);
• MIPS compilers use beq, bne, slt, and register $zero whose value is 0
(and which is register # 0) to create any desired condition (e.g. >=, >, …).
• Example: if(a >= b) goto Label;
• Let’s think about how a typical C switch instruction would be
implemented in MIPS assembly language.
• Example:
switch (k) {
case 0: f = i + j; break;
case 1: f = g +h; break;
case 2: f = g - h; break;
case 3: f = i - j; break;
} 19
Switch in MIPS
• Remark: Clearly, we can think of the switch statement as a sequence of
if-the-else statements and solve directly. However, below is a more
elegant/compact solution that uses the following instruction.
• Jump register instruction:
Syntax: jr register
Semantics: goto address contained in register;
(Attention: this jump is unconditional and indirect)
• Solution (for Switch example):
Assume: {f, …, k} à {$s0, …, $s5} and $t2 contains the value 4.
First we make sure that k is in the range [0, 3]:
slt $t3, $s5, $zero # test if k < 0
bne $t3, $zero, Exit # if k < 0 go to Exit
slt ? ? ? # test if k < 4
bne ? ? Exit # if k >= 4 go to Exit
20
Switch in MIPS
• Solution (cont’d):
Now, assume that the four switch cases correspond to labels L0, …, L3
and that we already managed to put these labels in an array (in memory)
with starting address in $t4 (in other words we have: $t4[i] = Li). Hence,
if we can put $t4[k] in some register, say $t0, we can use jr $t0 to jump to
the correct label.
First, we multiply (as usual in MIPS) the index k (=$s5) by 4 to get the
correct byte address:
add $t1, $s5, $s5 # $t1 = 2*$s5
add $t1, $t1, $t1 # $t1 = 4*$s5
Now, we set $t0 = $t4[k]:
add $t1, $t1, $t4 # $t1 = address of $t4[k]
lw $t0, 0($t1) # $t0 = $t4[k]
Now we execute the (indirect) jump:
jr $t0
21
Switch in MIPS
• Solution (cont’d):
The rest is easy, just put a label for each line in the C code and exit:
L0: add $s0, $s3, $s4
j Exit
L1: add $s0, $s1, $s2
j Exit
L2: sub $s0, $s1, $s2
j Exit
L3: sub $s0, $s3, $s4
Exit:
22
Binary Formats of Jump Instructions
• See textbook pp. 131:
23
MIPS Procedure Calls
• Idea of a procedure (function):
Input
Output
Procedure1
(e.g. main) Procedure 2
(e.g. foo)
28
MIPS Procedure Calls
• Solution of fac():
Since we have a nested procedure we need to save arguments/return ads:
fac:
sub $sp, $sp, 8 # adjust stack for 2 items
sw $ra, 4($sp) # push return address $ra on the stack
sw $a0, 0($sp) # push argument on the stack
Check whether or not argument n is < 1:
slt $t0, $a0, 1 # is n < 1?
beq $t0, $zero, L1 # if n >= 1 goto L1
Handle the case where n < 1:
add $v0, $zero, 1 # return 1 ($v0 = 1)
add $sp, $sp, 8 # pop 2 items off the stack
jr $ra # go to caller (address after jal)
Handle the case where n >=1:
L1:
sub $a0, $a0, 1 # n = n-1
jal fac # call myself recursively with n-1 as argument 29
MIPS Procedure Calls
• Solution of fac() (cont’d):
The next instruction will be executed after a return. Thus, we have to restore
the old argument, the old return address, and the stack pointer:
lw $a0, 0($sp) # restore old n
lw $ra, 4($sp) # restore old return address
add $sp, $sp, 8 # adjust stack pointer to pop 2 items
Assuming we have mul instruction for multiplication, we finish with:
mul $v0, $a0, $v0 # return n*fac(n-1)
jr $ra # goto caller again
Local arrays,
structures
(if any)
$sp 31
MIPS Procedure Calls
• MIPS Register Convention:
And:
Register 1 ($at) reserved for the assembler
Registers 26, 27 ($k0, $k1) reserved for the OS
32
MIPS Addressing Modes
• Byte transfer instructions: Example: copying a byte
lb $t0, 0($sp) # LSB($t0) = byte at address 0($sp) (LSB: least significant byte)
sb $t0, 0($gb) # store the read byte back to memory at address 0($gp)
• See textbook pp. 143 for another example (strcpy())
• Immediate operands in MIPS:
• Use of constants (<= 16 bits) in instructions is allowed.
• Example: Variant of add with an immediate operand:
addi $sp, $sp, 4 # $sp = $sp + 4
• Example: Variant of slt with an immediate operand:
slti $t0, $s2, 10 # $t0 = ($s2 < 10? 1: 0);
• Want to work with operands > 16 bits?
• Use load upper immediate instruction (lui)
• Example: lui $t0, 255 # 2MSB($t0) = 255, 2LSB($t0) = 0
# (MSB: most significant byte)
• Example: a = 2^31+2+1 (suppose a à $s0)? (hint: use lui and addi)
33
MIPS Addressing Modes
• Addressing in Jump instructions:
j 10000
op=2 26 bit address=2500
35
MIPS Addressing Modes
• What if the address in a conditional branch does not fit in 16 bits?
à No problem, make use of (unconditional) jump instruction, which offers 26 bits.
à How? (see next example)
• Example for working with target addresses outside the typical range:
beq $s0, $s1, 2^20
add …
is replaced by:
bne $s0, $s1, L
j 2^20
L: add …
• Pseudo-direct addressing (used in j/jal):
Here, the target address is computed as follows:
A = “4 most significant bits of PC” + “26 bits of instruction” + “00”
“+” = concatenation (not addition!)
36
MIPS Addressing Modes
• Summary of MIPS addressing modes:
• Register addressing: ad(operand) = register (number).
• Base or displacement addressing: ad(operand) = offset+register.
• Immediate addressing: operand in instruction itself.
• PC-relative addressing: address = offset + PC.
• Pseudo-direct addressing: address = “4 bits of PC” + “26 bits of
instruction” + “00”.
• Pseudo-instructions:
• Instructions that do not exist at hardware level and only introduced for the
convenience of programming in assembly language.
• It is the task of the assembler to translate them to “real” instruction
(sequences).
• Examples: move $t0, $t1 # $t0 = $t1
(would be translated to: add $t0, $zero, $t1)
Also, different branch variants are accepted: e.g. bgt (>), bge (>=) , …
• Similarly, assemblers do accept in general numbers in decimal,
hexadecimal, … notation. 37
MIPS Simulator on SUN Machines
• Write your code using your favorite editor.
Sample format for your code:
.text
.globl main
main:
<insert here your instruction sequence>
.data:
<insert here your data>
• Issue the following commands:
• cd /usr/local/bin
• xspim
• Last command will start the simulator.
• From xspim:
• Load your file.
• Run it.
• Other commands (single step mode, breakpoints, …) also available.
• You should spend some (long?) time playing with the simulator as the next
assignment will be a programming one. 38
More Programming Examples
• Example: Swap in MIPS
Translate the following C code into MIPS assembly language:
void swap(int v[], int k) { int temp; temp = v[k]; v[k] = v[k+1]; v[k+1] = temp; }
• Solution:
Input: v à $a0, k à $a1
Allocation of stack memory: no, we simply assign temp à $t0
Code for procedure body:
swap: add $t1, $a1, $a1 # $t1 = 2*k
add $t1, $t1, $t1 # $t1 = 4*k
add $t1, $a0, $t1 # $t1 = v + 4*k
lw $t0, 0($t1) # $t0 = v[k]
lw $t2, 4($t1) # $t2 = v[k+1]
sw $t2, 0($t1) # v[k] = $t2
sw $t0, 4($t1) # v[k+1] = $t0
Release of stack memory: No, since no such memory has been used.
Return to caller:
jr $ra 39
More Programming Examples
• Example: Sorting in MIPS
Translate the following C code into MIPS assembly language:
void 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);
}}}
• Solution:
We first seek a pseudo-solution and then think about register allocation and the like.
Code for procedure body:
We have two nested for-loops! Call outer loop for1 and inner loop for2.
Pseudo-code for for2:
1. move j, i-1 # init for2
2. for2: blt j, 0, endfor2 # test condition 1
3. ble v[j], v[j+1], endfor2 # test condition 2
4. jal swap(v, j) # call swap
5. sub j, j ,1 # decrement the j
6. j for2 # loop back to for2
7. endfor2: # we are done with for2 40
More Programming Examples
• Solution (cont’d):
Pseudo-code for for1:
1. move i, 0 # init for1
2. for1: bge i, n, endfor1 # test condition
3. j for2 # do the work
4. add i, i, 1 # increment the i
5. j for1 # loop back to for1
6. endfor1: # we are done with for1
Let’s think now about the register allocation:
We know v à $a0 and n à $a1
Reading the steps of for1 we see:
1. move i, 0: The i should be assigned a saved register, say $s0 (not a temporary one, why?).
And final code should write $zero instead of 0.
2. for1: bge i, n, endfor1: We can use bge (pseudo-instruction) or work with slt/bne. Easier
is to work with pseudo-instructions, so we leave step 2 as it is. But we use $s0 for i and
$a1 (in fact $s3, see later) for n.
3. j for2: Instead of jumping to for2, we better put the code of for2 right here.
4. add i, i, 1: The add becomes here addi, since an immediate operand is used and i à $s0.
5. j for1: Remains unchanged.
6. endfor1: Remains unchanged.
41
More Programming Examples
• Solution (cont’d):
Reading the steps of for2 we see:
1. move j, i-1: We assign $s1 to j and use addi instead of move because we need to
subtract 1 anyway. Thus, the new instruction is: addi $s1, $s0, -1
2. for2: blt j, 0, endfor2: Put here $s1 for j and $zero for 0.
3. ble v[j], v[j+1], endfor2: This is changed to the sequence:
add $s1, $s1, $s1 # j = 2*j
add $s1, $s1, $s1 # j = 4*j
add $t0, $a0, $s1 # $t0 = v + 4*j
lw $t1, 0($t0) # $t1 = v[j]
lw $t2, 4($t0) # $t2 = v[j+1]
ble $t1, $t2, endfor2 # and finally the branch we want
4. jal swap(v, j): We have to prepare the inputs in $a0 and $a1 prior to the call.
Thus, we save our arguments first:
move $s2, $a0 # $s2 = v
move $s3, $a1 # $s3 = n
And prepare the inputs for swap():
move $a0, $s2 # $a0 = v
move $a1, $s1 # $a1 = j
And finally issue the call:
42
jal swap
More Programming Examples
• Solution (cont’d):
5. sub j, j ,1: This is changed to addi $s1, $s1, -1 # or subi
6. j for2: Remains as it is.
7. endfor2: Remains as it is.
What else do we need?
We used $s0 to $s3 in the code. All of them should be pushed on the stack together with
$ra, since we will be calling swap(). Thus:
Beginning of sort should be:
addi $sp, $sp, -20 # allocate stack memory for five registers
sw $ra, 16($sp) # push $ra
sw $s3, 12($sp) # push $s3
sw $s2, 8($sp) # push $s2
sw $s1, 4($sp) # push $s1
sw $s0, 0($sp) # push $s0
43
More Programming Examples
• Solution (cont’d):
End of sort should be:
lw $s0, 0($sp) # pop $s0
lw $s1, 4($sp) # pop $s1
lw $s1, 8($sp) # pop $s2
lw $s2, 12($sp) # pop $s3
lw $ra, 16($sp) # pop $ra
addi $sp, $sp, 20 # release allocated stack memory
And we should not forget to return to the caller:
jr $ra # back to caller of sort()
It only remains to put the pieces together as explained.
• Important:
It is rewarding to first ignore the technicalities of assembly language by providing a
pseudo-solution that is refined in a step-by-step manner until the required solution
is achieved.
44
More Programming Examples
• Working with arrays and pointers:
Next we will have two versions of a procedure clear; one works with array indices and one works
with pointers.
void arrayClear(int array[], int size) {
int i; for(i=0; i < size; i=i+1); array[i] = 0;
}
Pseudo code for body of arrayClear():
1. move i, 0 # init for loop
2. for: bge i, size, endfor # test condition
3. sw 0, 4*i(base(array)) # clear array[i]
4. add i, i, 1 # increment the i
5. j for # loop back to for
6. endfor: # we are done with for
Let’s think now about the register allocation:
We know base(array) à $a0 and size à $a1 and we assign i à $t0.
Reading the steps of for we see:
1. move i, 0 à move $t0, $zero
2. bge i, size, endfor à bge $t0, $a1, endfor
3. sw 0, 4*i(base(array)) à [add $t0, $t0, $t0; add $t0, $t0, $t0; add $t1, $t0, $a0;
sw $zero, 0($t1)] (address calculation must be done inside loop!)
4. add i, i, 1 à addi $t0, $t0, 1
5. j for: Remains unchanged.
6. endfor: Remains unchanged. 45
Now embed code in a procedure called arrayClear (i.e. plus stack allocation, release, and jr)
More Programming Examples
• Working with arrays and pointers (cont’d):
Next procedure is a pointer-based version of arrayClear():.
void pointerClear(int *array, int size) {
int *p; for(p=&array[0]; p < &array[size]; p=p+1); *p = 0;
}
Pseudo code for body of pointerClear():
1. move p, &array[0] # init for-loop
2. for: bge p, &array[size], endfor # test condition
3. sw 0, p # clear p
4. add p, p, 4 # increment by 4, since p points to an integer (=4 bytes)
5. j for # loop back to for
6. endfor: # we are done with for
Let’s think now about the register allocation:
We know base(array) à $a0 and size à $a1 and we assign p à $t0.
Reading the steps of for we see:
1. move p, &array[0] à move $t0, $a0
2. bge p, &array[size], endfor à [add $t1, $a1, $a1; add $t1, $t1, $t1; add $t1, $t1, $a0;
bge $t0, $t1, endfor] (Better: do calculation of &array[size] before the loop; saves 4 instructions/iteration)
3. sw 0, p à sw $zero, 0($t0)
4. add p, p, 4 à addi $t0, $t0, 4
5. j for: Remains unchanged.
6. endfor: Remains unchanged.
Now embed code in a procedure called pointerClear (i.e. plus stack allocation, release, and jr)
46