Presentation is loading. Please wait.

Presentation is loading. Please wait.

Functions in Hmmm Assembly

Similar presentations


Presentation on theme: "Functions in Hmmm Assembly"— Presentation transcript:

1 Functions in Hmmm Assembly

2 Functions in Python vs. assembly
r1 = int(input()) r13 = f(r1) print(r13) def f(r1): r13 = r1*(r1-1) return r13 0 read r1 1 calln r14, 4 2 write r13 3 halt 4 copy r13, r1 5 addn r13, -1 6 mul r13,r1,r13 7 jumpr r14 Write a NEW FUNCTION that returns 1 if the input is > 0 and 2 if the input is <= 0

3 Why Functions? Function is just a block of computation, no real magic. We can use “jumpn” to accomplish the same goal. # computes n*(n-1) without function 0 read r1 jumpn 4 write r13 halt copy r13, r1 addn r13, -1 mul r13,r1,r13 jumpn 2 This program does exactly the same as the function before without function (“calln”). We “hard-coded” the return address “jumpn 2.” But, what if another place in the program needs this part of the computation??? “jumpn 2” will lead to a wrong place! “jumpr r14” (thus function) will be needed!

4 Hmmm function exercises
See the problem set on the course website. Hmmm function exercises

5 We need to "push" (save) everything that we need to protect:
What about recursive functions? We need to "push" (save) everything that we need to protect: x the parameter Every call needs its own copy The return address Calls may occur from different places! So, where do we STORE and LOAD data that we want to protect? x = int(input()) y = fac(x) print(y) def fac(x): if x == 0: return 1 else: res = fac(x-1) return x * res

6 Functions with storer + loadr
Memory ("the stack") fac(3) 3 x, the input x = int(input()) y = fac(x) print(y) def fac(x): if x==0: return 1 else: res = fac(x-1) return x*res ? ret. value main ret. address A "stack frame" stores all of the important data that might get destroyed during a function call (modified by the function called) base case factorial recursive step

7 Functions with storer + loadr
Memory ("the stack") fac(3) 3 x, the input x = int(input()) y = fac(x) print(y) def fac(x): if x==0: return 1 else: res = fac(x-1) return x*res ? ret. value main ret. address THE STACK fac(2) 2 x, the input ? ret. value ret. address base case factorial growing downward Each stack frame stores a potentially different input and return address! recursive step

8 Functions with storer + loadr
Memory ("the stack") fac(3) 3 x, the input x = int(input()) y = fac(x) print(y) def fac(x): if x==0: return 1 else: res = fac(x-1) return x*res ? ret. value main ret. address THE STACK fac(2) 2 x, the input ? ret. value ret. address base case fac(1) 1 x, the input factorial ? ret. value recursive step ret. address growing downward One stack frame per function call

9 Functions with storer + loadr
Memory ("the stack") fac(3) 3 x, the input x = int(input()) y = fac(x) print(y) def fac(x): if x==0: return 1 else: res = fac(x-1) return x*res ? ret. value main ret. address fac(2) 2 x, the input ? ret. value ret. address base case fac(1) 1 x, the input factorial ? ret. value recursive step ret. address fac(0) x, the input 1 Aha! ret. value ret. address

10 Functions with storer + loadr
Memory ("the stack") fac(3) 3 x, the input x = int(input()) y = fac(x) print(y) def fac(x): if x==0: return 1 else: res = fac(x-1) return x*res ? ret. value main ret. address fac(2) 2 x, the input ? ret. value ret. address base case fac(1) 1 x, the input factorial 1 ? ret. value recursive step ret. address this Stack Frame is now gone!

11 Functions with storer + loadr
Memory ("the stack") fac(3) 3 x, the input x = int(input()) y = fac(x) print(y) def fac(x): if x==0: return 1 else: res = fac(x-1) return x*res ? ret. value main ret. address fac(2) 2 x, the input ? ret. value ret. address base case fac(1) 1 x, the input factorial 1 ret. value recursive step ret. address

12 Functions with storer + loadr
Memory ("the stack") fac(3) 3 x, the input x = int(input()) y = fac(x) print(y) def fac(x): if x==0: return 1 else: res = fac(x-1) return x*res ? ret. value main ret. address fac(2) 2 x, the input ? ret. value ret. address base case factorial another Stack Frame is gone 1 recursive step

