This is a problem set question.
Lecture_notes/1.3_Functions/3_-_Function_calling_conventions.pdf Calling Conventions passing arguments to functions • Terminology: • Callee has parameters that are passed in to it (also called formal parameters) • Caller passes arguments to callee (also called actual parameters) • But this transfer of data between caller and callee can take many forms! int main() { ... p (a, b) ... } int p(int x, int y) { return x * y; } different kinds of parameters • Value parameters (call-by-value) • Reference parameters (call-by-reference) value parameters • “Call-by-value” • Used in C, Java, default in C++ • Passes the value of an argument to the function • Makes a copy of argument when function is called • Advantages? Disadvantages? value parameters int x = 1; void main () { foo(x, x); print(x); } void foo(int y, int z) { y = 2; z = 3; print(x); } • What do the print statements print? • Answer: print(x); //prints 1 print(x); //prints 1 reference parameters • “Call-by-reference” • Optional in Pascal (use “var” keyword) and C++ (use “&”) • Pass the address of the argument to the function • If an argument is an expression, evaluate it, place it in memory and then pass the address of the memory location • Advantages? Disadvantages? reference parameters int x = 1; void main () { foo(x, x); print(x); } void foo(int &y, int &z) { y = 2; z = 3; print(x); print(y); } • What do the print statements print? • Answer: print(x); //prints 3 print(x); //prints 3 print(y); //prints 3! how to pass values • Pass arguments in registers (value, if call by value; address of data if call by reference) • Advantage: fast, no memory operations to retrieve values • Disadvantage: limited space, need to be careful for more complicated data, uses more registers • Pass arguments on the stack (through memory) • Advantage: unlimited space for passing arguments, saves registers for other use • Disadvantage: requires more memory, adds instruction overhead • Architectures with lots of registers (e.g., RISC-V) prefer to pass arguments in registers, but all architectures default to stack if needed • In project, we will pass arguments on the stack to simplify code generation; passing in registers is a good optimization! next: tracking function symbols Lecture_notes/1.3_Functions/4_-_Functions_in_symbol_tables.pdf Symbols for Functions why do functions need to be in symbol tables? • Functions are symbols, so tracking them is important! • Avoid name conflicts (different functions with the same name) • This interacts in a funny way with function overloading • Keep track of the arguments and return information about a function • To make sure that functions are called properly • This also interacts in a funny way with function overloading • Keep track of the names of parameters to a function • To make sure they are accessed correctly during code generation functions are symbols and scopes • Functions also have their own scope! • Local variables in functions are in a different scope than local variables in other functions or global variables • Variable names can be reused • In global scope: need to track memory address of variables • In local scope: need to track stack offset of variables • Remember, local variables are stored on the stack, accessed relative to stack/frame pointers int foo(int x, int y) { int a; int b; ... } Name Type Location x int reg: a1 y int fp: +8 a int fp: -4 b int fp: -8 symbol tables are trees • Scopes are nested within one another • Global scope • Function scope nested within global scope • Local blocks nested within functions (not in uC) • Variables can be accessed if they are in scope: if they exist in the current scope or any scope this scope is nested inside • Store pointers from parent scopes to children scopes (e.g., global scope has a child scope for each function), and from children scope to parent scope int foo( ... ) { ... } int bar( ... ) { ... for ( ... ) { ... } } Global foo bar for-loop looking for symbols • When you access a variable in code, you want to check the current scope for the variable, as well as all parent scopes • Bind the variable to the entry in the “closest” scope • When generating code for that variable, generate address based on entry • Global scope: absolute address • Local scope: address offset from frame pointer int foo( ... ) { ... } int bar( ... ) { ... for ( ... ) { ... } } Global foo bar for-loop dealing with overloading • Some language support function overloading • Multiple functions with the same name, but different numbers/types of arguments • How do we deal with repeated names for functions? • Use name mangling: encode additional information into each function to incorporate information about argument types • Creates a different name for each distinct function void foo(int x, float y) becomes void foo3_int_float(int x, float y) //why put “3” at the end of foo? next: code generation for functions Lecture_notes/1.3_Functions/Calling_a_function.pdf Calling a Function int main() { int x; read(x); return p(x); } int p(int x) { int y; y = x * x; return y; } what happens when a function is called? • Transfer arguments from caller to callee • Can happen through registers or by storing arguments in memory int main() { int x; read(x); return p(x); } int p(int x) { int y; y = x * x; return y; } what happens when a function is called? • Transfer arguments from caller to callee • Can happen through registers or by storing arguments in memory • Save important local data • e.g., register holding return address of this function int main() { int x; read(x); return p(x); } int p(int x) { int y; y = x * x; return y; } what happens when a function is called? • Transfer arguments from caller to callee • Can happen through registers or by storing arguments in memory • Save important local data • e.g., register holding return address of this function • Jump to function int main() { int x; read(x); return p(x); } int p(int x) { int y; y = x * x; return y; } what happens when a function is called? • Transfer arguments from caller to callee • Can happen through registers or by storing arguments in memory • Save important local data • e.g., register holding return address of this function • Jump to function • Allocate space for function locals • Temporaries, local variables, int main() { int x; read(x); return p(x); } int p(int x) { int y; y = x * x; return y; } what happens when a function is called? • Transfer arguments from caller to callee • Can happen through registers or by storing arguments in memory • Save important local data • e.g., register holding return address of this function • Jump to function • Allocate space for function locals • Temporaries, local variables • Execute function int main() { int x; read(x); return p(x); } int p(int x) { int y; y = x * x; return y; } what happens when a function is called? • Transfer arguments from caller to callee • Can happen through registers or by storing arguments in memory • Save important local data • e.g., register holding return address of this function • Jump to function • Allocate space for function locals • Temporaries, local variables • Execute function • Transfer return value from callee to caller int main() { int x; read(x); return p(x); } int p(int x) { int y; y = x * x; return y; } what happens when a function is called? • Transfer arguments from caller to callee • Can happen through registers or by storing arguments in memory • Save important local data • e.g., register holding return address of this function • Jump to function • Allocate space for function locals • Temporaries, local variables • Execute function • Transfer return value from callee to caller • Return control back to caller function stack foo() baz() main() main() { foo(); ... } foo() { bar(); ... baz(); } fp sp call stack function stack main() { foo(); ... } foo() { bar(); ... baz(); } call stack Ad dr es se s go d ow n function stack main() main() { foo(); ... } foo() { bar(); ... baz(); } fp sp call stack Ad dr es se s go d ow n function stack main() main() { foo(); ... } foo() { bar(); ... baz(); } fp sp call stack Ad dr es se s go d ow n foo() function stack main() main() { foo(); ... } foo() { bar(); ... baz(); } fp sp call stack Ad dr es se s go d ow n foo() bar() function stack main() main() { foo(); ... } foo() { bar(); ... baz(); } fp sp call stack Ad dr es se s go d ow n foo() function stack main() main() { foo(); ... } foo() { bar(); ... baz(); } fp sp call stack Ad dr es se s