Procedures and the Call Stack Topics • Procedures • Call stack • Procedure/stack instructions • Calling conventions • Register-saving conventions
Why Procedures? Why functions? Why methods? int contains_char(char* haystack, char needle) { while (*haystack != '\0') { if (*haystack == needle) return 1; haystack++; } return 0; } Procedural Abstraction
Implementing Procedures How does a caller pass arguments to a procedure? How does a caller get a return value from a procedure? Where does a procedure store local variables ? How does a procedure know where to return (what code to execute next when done)? How do procedures share limited registers and memory ? 3
Call Chain Example Call Chain yoo(…) { yoo • • who(…) who(); who { • • • • • ru(); ru ru ru(…) } • • • { ru(); • • • • • } • } 4
First Try (broken) yoo: who: jmp who back: done: jmp back stop: What if we want to call a function from multiple places in the code?
First Try (broken) What if we want to call a function from multiple places in the code? who: 1 2 ru: jmp ru back2: 6 3,7 5 jmp ru 4 back2: done: jmp back2 9 8 stop:
Implementing Procedures How does a caller pass arguments to a procedure? How does a caller get a return value from a procedure? Where does a procedure store local variables ? How does a procedure know where to return (what code to execute next when done)? How do procedures share limited registers and memory ? All these need separate storage per call! (not just per procedure) 7
Memory Layout Addr Perm Contents Managed by Initialized Stack 2 N -1 RW Procedure context Compiler Run-time Programmer, Dynamic Heap RW Run-time malloc/free, data structures new/GC Global variables/ Compiler/ Statics RW Startup static data structures Assembler/Linker Compiler/ Literals R String literals Startup Assembler/Linker Compiler/ Text X Instructions Startup Assembler/Linker 0
Call Stack Stack “Bottom” Memory region managed with stack discipline higher %rsp holds lowest stack address addresses (address of "top" element) stack grows toward lower addresses Stack Pointer: %rsp Stack “Top” 9
Call Chain Example Example Call Chain yoo(…) { yoo • • who(…) who(); who { • • • amI(); amI amI amI(…) } • { amI(); • amI • if(…){ } amI() } amI • } Procedure amI is recursive (calls itself) 14
Stack yoo yoo(…) { yoo yoo • who %rsp • who(); amI amI • • } amI amI 15
Stack yoo(…) yoo who(…) { yoo yoo { • amI(…) who • • • • { amI(); who(); amI(…) • who • • • amI • amI { if(…){ amI(); amI(…) • • amI() { • • • } if(…){ } amI amI • } amI() • if(…){ } } amI() • amI amI } } • } amI %rsp 19
Stack yoo(…) yoo who(…) { yoo { yoo • who • • amI(); who(); who • amI • amI %rsp amI(); • • } amI amI } amI amI amI 22
Stack yoo(…) yoo who(…) { yoo yoo { • amI(…) who • • { amI(); who(); • who • amI • amI if(){ amI(); • amI() • } } amI amI } • %rsp } amI 23
Stack yoo yoo(…) yoo yoo { who %rsp • • who(); who amI amI • • amI } amI amI 25
Stack frames support procedure calls. Contents Local variables Caller Function arguments (after first 6) Frame Return information Temporary space Frame for current Management procedure Space allocated when procedure is entered %rsp Stack Pointer “Setup” code Space deallocated before return Stack “Top” “Finish” code Why not just give every procedure a permanent chunk of memory to hold its local variables, etc? 26
Code Examples void multstore (long x, long y, long *dest) { long t = mult2(x, y); *dest = t; 0000000000400540 <multstore>: } 400540: push %rbx # Save %rbx 400541: mov %rdx,%rbx # Save dest 400544: callq 400550 <mult2> # mult2(x,y) 400549: mov %rax,(%rbx) # Save at dest 40054c: pop %rbx # Restore %rbx 40054d: retq # Return long mult2(long a, long b){ 0000000000400550 <mult2>: long s = a * b; 400550: mov %rdi,%rax # a return s; 400553: imul %rsi,%rax # a * b } 400557: retq # Return 27
Procedure Control Flow Instructions Procedure call: callq label 1. Push return address on stack 2. Jump to label Return address: Address of instruction after call . Example: 400544: callq 400550 <mult2> 400549: movq %rax,(%rbx) Procedure return: retq 1. Pop return address from stack 2. Jump to address 28
Call Example (step 1) • • 0x130 0000000000400540 <multstore>: • 0x128 • • 0x120 400544: callq 400550 <mult2> 400549: mov %rax,(%rbx) • %rsp 0x120 • %rip 0x400544 0000000000400550 <mult2>: 400550: mov %rdi,%rax • • 400557: retq 29
Call Example (step 2) • • 0x130 0000000000400540 <multstore>: • 0x128 • • 0x120 400544: callq 400550 <mult2> 0x118 0x400549 400549: mov %rax,(%rbx) • %rsp 0x118 • %rip 0x400550 0000000000400550 <mult2>: 400550: mov %rdi,%rax • • 400557: retq 30
Return Example (step 1) • • 0x130 0000000000400540 <multstore>: • 0x128 • • 0x120 400544: callq 400550 <mult2> 0x118 0x400549 400549: mov %rax,(%rbx) • %rsp 0x118 • %rip 0x400557 0000000000400550 <mult2>: 400550: mov %rdi,%rax • • 400557: retq 31
Return Example (step 2) • • 0x130 0000000000400540 <multstore>: • 0x128 • • 0x120 400544: callq 400550 <mult2> 400549: mov %rax,(%rbx) • %rsp 0x120 • %rip 0x400549 0000000000400550 <mult2>: 400550: mov %rdi,%rax • • 400557: retq 32
Procedure Data Flow Remaining arguments passed First 6 arguments passed on stack (in memory) in registers High Addresses Arg 1 %r d i Diane’s • • • Silk %r s i Dress Arg n %r d x Costs $8 9 %r c x • • • %r 8 Arg 8 Arg 6 %r 9 Arg 7 Return value Low Addresses %rax Only allocate stack space when needed
Stack Frame … Caller Frame Extra Arguments to callee Return Address Saved Registers + Callee Local Variables Frame Extra Arguments Stack pointer %rsp for next call 35
Common Stack Frame Caller Return Address Frame Callee Frame Stack pointer %rsp 36
Data Flow Examples void multstore (long x, long y, long *dest) { long t = mult2(x, y); *dest = t; 0000000000400540 <multstore>: } # x in %rdi, y in %rsi, dest in %rdx • • • 400541: movq %rdx,%rbx # Save dest 400544: callq 400550 <mult2> # mult2(x,y) # t in %rax 400549: movq %rax,(%rbx) # Save at dest • • • long mult2(long a, 0000000000400550 <mult2>: long b){ # a in %rdi, b in %rsi long s = a * b; 400550: movq %rdi,%rax # a return s; 400553: imul %rsi,%rax # a * b } # s in %rax 400557: retq # Return 37
Example: increment long increment(long* p, long val) { long x = *p; long y = x + val; *p = y; return x; } increment: Register Use(s) movq (%rdi), %rax Argument p %rdi addq %rax, %rsi movq %rsi, (%rdi) Argument val , y %rsi ret x , Return value %rax 38
Procedure Call Example (initial state) Initial Stack Structure long call_incr() { long v1 = 240; long v2 = increment(&v1, 61); • • • return v1+v2; %rsp } Return addr <main+8> call_incr: subq $16, %rsp %rdi movq $240, 8(%rsp) movl $61, %esi leaq 8(%rsp), %rdi %rsi call increment addq 8(%rsp), %rax addq $16, %rsp %rax ret 39
Procedure Call Example (step 1) Stack Structure long call_incr() { long v1 = 240; • • • long v2 = increment(&v1, 61); return v1+v2; %rsp } Return addr <main+8> Space for v1 à 240 Compiler allocated extra space for alignment à Unused call_incr: subq $16, %rsp Allocate space for local vars %rdi movq $240, 8(%rsp) movl $61, %esi leaq 8(%rsp), %rdi %rsi call increment addq 8(%rsp), %rax addq $16, %rsp %rax ret 40
Procedure Call Example (step 2) Stack Structure long call_incr() { long v1 = 240; • • • long v2 = increment( &v1, 61 ); return v1+v2; %rsp } Return addr <main+8> 240 Unused call_incr: subq $16, %rsp %rdi movq $240, 8(%rsp) &v1 movl $61, %esi Set up args for call leaq 8(%rsp), %rdi to increment %rsi call increment 61 addq 8(%rsp), %rax addq $16, %rsp %rax ret Aside: movl is used because 61 is a small positive value that fits in 32 bits. High order bits of % rsi get set to zero automatically. It takes one less byte to encode a movl than a movq . 41
Procedure Call Example (step 3) Stack Structure long call_incr() { long v1 = 240; long v2 = increment( &v1, 61 ) ; return v1+v2; • • • } %rsp Return addr <main+8> call_incr: subq $16, %rsp 240 movq $240, 8(%rsp) Unused movl $61, %esi Return addr <call_incr+?> leaq 8(%rsp), %rdi call increment %rdi addq 8(%rsp), %rax addq $16, %rsp &v1 ret %rsi increment: 61 movq (%rdi), %rax addq %rax, %rsi %rax movq %rsi, (%rdi) ret 42
Recommend
More recommend