13 Functions with storer + loadr
Memory ("the stack") fac(3) 3 x, the input x = int(input()) y = fac(x) print(y) def fac(x): if x==0: return 1 else: res = fac(x-1) return x*res ? ret. value main ret. address fac(2) 2 x, the input 2 ret. value ret. address base case factorial recursive step

14 Functions with storer + loadr
Memory ("the stack") fac(3) 3 x, the input x = int(input()) y = fac(x) print(y) def fac(x): if x==0: return 1 else: res = fac(x-1) return x*res ? ret. value main ret. address another Stack Frame is gone base case factorial 2 recursive step

15 Functions with storer + loadr
Memory ("the stack") fac(3) 3 x, the input x = int(input()) y = fac(x) print(y) def fac(x): if x==0: return 1 else: res = fac(x-1) return x*res 6 ret. value main ret. address base case factorial recursive step

16 6 Functions with storer + loadr The Stack is Empty. x = int(input())
y = fac(x) print(y) def fac(x): if x==0: return 1 else: res = fac(x-1) return x*res The Stack is Empty. 6 main 6 base case factorial recursive step

17 storer stores TO memory
Hmmm RAM Hmmm CPU setn r1 42 r0 1 setn r15 70 42 stores r1 into r15's MEMORY LOCATION (not into r15 itself!) r1 2 storer r1 r15 3 write r0 70 r15 They have it in the handout. Have them step through the code. 4 write r1 points to a location in memory 5 halt storer r1 r15 . 70

18 loadr loads FROM memory
Hmmm CPU Hmmm RAM nop r0 1 setn r15 70 r1 loads data into r1 from r15's MEMORY LOCATION (not r15 itself) 2 loadr r1 r15 3 write r0 70 r15 4 write r1 points to a location in memory 5 halt . loadr r1 r15 70 42

19 A function example 0 read r1 # Get the "x" for our function
1 setn r15, # Set the stack pointer, (i.e., # load address of stack into r15) 2 storer r1, r # Store r1, since f overwrites it 3 calln r14, # Call our function f(x) 4 loadr r1, r # Load r1 back in place write r # Print result halt # Stop the program 7 addn r1, # Compute f(x) = x + 1 8 copy r13,r # Save result into r13 9 jumpr r # Finished function, jump back

20 Are there any difference between instructions and values (numbers)?
From computers’ point of view, the memory has separate dedicated area for data and instructions. So the computer knows which piece is data, which piece is instruction. But human beings can’t tell data from instructions just from its form. The program on the previous pages are compiled into machine form in red. 0 : # 0 nop 1 : # 1 setn r15, 70 2 : # 2 loadr r1, r15 3 : # 3 write r0 4 : # 4 write r1 5 : # 5 halt ... 70: # 70: integer 42

21 Jumps in Hmmm Unconditional jump Conditional jumps Indirect jump
jumpn n # jump to line n (set PC to n) Conditional jumps jeqzn rx n # if reg x == 0, jump to line n jnezn rx n # if reg x != 0, jump to line n jltzn rx n # if reg x < 0, jump to line n jgtzn # if reg x > 0, jump to line n Indirect jump jumpr rx # jump to the value in reg x

22 Use jump to repeat work In many computational tasks, we need to repeat certain work. For example, count the number of letter ‘c’ in a string (we’ve done this…) In Python, we have ready structures to accomplish this, we can either do it using recursion, or using a loop (we’ll learn Python loop soon.) Let’s first consider how we do it in Hmmm assembly …

23 What does the following do?
Discuss it with your neighbor(s) … 0 read r1 1 setn r3, 0 2 jeqz r1, 6 3 add r3, r3, r1 4 addn r1, -1 5 jumpn 2 6 write r3 7 halt Compute sum = … + n

24 Functions revisit … Suppose we want to do the summation function many different times. In Python syntax: In Hmmm syntax: def sum(n): if n == 1: return 1 else: return n + sum(n-1) print(sum(10)) print(sum(5)) 0 setn r1, 10 1 calln r14,7 2 write r13 3 setn r1, 5 4 calln r14,7 5 write r13 6 halt # function code … 7 …

25 Functions revisit We put the code for sum(n) we wrote before into use.
The sum program we had before 0 setn r1, 10 1 calln r14,7 2 write r13 3 setn r1, 5 4 calln r14,7 5 write r13 6 halt # function code … 7 … 0 read r1 1 setn r3, 0 2 jeqz r1, 6 3 add r3, r3, r1 4 addn r1, -1 5 jumpn 2 6 write r3 7 halt The code in red can be readily used.

