# Functions

• A function in a high-level language can either be recursive or non-recursive.
• A recursive function calls itself (directly or indirectly). E.g.

def fact(n):
if n == 1:
return 1
else:
return n * fact(n-1)
• A non-recursive function does not call itself. E.g.

def fact(n):
factn = 1
for i in range(1, n+1):
factn *= i
return factn

# Non-recursive Functions in SLIM

• The code for every function should be in one chunk, cleanly separated from the rest of the program. It should have only one entry point, but can have multiple exit points.
• A function may call (or invoke) another function. The function that makes the call is the caller and the function that gets called is the callee.
• The caller and callee communicate via a protocol.
1. Before making the call, the caller must load appropriate registers with all the arguments that the callee expects.
2. The caller must let the callee know about the place where the callee must come back to after the callee finishes. This information (the return address) is passed from the caller to the callee in a special register, which we will call the continuation register.
3. After the caller loads all the registers with appropriate values, and loads the continuation register with the return address, it jumps to the entry point of the callee.
4. The callee executes its code. When it finishes, it loads a designated register with the return value if any, then loads the PC with the address in the continuation register and returns.

# Translating a non-recursive function call

• A nonrecursive function call in python like

f(a, b)

gets translated into

    li a <avalue>
li b <bvalue>
li cont afterCall
j f
afterCallL:

# Recursive Function Calls in SLIM

• A recursive function needs to go through the same rigmarole that a non-recursive function does, and then more.
• An extra thing that a recursive caller has to do is that before invoking a recursive callee, the caller must save all the registers that it is using and may be clobbered by the callee in some safe place (i.e., data memory). Also, after returning from the callee, the caller must load back all the values that it has saved prior to invoking the callee.
• In this stack discipline the caller is responsible for saving and retrieving relevant registers. It is also possible that the callee is responsible for this work. In a real system, some registers are designated as caller-saved while others are callee-saved.
• In fact, in every function call (whether recursive or not) we have to make sure the caller saves any register that will be clobbered by the callee but that the caller expects to contain the same value before and after the call. We simply chose to tell you now instead of in the previous slide!

# Translating a recursive function call (caller-saved)

• A recursive function call in python like

<prest>
recurse(n)
<postst>

gets translated into

    <prest>
li n <nvalue>
li cont afterCall
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; statements to save working registers go here
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
j recurse
afterCallL:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; statements to retrieve working registers go here
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
<postst>

if using caller-saved method.

# Translating a recursive function call (callee-saved)

• Exercise. Translate the function call

<prest>
recurse(n)
<postst>

into assembly code assuming the callee-saved method is used.