370 likes | 379 Views
Explore the background, parameter passing methods, array passing, & implementation issues in subroutines. Learn call-by-value, result, reference, name, & more.
E N D
CS 363 – Chapter 9 Subroutines • background • parameter passing • ways of specifying parameters • some implementation issues
Other topics • What happens when we pass arrays to functions? • Passing function as parameter • Variable # of parameters • Generic/template functions • Implementation: static chain & display methods • Cactus
Background • Sub-programs allow for modularity and information hiding. • Called: functions, procedures, methods, subroutines, etc. • Need to communicate information ! • Global data • Parameters to function • Return value(s) from function
Formal parameters are declared with function header Actual parameters (or arguments) appear in call to function Return value main() { int x; x = add(7, 4); } int add(int a, int b) { return a + b; } Terminology
Parameter passing • Call (or pass) by value • Common • Call by result • Good if you want to “return” multiple values • Call by value-result • Actually, there are 2 versions of this • Call by reference • Common • Call by name
What values are in a[1] and a[2] for the following parameter modes ? Value Value-result Reference Name int i, a[10]; main() { a[1] = 1; a[2] = 2; i = 1; fun(a[i]); } void fun(int x) { a[1] = 6; i = 2; x = x + 3; } Example
The value of a[i] gets substituted for x. a[i] = a[1] = 1, so x = 1 fun(): a[1] becomes 6 i becomes 2 x becomes 4, but local so we throw it away. a[2] stays at 2 int i, a[10]; main() { a[1] = 1; a[2] = 2; i = 1; fun(a[i]); } void fun(int x) { a[1] = 6; i = 2; x = x + 3; } Call by value
Value of a[i] (1) passed into x. Parameter is also a return value. fun(): a[1] becomes 6 i becomes 2 x becomes 4 Return value 4 is assigned to parameter a[i]. But what does “a[i]” mean? New binding: a[2]=4 and a[1] stays 6 Old binding: a[1]=4 and a[2] stays 2 Two variants of value-result! Old-binding is more common. int i, a[10]; main() { a[1] = 1; a[2] = 2; i = 1; fun(a[i]); } void fun(int x) { a[1] = 6; i = 2; x = x + 3; } Call by value-result
fun() receives reference to a[i], so fun() is allowed to change it. So every time we see x in fun(), it’s really a[1]. fun(): a[1] becomes 6 i becomes 2 a[1] becomes 9 a[2] stays at 2 int i, a[10]; main() { a[1] = 1; a[2] = 2; i = 1; fun(a[i]); } void fun(int x) { a[1] = 6; i = 2; x = x + 3; } Call by reference
Works like a macro substitution. We pass the string a[i] to fun(). fun(): a[1] becomes 6 i becomes 2 a[i] = a[2] incr by 3, so a[2] = 5 int i, a[10]; main() { a[1] = 1; a[2] = 2; i = 1; fun(a[i]); } void fun(int x) { a[1] = 6; i = 2; x = x + 3; } Call by name
Subroutines • More on parameters • Default • Named vs. positional • Arbitrary number of • Using the stack
Used in C++ and Ada Formal parameters specify which can be default Actual parameters can omit default. Omitted parameter must be at end of actual list! void change(int& value, int mul = 1, int add = 1) { value = value * mul + add; } main() { ... change(x, 2, 3); change(y, 5); change(z); ... Default parameters
Named parameters • Used in PL/I, Fortran 90, Ada • Saves the trouble of getting the order of parameters right. • Makes calls like fun(8, 10, 2, 4) more readable. • From previous example, could write change(x, mul => 2, add => 3); change(add => 3, mul => 2, value => x); • You don’t have to name all parameters, but any positional (i.e. non-named) actual parameters must go first. • Unfortunately, need to know names!
Some PL allow you to write function with arbitrary number of elements! e.g. printf Requires run-time support In Cwhen you write code using variable arguments, need to call pre-defined functions in <stdarg.h> double average(int n, ...) { double total = 0; va_listarg_ptr; va_start(arg_ptr, n); for (i=1; i<=n; ++i) total += va_arg(arg_ptr, double); va_end(arg_ptr); return total/n; } Variable arguments
Run-time stack • Stores activation records of each function while in use (example p. 413) • Need to maintain links (pointers) to other functions on stack • Static link: to access variables in an outer nested scope • Only used in languages with nested procedures • A function may have many static links • Dynamic link: to know where to return
Caller Before calling function, save any registers you think the function may trample Put parameters in registers or on stack. Call function When return, restore registers that you saved. Callee Get parameters Save registers you know you’ll need Adjust stack pointer … do work … Set return value register Restore registers used Adjust stack pointer Calling sequence Assembly code needed to properly call & return from a function…
// set parameters 5 and 7 move $a0, 5 move $a1, 7 // call function call add // copy return value move $t0, $v0 add: // grab parameters move $t0, $a0 move $t1, $a1 // do work add $t2, $t0, $t1 // return value move $v0, $t2 return Example
// set parameters 5 and 7 move $a0, 5 move $a1, 7 // call function call add // copy return value move $s0, $v0 add: // grab parameters move $s0, $a0 move $s1, $a1 st $ra, 0($sp) add $sp, $sp, -4 // do work add $s2, $s0, $s1 // return value move $v0, $s2 add $sp, $sp, 4 ld $ra, 0($sp) return Save return address…
// set parameters 5 and 7 move $a0, 5 move $a1, 7 st $s0, 0($sp) st $s1, -4($sp) add $sp, $sp, -8 // call function call add add $sp, $sp, 8 lw $s1, -4($sp) lw $s0, 0($sp) // copy return value move $s0, $v0 add: // grab parameters move $s0, $a0 move $s1, $a1 st $ra, 0($sp) add $sp, $sp, -4 // do work add $s2, $s0, $s1 // return value move $v0, $s2 add $sp, $sp, 4 ld $ra, 0($sp) return Save registers
Caller or callee save? • Caller save: the caller should protect itself by saving registers before the call • Callee save: a function should be responsible and save values before using registers • Neither? Some registers can be treated as temporary. • Don’t do both! • The basic question is – which registers to save, and who knows the answer. • Does the caller know if the callee is a leaf routine?
In-lining • Some languages allow programmer to give a “significant” comment to compiler. • Compiler may choose to ignore the advice. • Ex. “register” in C • “inline” in Ada and C++ • Tear down the function and expand it like a macro. • Saves a lot of overhead • Useful when function small and used a lot.
Subroutines • Static chain • Function instance graph
Static chain • Activation record needs to keep track of • Local saved information (registers) • Dynamic link: return address • Static link: pointer to function where I’m contained • We only need the static link in languages that support nested procedures. • Why static “chain” ? • When looking for a variable declaration, we may need to go up more than one level.
Variables in a {block} • C, C++ and Java use blocks where we can declare very local variables. • Can treat as a “degerate” function, with its own activation record • More efficient way to store block’s variables: • Since we just “fall in” to blocks, we can allocate space after the other function’s variables • Blocks can share memory, because they are mutually exclusive.
main() { int x, y, z; while(...) { int a, b, c; ... while(...) { int d, e; } } while(...) { f, g; } Stack {block} example
Function instances • Very useful for compiler to know who calls what. • If functions a and b both call c, need to calculate different distances for both cases. • Function instance graph or “call graph” • NOT same thing as static nesting of functions! • Easy to draw by hand – try it! • Start with main • Draw tree one level at a time • Functions called by main become level 1 vertices • Functions called by level 1 become level 2, etc. • Functions at bottom are leaves
Finish chapter 9 (Advanced subroutine stuff) • Templates/generics • Exceptions • Co-routines & concurrency Be familiar with the ideas so you can recognize them, not so much book’s details.
Generic functions • “Generic” a.k.a “template” • A function having a “general” parameter type • Code re-use; good for languages w/o OO. • Featured in Ada, C++, Java • You write a function and… compiler creates multiple versions, one for each instantiated type. • Examples: printing an array; a linked list package. • Java 1.5 extends concept to its collections.
C++ Example template<class T> // announce which params are template void print_array(T *array, int count) { for (int i = 0; i < count; ++i) cout << array[i] << endl; } ----------------------------------------- int a[10]; float f[30]; double x[50]; ... print_array(a, 10); print_array(f, 30); print_array(x, 50);
Generic in Java • You have seen generic classes in Java • Ex. ArrayList<String> • Why is it called generic? • Behind the scenes implementation – all instantiations can share code, because of “Object” type • Primary advantage is we don’t need casts.
Exceptions • Fortran if accumulator overflow labelX, labelY if quotient overflow labelX, labelY Designed to mirror instructions on the IBM 704 • PL/I … the “on” statement • unusual dynamic binding: handle exception if the condition ever arises in the program, not where the ON statement resides • C signals • Can write handler routine to catch signals (e.g. bus error, segmentation fault, kill) when they occur
Exceptions today • Ada, C++, Java use the “try/catch” method you are used to statically bound • Programmer can immediately implement contigency plan in case something goes wrong • Major advantage of handling exceptions – program doesn’t have to die right away • Can treat as normal control flow.
Here’s an example of using exception handling as ordinary control flow. Let’s say we have several files all of the form adr__.txt where the blank is a 2 digit number, and there are only a few files missing at this time. for name = "adr00.txt" to "adr99.txt" { try { open the file } catch (file_not_found) { continue to next filename } // rest of loop Example
Concurrency • Two ways to implement • Threads: have the 2 functions run simultaneously, with different run-time stacks (chapter 12) Ada & Java • Co-Routines: 2 functions just exist at same time, but take turns running Simula & Modula-2 Hint: since the only 2 languages that use co-routines are pretty old, you can tell people have migrated to the thread model.
Co-routines • Instead of “calling” the other function, we have various transfer points. • When we transfer to another function, we don’t necessarily begin at the top of that function! We resume where we left off. • What is difference between co-routine & thread: • Co-routine explicitly says when to switch to other function, giving programmer more control. • Implementation: every co-routine needs to be “detached” from the main run-time stack, because the calls & returns are not linear anymore. (p. 454)