26 Will this work ? The line numbers must be changed!!! Need to change the result register. 0 setn r1, 10 1 calln r14,7 2 write r13 3 setn r1, 5 4 calln r14,7 5 write r13 6 halt # function code … 7 setn r3, 0 2 jeqz r1, 6 3 add r3, r3, r1 4 addn r1, -1 5 jumpn 2 0 setn r1, 10 1 calln r14,7 2 write r13 3 setn r1, 5 4 calln r14,7 5 write r13 6 halt # function code … 7 setn r13, 0 8 jeqz r1, 6 #??? 9 add r13, r13, r1 10 addn r1, -1 11 jumpn 8 Where should we jump to when finished (Line 8)?

27 Where to jump? Where should we jump to when finished (Line 8)?
0 setn r1, 10 1 calln r14,7 2 write r13 3 setn r1, 5 4 calln r14,7 5 write r13 6 halt # function code … 7 setn r13, 0 8 jeqz r1, 12 # end 9 add r13, r13, r1 10 addn r1, -1 11 jumpn 8 12 jumpn # 2 or 5???

28 jumpr The jumpr instruction allows program to jump to an address that is stored in a register, not a fixed number (address). jumpr r14 # jump to the address specified in r14 The value in r14 (or whichever register is used) was set when the function is called with calln.

29 Now we know 0 setn r1, 10 1 calln r14,7 2 write r13 3 setn r1, 5
6 halt # function code … 7 setn r13, 0 8 jeqz r1, 12 # end 9 add r13, r13, r1 10 addn r1, -1 11 jumpn 8 12 jumpr r14 Run the actual program to show the result.

30 What if we want to read (or set) the two numbers, n and m, before calling the function?
0 setn r1, 10 1 setn r1, 5 2 calln r14,7 3 write r13 4 calln r14,7 5 write r13 6 halt # function code … 7 setn r13, 0 8 jeqz r1, 12 # end 9 add r13, r13, r1 10 addn r1, -1 11 jumpn 8 12 jumpr r14 The program obviously will generate wrong result!!!! Last time we saw 55 and 15 were printed. What would be printed now? 15 ?????? We need to save certain values when calling functions!!!

31 How and where to save values?
The answer is “stack”! Essentially, set an area of memory where the values can be stored. Save selected values in that area of memory (stack) when function is called. When function is finished, restore these values from the stack. The reason we call it a “stack” is that it “grows” in one direction, “shrinks” when reversing.

32 Recall the Hmmm architecture
16 registers CPU Registers Random Access Memory (RAM) Control Unit ALU Program Counter Instruction Register Control Processing Unit (CPU) 256 memory units

33 Use a portion of memory for stack
A stack pointer register specifies where in memory is the beginning of the stack. Random Access Memory (RAM) Mem address 255 Stack pointer register, e.g., r15 99 99 r15 2 Which means the stack starts at address 99 1 256 memory units

34 Now let’s go back to the function call
0 setn r1, 10 1 setn r2, 5 2 setn r15,99 # stack pointer 3 calln r14,9 # r14 contains the value 4 after calln 4 write r13 5 copy r1, r2 # function takes r1 as parameter! 6 calln r14,9 # r14 contains the value 7 after calln 7 write r13 8 halt # function code … 9 addn r15, -1 # stack grows downwards # store value of r1 to memory specified by r15 10 storer r1, r15 11 setn r13, 0 # initialize result 12 jeqz r1, 16 # if n == 0, finished 13 add r13, r13, r1 # add n to the sum 14 addn r1, -1 # decrement n by 1 15 jumpn # repeat 16 loadr r1, r15 # restore r1 17 addn r15, 1 # pop stack 18 jumpr r14

35 What if we need to call another function?
Our original program New requirement def sum(n): total = 0 for (i in range(n+1)): total = total + i return total n = 10 m = 5 s = sum(n) print(s) s = sum(m) def sum(n): total = 0 for (i in range(n+1)): total = total + i write_partial(total, i) return total n = 10 m = 5 s = sum(n) print(s) s = sum(m)

36 New requirement def write_partial(i, p): print(i) print(p) def sum(n):
total = 0 for (i in range(n+1)): total = total + i write_partial(i, total) return total n = 10 m = 5 s = sum(n) print(s) s = sum(m)

