Subroutines and Control Abstraction ICOM 4036 Lecture 8
Subroutines and Control Abstraction ICOM 4036 Lecture 8
Implementing Procedures • Why procedures? – Abstraction – Modularity – Code re-use • Initial Goal – Write segments of assembly code that can be reused, or “called” from different points in the main program. – KISS: Keep It Simple Stupid: • no parameters, no recursion, no locals, no return values
Procedure Linkage Approach I • Problem – procedure must determine where to return after servicing the call • Solution: Architecture Support – Add a jump instruction that saves the return address in some place known to callee • MIPS: jal instruction saves return address in register $ra – Add an instruction that can jump to return address • MIPS: jr instruction jumps to the address contained in its argument register
Computing Integer Division (Procedure Version) Iterative C++ Version int a = 0; int b = 0; int res = 0; main () { a = 12; b = 5; res = 0; # div function div(); # PROBLEM: Must save printf(“Res = %d”, res); d: } la void div(void) { lw la while (a >= b) { lw a = a - b; la lw res ++; while: bgt sub } addi } j ewhile: C++ enddiv: MIPS Assembly Language la sw jr args and registers before using them # void d(void) { # // Allocate registers for globals $s 0, x # // x in $s 1, 0($s 0) $s 0, y # // y in $s 2, 0($s 0) $s 0, res # // res in $s 3, 0($s 0) $s 2, $s 1, ewhile # while (x <= y) { $s 1, $s 2 # x = x - y $s 3, 1 # res ++ while # } # // Update variables in memory $s 0, x $s 1, 0($s 0) $s 0, y $s 2, 0($s 0) $s 0, res $s 3, 0($s 0) $ra # return; # }
Computing Integer Division (Procedure Version) Iterative C++ Version int a = 0; int b = 0; . data. word int res = 0; x: y: . word main () { res: . word pf 1: . asciiz a = 12; pf 2: . asciiz b = 5; . globl. text res = 0; main: div(); la printf(“Res = %d”, res); li sw } la void div(void) { li sw while (a >= b) { la a = a - b; li sw res ++; jal } lw la } li C++ MIPS Assembly Language syscall move li syscall la li syscall move li syscall jr 0 0 0 "Result = " "Remainder = " main $s 0, $s 1, $s 0, $s 2, $s 0, $s 3, d $s 3, $a 0, $v 0, x 12 0($s 0) y 5 0($s 0) res 0 0($s 0) # int main() { # // main assumes registers sx unused # x = 12; Function # Call y = 5; # res = 0; # div(); # # printf("Result = %d n"); //system call to print_str $a 0, $s 3 $v 0, 1 # //system call to print_int $a 0, pf 2 $v 0, 4 # # printf("Remainder = %d n"); //system call to print_str $a 0, $s 1 $v 0, 1 # //system call to print_int $ra # return // TO Operating System 0($s 0) pf 1 4
Pending Problems With Linkage Approach I • Registers shared by all procedures – procedures must save/restore registers (use stack) • Procedures should be able to call other procedures – save multiple return addresses (use stack) • Lack of parameters forces access to globals – pass parameters in registers • Recursion requires multiple copies of local data – store multiple procedure activation records (use stack) • Need a convention for returning function values – return values in registers
Recursion Basics int fact(int n) if (n == 0) return else return } { { 1; n = 0 (fact(n-1) * n); n = 1 fact(3) n = 3 3 * 2 = 6 fact(2) n = 2 n = 3 fact(1) 2 * 1 = 2 1 * 1 = 1 n = 1 1 fact(0) n = 0
Solution: Use Stacks of Procedure Frames • Stack frame contains: – Saved arguments – Saved registers – Return address – Local variables OS main stack frame div stack frame stack growth
Anatomy of a Stack Frame caller’s stack frame function arguments frame Pointer $fp in MIPS return address saved registers local variables of static size stack Pointer $sp in MIPS work area Contract: Every function must leave the stack the way it found it
Example: Function Linkage using Stack Frames int x = 0; int y = 0; int res = 0; main () { x = 12; y = 5; res = div(x, y); printf(“Res = %d”, res); } int div(int a, int b) { int res = 0; if (a >= b) { res = div(a-b, b) + 1; } else { res = 0; } return res; } • Add return values • Add parameters • Add recursion • Add local variables
Example: Function Linkage using Stack Frames div: if: else: endif: enddiv: # sub sw sw sw li sw $sp, $a 0, $a 1, $ra, $s 1, $s 2, $s 3, $sp, 28 24($sp) 20($sp) 16($sp) 12($sp) 8($sp) 4($sp) 0 0($sp) # # # Alloc space for 28 byte stack frame Save argument registers a in $a 0 Save other registers as needed Save callee saved registers ($sx) lw lw lw $s 1, 24($sp) $s 2, 20($sp) $s 3, 0($sp) bgt sub move jal addi j li $s 2, $s 1, else $a 0, $s 1, $s 2 $a 1, $s 2 div $s 3, $v 0, 1 endif $s 3, 0 # if (a >= b) { # sw sw sw move $s 1, $s 2, $s 3, $v 0, 32($sp) 28($sp) 0($sp) $s 3 # deallocate a from $s 1 # deallocate b from $s 2 # deallocate res from $s 3 # return res lw lw lw addu jr $a 0, $a 1, $ra, $s 1, $s 2, $s 3, $sp, $ra 24($sp) 20($sp) 16($sp) 12($sp) 8($sp) 4($sp) $sp, 28 # # # No need to save $s 4, since not used # int res = 0; # Allocate registers for locals # a in $s 1 # b in $s 2 # res in $s 3 # # res = div(a-b, b) + 1; # } # else { res = 0; } Restore saved registers a in $a 0 Save other registers as needed Save callee saved registers ($sx) # No need to save $s 4, since not used # pop stack frame # return;
Run Div Example in SPIM
MIPS: Procedure Linkage Summary • • • First 4 arguments passed in $a 0 -$a 3 Other arguments passed on the stack Return address passed in $ra Return value(s) returned in $v 0 -$v 1 Sx registers saved by callee Tx registers saved by caller
Blackboard Exercise • Implement recursive gcd in MIPS int gcd(int a, int b) { if (a % b == 0) return b; return gcd(b, a % b); }
Discuss Impact of Other Procedure Features on Implementation • • • Reference parameters Functional parameters Complex object parameters Variable number of parameters Functional return values Named parameters Which phases of the compiler will be affected?
- Slides: 15