Day 17
I. Last Time:
Hw #5 Posted
Lab #1 Due NOW!
Lab #2 Posted: Early Bird Special:
+1% Extra for each weekday early up to +5%
In My Mailbox Wed Morn: +1
Tues +2, Mon +3 Fri +4 Thurs +5
A. Functions in MIPS
Functions - What is a function/subroutine?
A "self contained" set of instructions that does some work
It normally requires some input (arguments) and produces
some output (return value).
Why functions?
Code Re-use
Complexity reduction
1. MIPS function calls:
Much like syscalls:
a. Setup-Arguments
Save and "temp" data that the function may trounce!
b. "Call" the function
c. Use/store the return value
Arguments: The a0-a3 registers
(What if we need more?)
"Call": jal Jump AND Link for calling a function and
saving the way back to the caller.
Go to a specific address AND save the current
IP/PC position so we can get back
(leave a trail of bread crumbs)
II. New Stuff:
A. Short Circuit Evaluation:
C does what is called "short circuit" evaluation.
I.e. it only does the minimum work nesc.
If something being ANDed is a 0 (false) the result
must be false, so stop checking.
If something being ORed is a !0 (true) the result
must be true, so stop checking.
if(a!=0 && b!=0 && c!=0)
{
}
beqz a, after
beqz b, after
beqz c, after
...
after:
if(a!=0 || b!=0 || c!=0)
{
}
bnez a, into
bnez b, into
beqz c, after
into:
...
after:
...
Although this can be confusing, it can also be helpful:
if(a && test(a))
{
...
}
The test function might be called but it might not.
if(a && a->test())
{
...
}
This can be good: if a is a valid pointer, the test function
will be called. if it's NOT, the function won't be called.
(Which would've resulted in an error)
P.S. this isn't considered good "style" by many -
that's a matter of personal preference.
B. Back to Fact()
fact(5) = 5*fact(4) - 4*fact(3) - 3*fact(2) - 2*fact(1) - 1
- 2*1=2
3*2=6
4*6=24
5*24=120
Details of assembly Language:
a single set of assembly instructions is being
executed repreatedly?
where/how does it return to the caller? WHICH Caller?
how does it keep track/use local variables/registers?
C. The Stack - Like a stack in C/C++, but simplier.
The Stack is a Stack of Function FRAMES.
Whenever a function is called, it creates a FRAME for itself.
The Frame contains all the local information to the function -
things like local variables - both explicit (the ones you know about)
and some implicit (the compiler creates for you - Things like return
values and in some cases arguments to functions)
The Stack Operations: Push, Pop, "Peek"
Push/Pop - Stack Frames are being pushed/poped off the stack.
Peek - Whenver a function is running it may need to use the
space it has set aside for itself (I.e. peek at it's
data)
A stack is made of bytes
Must be allocated in double word increments (multiple of 8)
Must be used in a very consistent way to be complatible with C/C++
(C will pass arguments and expect arguments to be passed in a very
rigid format - we will be using this format)
Grows DOWN in memory. I.e. to allocate space, subtract from $sp
It's a pointer, so use unsigned instructions (techincally)
The Stack:
Why a stack?
To support recursion, more efficient use/re-use of space
(localy scoped variables)
Think:
If every variable in every function had to actually
have space set aside for it in memory at the same time
VERY inefficient - instead re-use parts of memory only
for the currently executing function...
And ONLY ONE function can really run at a time...
Implications:
Local variables of functions are NOT implicitly
initialized.
Limited Recursion - Stack Overflow
Alternatives:
No Runtime stack - no recursion, static variables only
Very Small stack - limited recursion
Both are common in embedded applications
1. Stack Frames/Activation Records
When a function is called it creates a "frame" or activation
record
Two purposes:
1. Space for local variables
2. Space for any called functions to store caller's info.
(Arguments and large return values typically)
2. Special Stuff:
jal - Jump AND Link for calling a function and
saving the way back to the caller.
Go to a specific address AND save the current
IP/PC position so we can get back
(leave a trail of bread crumbs)
jr - Return to the address provided in a specific reg
$sp - the "stack pointer" tells where the top of the stack is.
3. What's Stored:
Arguments, Local Variables and Return Points
(Args to other functions as well)
Stores "local" scopped variables for a specific instance
of a function. I.e. a specific call. This info is called
the "frame" or activation record. and it is accessed via
the stack pointer ($sp)
Advantages: Re-uses "space" - more efficient
Potential Problems - Limited size - stack overflow
Stack variables are not implicitly
initialized.
D. MIPS stack conventions:
What's in the Frame:
1. Space for callee argument (used for large arguments)
2. Space to backup registers (in numerical order)
3. "Local" Variables/space
Misc. Rules:
If a "leaf" function (doesn't call other functions):
Stack space may not be needed unless more than the 10 t-regs
are required
If a "non-leaf" function (call's something else)
Must allocate at least 16 bytes for callee's a0-a3.
Must allocate at least 4 bytes for ra
All frames size should be a multiple of 8
A function:
1. Function setup (Pushing)
a. Sets aside "frame" space
b. Saves any registers that it's going to change
2. Performs computation / calls other functions / etc.
(sets up return value)
3. Prepares to return to caller (Poping)
a. Restores changed registers
b. releases frame
c. returns to caller (jr $ra)
III. Everything you ever wanted to know about Fibannoci,
but were afraid to ask...
DETAILED, SLOW Fibannoci example.
If you pay attention/understand, the next Hw will be a breeze
(take ~1hr) if NOT, it will take a lot longer)
A. Definition:
Note: This problem is 800 years old - proposed by Fibonacci
in 1202.
Suppose that you have a pair of rabbits - every month the
rabbits breed and have a pair of offspring, who will begin
the breeding process themselves in two months.
Month 1 - 1 Pair - New Born (NB)
Month 2 - 1 Pair - Ready to Breed (RTB)
Month 3 - 2 Pair (1 breeding (B), 1 NB)
Month 4 - 3 Pair (1 B, 1 RTB, 1 NB)
Month 5 - 5 Pair (2 B, 1 RTB, 2 NB)
Month 6 - 8 Pair (3 B, 2 RTB, 3 NB)
Notice that each term is the sum of the 2 proceeding.
(I.e. month 6 = month 5+month 4)
As a function:
F(0)=1
F(1)=1
F(N)=F(N-1)+F(N-2)
Notice that this is a function with 2 base cases,
and a single "recursive" case which recurses in
2 "directions".
NOTE: Solving this with recursion is (in my opinion),
the WORST possible way to solve the problem.
(Closed Form is best, Iteration second best)
We do this merly to gain experience with the
stack.
B. Solution - in C++:
unsigned Fib(unsigned n)
{
if(n<=1) // Neat observation based on seq
return n; // Base Case
else
return Fib(n-1)+Fib(n-2);
}
Notes on C++ Conversion:
n will be in $a0
We may need temp places to store Fib(n-1) & Fib(n-2)
($t0, $t1)
C. Solution in ASM:
All ASM routines have 3 parts -
a begining, a middle, and an end
fib:
# Allocate Stack Space
subu $sp,$sp,32 # (let's try 32)
# Store Registers that may be changed by called functions:
sw $a0,32($sp)
sw $ra,28($sp)
bgt $a0,1,fib_recurse # Check Base Case
move $v0,$a0 # Put return value in ret val reg
j fib_end
fib_recurse: # Recursive Case
addiu $a0,$a0,-1 # Setup for fib(n-1)
jal fib # call fib(n-1)
sw $v0,24($sp) # deal with return value
addiu $a0,$a0,-1 # setup for fib(n-2)
jal fib # call fib(n-2)
lw $t0,24($sp) # retreive fib(n-1)
add $v0,$v0,$t0 # v0=fib(n-1)+fib(n-2)
fib_end: # Clean-up/return to caller
lw $ra, 28($sp) # Restore ra for caller
lw $a0, 32($sp) # Restore a0
addu $sp,$sp,32 # Restore stack
jr $ra # return to caller
D. A sample Run:
Memory Address Instruction(s)
-------------- -------------------------------------------
[0x00400020] subu $sp,$sp,32 # (let's try 32)
[0x00400024] sw $a0,32($sp)
[0x00400028] sw $ra,28($sp)
[0x0040002c] bgt $a0,1,fib_recurse
[0x00400030] (beq $1, $0, 12 [fib_recurse-0x00400030])
[0x00400034] move $v0,$a0
[0x00400038] j fib_end
[0x0040003c] addiu $a0,$a0,-1 # Setup for fib(n-1)
[0x00400040] jal fib
[0x00400044] sw $v0,24($sp)
[0x00400048] addiu $a0,$a0,-1 # setup for fib(n-2)
[0x0040004c] jal fib
[0x00400050] lw $t0,24($sp)
[0x00400054] add $v0,$v0,$t0
[0x00400058] lw $ra, 28($sp)
[0x0040005c] lw $a0, 32($sp)
[0x00400060] addu $sp,$sp,32
[0x00400064] jr $ra
What happens during fib(1)?
fib(2)?
fib(5)?
E. Summary:
Stack is used to implement locally scoped variables and to
"re-use" memory for local variables so we don't have to
waste space for variables not currently in use.
Stack allows recursion.
Some accounting info. must be stored on the stack: args & ra.
VI. Next Time:
A. More Mips Memory and Functions
B. MIPS memory segmentation
C. Using SPIM