37 Hmmm version 0 setn r1, 10 1 setn r2, 5 2 setn r15,99 # stack pointer
3 calln r14,9 # r14 contains the value 4 after calln 4 write r13 5 copy r1, r2 # function takes r1 as parameter! 6 calln r14,9 # r14 contains the value 7 after calln 7 write r13 8 halt # function code … 9 addn r15, -1 # stack grows downwards # store value of r1 to memory specified by r15 10 storer r1, r15 11 setn r13, 0 # initialize result 12 jeqz r1, 16 # if n == 0, finished 13 add r13, r13, r1 # add n to the sum 14 calln r14, 22 # call the write_partial function 15 addn r1, -1 # decrement n by 1 16 jumpn # repeat 17 loadr r1, r15 # restore r1 18 addn r15, 1 # pop stack 19 jumpr r14

38 The “write_partial” function
# function write_partial 22 write r1 # first parameter in r1, i 23 write r2 # second parameter in r2, total 24 jumpr r14 # return to the calling function This code looks decent, but we have two issues We didn’t quite set the parameters r1 and r2 When the function returns using jumpr r14 on line 24, where does it jump to?

39 Setting up r1 and r2 # function write_partial 22 write r1 # first parameter in r1, i 23 write r2 # second parameter in r2, total 24 jumpr r14 # return to the calling function This issue is slightly easier to resolve. In real assembly program, you’d have to save all parameters. Here we take a bit “short-cut.” We changed the r2 in main program to be r3 (Line 1), and copy r13 to r2 (Line 13, before calln r14, 22).

40 Setting up r1 and r2 0 setn r1, 10 1 setn r3, 5
2 setn r15,99 # stack pointer 3 calln r14,9 # r14 contains the value 4 after calln 4 write r13 5 copy r1, r3 # function takes r1 as parameter! 6 calln r14,9 # r14 contains the value 7 after calln 7 write r13 8 halt # function code … 9 addn r15, -1 # stack grows downwards # store value of r1 to memory specified by r15 …… 13 add r13, r13, r1 # add n to the sum 14 copy r2, r13 # copy partial total to r2 15 calln r14, 22 # call the write_partial function 16 addn r1, -1 # decrement n by 1

41 Where do we return in write_partial?
The second issue, where do we return after write_partial, is bit more challenge! As it is, what is the value in r14? # function write_partial 22 write r1 # first parameter in r1, i 23 write r2 # second parameter in r2, total 24 jumpr r14 # return to the calling function r14 == 16. The line right after calln r14, 22. This is correct. But when the function sum() completes at Line 19 (Slide 37), what is the value r14? Still 16!!! We’d jump to a wrong place.

42 Save return address! When making a function call, the programmer must save the return address, typically r14 in memory (stack), so the program returns to the correct place. A typical pattern is listed below. Note that it is most likely other parameters have to be saved as well in a similar fashion. n addn r15, # increase stack space n+1 storer r14, r15 # store r14 in mem(r15) n+2 calln r14, x # call function at x n+3 loadr r14, r15 # restore r14 n+4 addn r15, # pop the stack

43 Revised, complete program (1)
0 setn r1, 10 1 setn r3, 5 2 setn r15,99 # stack pointer 3 calln r14,9 # r14 contains the value 4 after calln 4 write r13 5 copy r1, r3 # function takes r1 as parameter! 6 calln r14,9 # r14 contains the value 7 after calln 7 write r13 8 halt # function code … 9 addn r15, -1 # stack grows downwards # store value of r1 to memory specified by r15 10 storer r1, r15 11 setn r13, 0 # initialize result 12 jeqz r1, 24 # if n == 0, finished 13 add r13, r13, r1 # add n to the sum 14 copy r2, r13 # copy the 2nd parameter, first is in r1 15 addn r15, -1 # increment stack pointer 16 storer r14, r15 # save return address 17 calln r14, 30 # call the write_partial function 18 loadr r14, r15 # restore return address 19 addn r15, 1 # pop stack

44 Revised, complete program (2)
20 addn r1, -1 # decrement n by 1 21 jumpn # repeat 22 loadr r1, r15 # restore r1 23 addn r15, 1 # pop stack 24 jumpr r14 25 nop 26 nop 27 nop 28 nop 29 nop # function write_partial 30 write r1 # first parameter in r1, i 31 write r2 # second parameter in r2, total 32 jumpr r14 # return to the calling function Demonstrate the program (nested_func.hmmm). This program has the storer/loadr pair within the loop. This is very inefficient. Try to move the pair outside loop. (Your exercise.)


Download ppt "Functions in Hmmm Assembly"

Similar presentations


Ads by Google