Monday, March 6, 2017 Topics for today C functions and Pep/9 subroutines Translating functions (c) Non-void functions (d) Recursive functions Reverse Engineering: Pep/9 to C C Functions and Pep/9 Subroutines (Section 6.3) Non-void functions We have seen how to translate void functions. What about functions that return a value such as A = min3 (p, q, r) B = exponent (I, N) T = abs (N) Our template for the calling and called environments needs additional steps: The calling environment reserves stack space for the value being returned 1 The called environment (subroutine) needs to put a value in there. In the following table, the three new steps are in italics and highlighted Calling environment (1) allocate stack space for result (2) allocate stack space for actual parameters (3) push actual parameters on stack (4) do CALL (10) deallocate space for parameters (11) use then deallocate returned value Called environment (subroutine) (5) allocate space for local variables (6) do action of the subroutine including.. (7) put return value on stack (8) deallocate space for local variables (9) do RET 1 Note that it would be easy, at the assembly code level, for a subprogram to return more than one value and the values returned could have different types. For example we could allocate 4 bytes for returned values and return an integer and two characters. Comp 162 Notes Page 1 of 11 March 6, 2017
Example 5: non-void function, parameter but no locals The abs function returns a positive number the absolute value of its parameter. Space is left on the stack for the returned value (R.V.) Function C int abs (int i) if (i<0) return -i; else return i; Pep/9 abs: ldwa 2,s ; i brge lab ; i >=0 nega ; now -i in Reg A lab: stwa 4,s ; returning value ret Call /* assume k global */ L = abs(k); subsp 2,i ; for returned value subsp 2,i ; for parameter ldwa k,d stwa 0,s ; parameter to stack call abs addsp 2,i ; finished with parameter ldwa 0,s ; the returned value stwa L,d addsp 2,i ; finished with returned value Maximum Stack R.A k R.V Comp 162 Notes Page 2 of 11 March 6, 2017
Example 6 non-void function with local variable and parameters. This last non-recursive example has something of everything and uses all 11 template steps. Function C int min3 (int A, int B, int C) int smallest; smallest = A; if (B<smallest) smallest = B; if (C<smallest) smallest = C; return smallest; Pep/9 min3: subsp 2,i ; local ldwa 4,s stwa 0,s ; local gets A cpwa 6,s ; compare with B brle skip ; B not smaller ldwa 6,s stwa 0,s ; smallest = B skip: cpwa 8,s ; compare with C brle skip2 ; C not smaller ldwa 8,s skip2:stwa 10,s ; return smallest addsp 2,i ret Call /* assume P,Q,R are global */ read (P,Q,R); W = min3(p,q,r); output(w); deci P,d deci Q,d deci R,d subsp 2,i ; return value subsp 6,i ; parameters ldwa P,d stwa 0,s ldwa Q,d stwa 2,s ldwa R,d stwa 4,s ; all params on stack call min3 addsp 6,i ; parameters go away ldwa 0,s stwa W,d ; return value addsp 2,i ; finished with it too deco W,d Maximum Stack smallest R.A. P Q R R.V. Comp 162 Notes Page 3 of 11 March 6, 2017
Recursion The calling environment for a subprogram can be (a) main program (b) another (non-main) subprogram (c) the subprogram itself. Cases (b) and (c) are really no different from (a). We just need to be particularly careful about managing the stack space. Warford's recursion example (Fig 6.25) is too complex for a first example. We will lead up to it with two simpler recursive functions: Example 7: a void function to print N stars Example 8: an int function to compute the sum of the first N integers. Example 7 recursive, void function: pstars Function C void pstars(int N) if (N>0) output('*'); pstars(n-1); Pep/9 pstars: ldwa 2,s ; parameter breq skip ; branch if zero ldbx '*',i stbx charout,d subsp 2,i ; param of recursion suba 1,i ; N-1 stwa 0,s ; is the parameter call pstars ; to print N-1 stars addsp 2,i ; get rid of parameter skip: ret Call read(count); pstars(count); deci count,d subsp 2,i ldwa count,d stwa 0,s call pstars addsp 2,i ; for parameter ; finished with parameter The two highlighted sections are the call of pstars from the main program and call of pstars from within pstars itself. Note the similarities. A trace of this program shows how the stack grows (proportional to N) until the recursion unwinds. If N is large enough, the Pep/9 stack might crash into the program instructions at the top of memory. The program is about 44 bytes. The Application Program area in the Pep/9 Comp 162 Notes Page 4 of 11 March 6, 2017
memory is 64399 bytes (see Chapter 4). The stack grows by 4 bytes at each recursive call so the largest value of N we could have without the stack running into the program is ( 64399 44 ) / 4 = 16088 Here is a modified version of pstars. It has 1000 bytes of (unused) local variables so the stack will crash into the program for smaller values of N. ( 64399 44 ) / 1004 = 64 pstars: subsp 1000,i ldwa 1002,s breq skip charo '*',i subsp 2,i suba 1,i stwa 0,s call pstars addsp 2,i skip: addsp 1000,i ret Here is the calling program call main stop count:.block 2 mess:.ascii "\ndone!\n\x00" main: deci count,d subsp 2,i ldwa count,d stwa 0,s call pstars addsp 2,i stro mess,d ret Here are some results of running the program the last one crashes. 10 ********** Done! 30 ****************************** Done! 60 ************************************************************ Done! 70 Comp 162 Notes Page 5 of 11 March 6, 2017
************************************************ Example 8 recursive int function to find sum of first N integers Function C int sum (int N) if (N==0) return 0; else return N+sum(N-1); Pep/9 sum: ldwa 2,s ; parameter breq zerocase subsp 2,i ; space for result of recursion subsp 2,i ; space for param of recursion ldwa 6,s ; which is N suba 1,i ; minus 1 stwa 0,s ; onto the stack call sum ; compute sum(n-1) addsp 2,i ; get rid of parameter space ldwa 0,s ; here is result of recursion addsp 2,i ; get rid of that space adda 2,s ; adding N to sum(n-1) zerocase: stwa 4,s ; return result ret Call read(k); M = sum(k); deci k,d subsp 2,i ; for returned value subsp 2,i ; for parameter ldwa k,d stwa 0,s ; parameter k call sum addsp 2,i ; finished with parameter ldwa 0,s ; result of call stwa m,d addsp 2,i ; finished with result The stack growth in this case is faster than in Example 7 (6 bytes rather than 4 each time) because we are also leaving space for a return value. Recursion and iteration are equivalent - anything you can do one way you can do the other. Sometimes an algorithm is easier to devise one way then the other. Recursion has some advantages: a recursive algorithm may be shorter and more obviously correct than the equivalent iterative or a closer match to a data structure (recursive list and tree algorithms for example). However, there are time and space overheads. The following table shows the number of Pep/9 instructions executed to find the sum of the first N integers using the recursive subroutine above and an equivalent iterative one. Comp 162 Notes Page 6 of 11 March 6, 2017
N Recursive Iterative 10 157 56 100 1417 416 160 2257 656 Increasing N by 1 requires an additional 14 instructions in the recursive version and only an additional 4 in the iterative version. Now we are ready to tackle the recursive example in the book. Figure 6.25 shows a doublyrecursive integer function, i.e., in some cases the function calls itself twice to determine the value to return. Note the checks for the non-recursive cases and the two calls in the recursive part of the function. Reverse Engineering: Pep/9 to C If we have a Pep/9 subroutine created using our checklists, we can usually do a translation back into C. Determining how the stack entries are used is important. In routines that follow our checklists, the stack regions are Local Variables Return Address (2 bytes) Parameters Space for returned value(s) Comp 162 Notes Page 7 of 11 March 6, 2017
Here is our first example. Line 1 mystery: subsp 2,i 2 ldwa 4,s 3 stwa 0,s 4 top: breq done 5 ldbx 6,s 6 stbx charout,d 7 ldwa 0,s 8 suba 1,i 9 stwa 0,s 10 br top 11 done: addsp 2,i 12 ret Our construction of the corresponding C function begins with a skeleton form using the name?? mystery ( ) We see (Line 1) that 2 bytes are allocated for local variables (these bytes are deallocated on line 11). References to this part of the stack (Lines 3, 7 and 9) use word operations so we assume this is a single integer variable rather than two character variables. Thus we have?? mystery () int temp; The return address is at 2,s so references to locations further down the stack than that are to parameters and return value space. The subroutine does not write to any of these locations so does not return a value so is a void function. void mystery () int temp; The parameter at 4,s is accessed using a word instruction (Line 2) so occupies 4,s and 5,s. The parameter at 6,s is accessed using a byte operation (Line 5). We can fill in the parameter types. void mystery (int A; char B) int temp; The body of the subroutine is an assignment of the parameter value to the local variable (Lines 2 and 3) followed by a loop (Lines 4-10). Comp 162 Notes Page 8 of 11 March 6, 2017
void mystery (int A; char B) int temp; temp = A; while ( ) All that remains is to fill out the condition and loop body void mystery (int A; char B) int temp; temp = A; while (temp!= 0 ) printf( %c,b); temp--; Thus a call of mystery(12, t ) will print 12 t s. Reading There is very little discussion of simple non-void functions in the text. Comp 162 Notes Page 9 of 11 March 6, 2017
Review Questions 1. We know that in the Fibonacci sequence, most terms are the sum of the two previous terms so why can t we define fib(n) as fib(n): return fib(n-1)+fib(n-2) 2. How can a Pep/9 non-void subroutine return, for example, two integers as its result? 3. When we are reverse-engineering a subroutine, how can we distinguish between the translations of int X (int, int, int) and void X (int,int,int,int) Both will have 8 bytes of space reserved by the calling environment. 4. A student claims that the following prints N in binary for non-negative N. Is she right? Printbin(N): if n=1 print ( 1 ) Else if odd(n) printbin(n/2); print ( 1 ) Else printbin(n/2); print ( 0 ) 5. Why is a recursive version of the Fibonacci function inefficient for large N? Comp 162 Notes Page 10 of 11 March 6, 2017
Review Answers 1. There is no non-recursive or base case. We need something like Fib(N): if N<3 return 1 else return fib(n-1)+fib(n-2) 2. The calling environment leaves room for 4 bytes of result on the stack and the subroutine puts two integers in those locations. 3. The former will have a word that is only written to, never read from, this represents the returned value. 4. Almost. If N is zero, there is infinite recursion and nothing printed 5. It makes repeated calls with the same parameter. Comp 162 Notes Page 11 of 11 March 6, 2017