Presentation is loading. Please wait.

Presentation is loading. Please wait.

Procedures & The Stack Announcements Lab 1 graded HW 2 out

Similar presentations


Presentation on theme: "Procedures & The Stack Announcements Lab 1 graded HW 2 out"— Presentation transcript:

1 Procedures & The Stack Announcements Lab 1 graded HW 2 out
Late days Extra credit HW 2 out Lab 2 prep in section tomorrow Bring laptops! Slides

2 Roadmap C: Java: Assembly language: OS: Machine code: Computer system:
Memory & data Integers & floats Machine code & C x86 assembly Procedures & stacks Arrays & structs Memory & caches Processes Virtual memory Memory allocation Java vs. C C: Java: car *c = malloc(sizeof(car)); c->miles = 100; c->gals = 17; float mpg = get_mpg(c); free(c); Car c = new Car(); c.setMiles(100); c.setGals(17); float mpg = c.getMPG(); Assembly language: get_mpg: pushq %rbp movq %rsp, %rbp ... popq %rbp ret OS: Machine code: Computer system:

3 Mechanisms required for procedures
Passing control To beginning of procedure code Back to return point Passing data Procedure arguments Return value Memory management Allocate during procedure execution Deallocate upon return All implemented with machine instructions An x86-64 procedure uses only those mechanisms required for that procedure P(…) { y = Q(x); print(y) } int Q(int i) { int t = 3*i; int v[10]; return v[t]; } USE BUILDS

4 Questions to answer about procedures
How do I pass arguments to a procedure? How do I get a return value from a procedure? Where do I put local variables? When a function returns, how does it know where to return? To answer some of these questions, we need a call stack …

5 Outline Stack Structure Calling Conventions Illustration of Recursion
Passing control Passing data Managing local data Illustration of Recursion

6 Memory Layout 2N-1 Stack Dynamic Data (Heap) Static Data Literals
High Addresses Stack local variables; procedure context Dynamic Data (Heap) variables allocated with new or malloc Memory Addresses Static Data static variables (including global variables (C)) Okay, last time I was hinting about how all your code is in memory... Well, today that should start to make sense Literals Large constants (e.g., “example”) Instructions Low Addresses

7 Memory Layout segmentation faults? Stack Dynamic Data (Heap)
Managed “automatically” (by compiler) writable; not executable Dynamic Data (Heap) writable; not executable Managed by programmer Static Data writable; not executable Initialized when process starts Initialized when prog starts Heap managed by programmer Stack usually automatic, but we’ll have to look at how it works in assembly HW/OS enforces permissions on regions of memory Will talk about it more later in the course Literals read-only; not executable Initialized when process starts Instructions read-only; executable Initialized when process starts

8 x86-64 Stack Stack “Bottom”
High Addresses Stack Pointer: %rsp Stack “Top” Stack “Bottom” Region of memory managed with stack “discipline” Grows toward lower addresses Customarily shown “upside-down” Register %rsp contains lowest stack address %rsp = address of top element, the most-recently-pushed item that is not-yet-popped Increasing Addresses Stack Grows Down Memory is still just an array But we treat the upper part like a stack that grows downwards rsp points at the lowest stack address, the top of the stack This will be the first thing to be popped off Remember: stacks only have 2 operations: push and pop Low Addresses

9 x86-64 Stack: Push Stack “Bottom” pushq Src Example: pushq %rcx
High Addresses Stack “Bottom” pushq Src Fetch operand at Src Src can be reg, memory, immediate Decrement %rsp by 8 Store value at address given by %rsp Example: pushq %rcx Adjust %rsp and store contents of %rcx on the stack Increasing Addresses Stack Grows Down Stack Pointer: %rsp Stack “Top” -8 Low Addresses 0x00…00

10 x86-64 Stack: Pop Stack “Bottom” popq Dest Example: popq %rcx
High Addresses Stack “Bottom” popq Dest Load value at address given by %rsp Store value at Dest (must be register) Increment %rsp by 8 Example: popq %rcx Stores contents of top of stack into %rcx and adjust %rsp Increasing Addresses Stack Grows Down +8 Stack Pointer: %rsp Stack “Top” Low Addresses 0x00…00 Those bits are still there; we’re just not using them.

11 Procedures Stack Structure Calling Conventions
Passing control Passing data Managing local data Illustration of Recursion

12 Procedure Call Overview
Caller procedures <set up args> call <clean up args> <find return val> Callee <create local vars> <set up return val> <destroy local vars> ret Callee must know where to find args Callee must know where to find return address Caller must know where to find return value Caller and Callee run on same CPU, so use the same registers How do we deal with register reuse? Unneeded steps can be skipped (e.g. if no arguments or no return value)

13 Procedure Call Overview
Caller <save regs> <set up args> call <clean up args> <restore regs> <find return val> Callee <save regs> <create local vars> <set up return val> <destroy local vars> <restore regs> ret The convention of where to leave/find things is called the calling convention (or procedure call linkage). Details vary between systems We will see the convention for x86-64/Linux in detail What could happen if our program didn’t follow these conventions? If everyone doesn’t use the same conventions, then we can’t call each others’ libraries

14 Code Examples void multstore (long x, long y, long *dest) {
long t = mult2(x, y); *dest = t; } <multstore>: 400540: push %rbx # Save %rbx 400541: mov %rdx,%rbx # Save dest 400544: callq <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) { long s = a * b; return s; } <mult2>: 400550: mov %rdi,%rax # a 400553: imul %rsi,%rax # a * b 400557: retq # Return Here’s an example where we have a funtion, multstore, which calls another function, mult2 Look at call, specifies where to jump to 400550 We’ll get to each of the details for this

15 Procedure Control Flow
Use stack to support procedure call and return Procedure call: call label Push return address on stack (why?, and which exact address?) Jump to label

16 Procedure Control Flow
Use stack to support procedure call and return Procedure call: call label Push return address on stack Jump to label Return address: Address of instruction immediately after call instruction Example from disassembly: 400544: callq <mult2> 400549: movq %rax,(%rbx) Return address = 0x400549 Procedure return: ret Pop return address from stack Jump to address next instruction happens to be a move, but could be anything

17 Procedure Call Example (step 1)
Procedure Call Example (step 1) 0x130 <multstore>: 400544: callq <mult2> 400549: mov %rax,(%rbx) 0x128 0x120 %rsp 0x120 %rip 0x400544 <mult2>: 400550: mov %rdi,%rax 400557: retq Push return address on stack update %rsp -> 0x118 400549 Jump to new function Update %rip

18 Procedure Call Example (step 2)
Procedure Call Example (step 2) %rsp 0x120 0x128 0x130 0x118 %rip <multstore>: 400544: callq <mult2> 400549: mov %rax,(%rbx) 0x400549 0x118 0x400550 <mult2>: 400550: mov %rdi,%rax 400557: retq Now return address points at where we’ll jump back to %rip is where we are executing now

19 Procedure Return Example (step 1)
Procedure Return Example (step 1) %rsp 0x120 0x128 0x130 0x118 %rip <multstore>: 400544: callq <mult2> 400549: mov %rax,(%rbx) 0x400549 0x118 0x400557 <mult2>: 400550: mov %rdi,%rax 400557: retq Now, after we’ve executed this function, we’re at the ret Pop return address read return address increment %rsp Jump back to return address update %rip with return address execute again starting there

20 Procedure Return Example (step 2)
Procedure Return Example (step 2) 0x130 <multstore>: 400544: callq <mult2> 400549: mov %rax,(%rbx) 0x128 0x120 %rsp 0x120 %rip 0x400549 <mult2>: 400550: mov %rdi,%rax 400557: retq

21 Procedures Stack Structure Calling Conventions
Passing control Passing data Managing local data Illustration of Recursion

22 Procedure Data Flow Registers – NOT in Memory! Stack – in Memory!
High Addresses First 6 arguments Return value Arg 7 • • • Arg 8 Arg n %rdi Diane’s Silk Dress Costs $8 9 %rsi %rdx %rcx %r8 %r9 Low Addresses 0x00…00 Diane’s Silk Dress Costs $8… 9 You’ll have this provided for you on exams, memorizing this just might be more convenient %rax Only allocate stack space when needed

23 X86-64 Return Values By convention, values returned by procedures are placed in the %rax register Choice of %rax is arbitrary, could have easily been a different register Caller must make sure to save the contents of %rax before calling a callee that returns a value Part of register-saving convention Callee places return value into the %rax register Any type that can fit in 8 bytes – integer, float, pointer, etc. For return values greater than 8 bytes, best to return a pointer to them Upon return, caller finds the return value in the %rax register Caller-saved vs callee-saved registers? Will talk about this more later Larger than 8 bytes: There’s some other part of the calling convention that dictates what to do Spoiler alert: usually it’s on the stack

24 Data Flow Examples void multstore (long x, long y, long *dest) {
long t = mult2(x, y); *dest = t; } <multstore>: # x in %rdi, y in %rsi, dest in %rdx • • • 400541: movq %rdx,%rbx # Save dest 400544: callq <mult2> # mult2(x,y) # t in %rax 400549: movq %rax,(%rbx) # Save at dest long mult2 (long a, long b) { long s = a * b; return s; } <mult2>: # a in %rdi, b in %rsi 400550: movq %rdi,%rax # a 400553: imul %rsi,%rax # a * b # s in %rax 400557: retq # Return How do we get data into and out of procedures? x -> a, y -> b, so no need to change those registers! but, we do need to save %rdx

25 Outline Procedures Stack Structure Calling Conventions Passing control
Passing data Managing local data Illustration of Recursion

26 Stack-Based Languages
Languages that support recursion e.g., C, Java, most modern languages Code must be re-entrant Multiple simultaneous instantiations of single procedure Need some place to store state of each instantiation Arguments Local variables Return pointer Stack discipline State for a given procedure needed for a limited time Starting from when it is called to when it returns Callee always returns before caller does Stack allocated in frames State for a single procedure instantiation Re-entrant means we need to be able to call the same function more than once Could be from within the function! or from another called function Execute a new copy Each time we call it, need somewhere to keep the local variables, and arguments The Stack gives us a place to put state while a function is executing only need state for the currently executing copy callee returns, and we can pop off its temporary data

27 Procedure amI is recursive (calls itself)
Call Chain Example Example Call Chain yoo(…) { who(); } yoo who(…) { amI(); } who amI(…) { if(…){ amI() } amI amI amI amI Procedure amI is recursive (calls itself)

28 Example: call to yoo Stack yoo yoo yoo(…) %rbp { • yoo who(); who %rsp
} %rbp %rsp yoo who amI amI amI amI

29 Example: call to who Stack yoo who yoo(…) { • who(); } yoo who(…) { •
amI(); } yoo who %rbp %rsp amI amI amI amI

30 Example: call to amI Stack yoo who amI yoo(…) { • who(); } yoo who(…)
• • • amI(); } yoo amI(…) { if(){ amI() } who amI amI %rbp %rsp amI amI

31 Example: recursive call to amI
Stack yoo who amI yoo(…) { who(); } yoo who(…) { • • • amI(); } yoo amI(…) { amI(); } who amI(…) { if(){ amI() } amI amI amI %rbp %rsp amI

32 Example: (another) recursive call to amI
Stack yoo who amI yoo(…) { who(); } yoo who(…) { • • • amI(); } yoo amI(…) { if(){ amI() } who amI(…) { if(){ amI() } amI amI amI(…) { if(){ amI() } amI amI %rbp %rsp Let’s say this time, the if condition is false, so we don’t recurse again

33 Return from: (another) recursive call to amI
Stack yoo who amI yoo(…) { who(); } yoo who(…) { • • • amI(); } yoo amI(…) { if(){ amI() } who amI(…) { if(){ amI() } amI amI amI %rbp %rsp amI amI Start popping our way back up the stack frames

34 Return from: recursive call to amI
Stack yoo who amI yoo(…) { who(); } yoo who(…) { • • • amI(); } yoo amI(…) { if(){ amI() } who amI amI %rbp %rsp amI amI amI amI

35 Return from: call to amI
Stack yoo who yoo(…) { who(); } yoo who(…) { amI(); } yoo who %rbp %rsp amI amI amI amI amI amI amI At this point, we’re back up to who, which called amI twice

36 Example: (second) call to amI
Stack yoo who amI yoo(…) { who(); } yoo who(…) { amI(); } yoo amI(…) { if(){ amI() } who amI amI %rbp %rsp amI amI

37 Return from: (second) call to amI
Stack yoo who yoo(…) { who(); } yoo who(…) { amI(); } yoo who %rbp %rsp amI amI amI amI amI

38 Return from: call to who
Stack yoo yoo %rbp %rsp yoo(…) { who(); } yoo who who amI amI amI amI amI

39 x86-64/Linux Stack Frame Caller’s Stack Frame
Extra arguments (if > 6 args) for this call Return address Pushed by call instruction Current/Callee Stack Frame Old frame pointer (optional) Saved register context (when reusing registers) Local variables (If can’t be kept in registers) “Argument build” area (If callee needs to call another function -parameters for function about to call, if needed) Caller Frame Arguments 7+ Frame pointer %rbp Return Addr Old %rbp (Optional) Saved Registers + Local Variables Argument Build (Optional) Stack pointer %rsp

40 April 22

41 Example: increment long increment(long *p, long val) { long x = *p;
long y = x + val; *p = y; return x; } increment: movq (%rdi), %rax addq %rax, %rsi movq %rsi, (%rdi) ret Register Use(s) %rdi Argument p %rsi Argument val, y %rax x, Return value (on handout)

42 Procedure Call Example (initial state)
Initial Stack Structure long call_incr() { long v1 = 351; long v2 = increment(&v1, 100); return v1+v2; } • • • Return addr <main+8> ⟵%rsp call_incr: subq $16, %rsp movq $351, 8(%rsp) movl $100, %esi leaq 8(%rsp), %rdi call increment addq 8(%rsp), %rax addq $16, %rsp ret Return address on stack is: address of instruction immediately following the call to “call_incr” (shown here as main, but could be anything). (on handout) We’re at the beginning of call_incr, let’s say it was called from main, so we already have some stuff up further in the stack, and the last thing, because we just started call_incr, is the return address to jump back into main

43 Procedure Call Example (step 1)
Stack Structure long call_incr() { long v1 = 351; long v2 = increment(&v1, 100); return v1+v2; } • • • Return addr <main+8> 351 Unused ⟵old %rsp ⟵%rsp+8 ⟵%rsp call_incr: subq $16, %rsp movq $351, 8(%rsp) movl $100, %esi leaq 8(%rsp), %rdi call increment addq 8(%rsp), %rax addq $16, %rsp ret Allocate space for local vars Setup space for local variables Only v1 needs space on the stack Compiler allocated extra space Often does this for a variety of reasons, including alignment. Remember back to when everyone was asking how we know where our variables live… Here, the compiler has created v1 on the stack…

44 Procedure Call Example (step 2)
Stack Structure long call_incr() { long v1 = 351; long v2 = increment(&v1, 100); return v1+v2; } • • • Return addr <main+8> 351 Unused ⟵%rsp+8 ⟵%rsp call_incr: subq $16, %rsp movq $351, 8(%rsp) movl $100, %esi leaq 8(%rsp), %rdi call increment addq 8(%rsp), %rax addq $16, %rsp ret Aside: movl is used because 100 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. Set up parameters for call to increment Now, we set up the arguments for our call to increment First parameter: &v1 8(%rsp) is the address of that value on our stack Second parameter: 100 use movl because 100 is a small positive value that will fit in 32 bits. The high order bits of %rsi get set to zero for us automatically. Next will be the actual call Register Use(s) %rdi &v1 %rsi 100

45 Procedure Call Example (step 3)
long call_incr() { long v1 = 351; long v2 = increment(&v1, 100); return v1+v2; } Stack Structure • • • Return addr <main+8> 351 Unused Return addr <call_incr+?> call_incr: subq $16, %rsp movq $351, 8(%rsp) movl $100, %esi leaq 8(%rsp), %rdi call increment addq 8(%rsp), %rax addq $16, %rsp ret ⟵%rsp State while inside increment. Return address on top of stack is address of the addq instruction immediately following call to increment. increment: movq (%rdi), %rax addq %rax, %rsi movq %rsi, (%rdi) ret Register Use(s) %rdi &v1 %rsi 100 %rax Push address of next instruction onto the stack In this case it’s the addq Then jump to the increment label Like push %rip+? Execute increment Read initial value where %rdi points Save that in %rax, that’s our return value do increment 100 with addq update what’s in memory

46 Procedure Call Example (step 4)
long call_incr() { long v1 = 351; long v2 = increment(&v1, 100); return v1+v2; } Stack Structure • • • Return addr <main+8> 451 Unused Return addr <call_incr+?> call_incr: subq $16, %rsp movq $351, 8(%rsp) movl $100, %esi leaq 8(%rsp), %rdi call increment addq 8(%rsp), %rax addq $16, %rsp ret ⟵%rsp State while inside increment. After code in body has been executed. increment: movq (%rdi), %rax # x = *p addq %rax, %rsi # y = x+100 movq %rsi, (%rdi) # *p = y ret Register Use(s) %rdi &v1 %rsi 451 %rax 351 Return Pop return address from stack Jump Like pop %rip

47 Procedure Call Example (step 5)
Stack Structure long call_incr() { long v1 = 351; long v2 = increment(&v1, 100); return v1+v2; } • • • Return addr <main+8> 451 Unused ⟵%rsp+8 ⟵%rsp call_incr: subq $16, %rsp movq $351, 8(%rsp) movl $100, %esi leaq 8(%rsp), %rdi call increment addq 8(%rsp), %rax addq $16, %rsp ret After returning from call to increment. Registers and memory have been modified and return address has been popped off stack. Register Use(s) %rdi &v1 %rsi 451 %rax 351

48 Procedure Call Example (step 6)
Stack Structure long call_incr() { long v1 = 351; long v2 = increment(&v1, 100); return v1+v2; } • • • Return addr <main+8> 451 Unused ⟵%rsp+8 ⟵%rsp call_incr: subq $16, %rsp movq $351, 8(%rsp) movl $100, %esi leaq 8(%rsp), %rdi call increment addq 8(%rsp), %rax addq $16, %rsp ret Update %rax to contain v1+v2 Register Use(s) %rax

49 Procedure Call Example (step 7)
Stack Structure long call_incr() { long v1 = 351; long v2 = increment(&v1, 100); return v1+v2; } • • • Return addr <main+8> 451 Unused ⟵%rsp ⟵old %rsp call_incr: subq $16, %rsp movq $351, 8(%rsp) movl $100, %esi leaq 8(%rsp), %rdi call increment addq 8(%rsp), %rax addq $16, %rsp ret De-allocate space for local vars Register Use(s) %rax 802

50 Procedure Call Example (step 8)
Stack Structure long call_incr() { long v1 = 351; long v2 = increment(&v1, 100); return v1+v2; } • • • Return addr <main+8> ⟵%rsp call_incr: subq $16, %rsp movq $351, 8(%rsp) movl $100, %esi leaq 8(%rsp), %rdi call increment addq 8(%rsp), %rax addq $16, %rsp ret State just before returning from call to call_incr. Register Use(s) %rax 802

51 Procedure Call Example (step 9)
Final Stack Structure long call_incr() { long v1 = 351; long v2 = increment(&v1, 100); return v1+v2; } %rsp . . . call_incr: subq $16, %rsp movq $351, 8(%rsp) movl $100, %esi leaq 8(%rsp), %rdi call increment addq 8(%rsp), %rax addq $16, %rsp ret State immediately AFTER returning from call to call_incr. Return addr has been popped off stack Control has returned to the instruction immediately following the call to call_incr (not shown here). Register Use(s) %rax 802

52 Register Saving Conventions
When procedure yoo calls who: yoo is the caller who is the callee Can register be used for temporary storage? No! Contents of register %rdx overwritten by who! This could be trouble – something should be done. Either: caller should save %rdx before the call (and restore it after the call) callee should save %rdx before using it (and restore it before returning) yoo: • • • movq $15213, %rdx call who addq %rdx, %rax ret who: • • • subq $18213, %rdx ret ?

53 Register Saving Conventions
When procedure yoo calls who: yoo is the caller who is the callee Can a register be used for temporary storage? Conventions “Caller Saved” Caller saves temporary values in its stack frame before calling Caller restores values after the call “Callee Saved” Callee saves temporary values in its stack frame before using Callee restores them before returning to caller

54 x86-64 Linux Register Usage, part1
%rax Return value Also caller-saved & restored Can be modified by procedure %rdi, ..., %r9 Arguments %r10, %r11 Caller-saved & restored Return value %rax %rdi %rsi %rdx Arguments %rcx %r8 %r9 %r10 Caller-saved temporaries %r11

55 x86-64 Linux Register Usage, part 2
%rbx, %r12, %r13, %r14 Callee-saved Callee must save & restore %rbp May be used as frame pointer Can mix & match %rsp Special form of callee save Restored to original value upon exit from procedure %rbx %r12 Callee-saved Temporaries %r13 %r14 %rbp Special %rsp

56 x86-64 64-bit Registers: Usage Conventions
%rax %r8 Return value - Caller saved Argument #5 - Caller saved %rbx %r9 Callee saved Argument #6 - Caller saved %rcx %r10 Argument #4 - Caller saved Caller saved %rdx %r11 Argument #3 - Caller saved Caller Saved %rsi %r12 Argument #2 - Caller saved Callee saved %rdi %r13 Argument #1 - Caller saved Callee saved %rsp %r14 Stack pointer Callee saved %rbp %r15 Callee saved Callee saved

57 Callee-Saved Example (step 1)
Initial Stack Structure long call_incr2(long x) { long v1 = 351; long v2 = increment(&v1, 100); return x+v2; } %rsp . . . Rtn address call_incr2: pushq %rbx subq $16, %rsp movq %rdi, %rbx movq $351, 8(%rsp) movl $100, %esi leaq 8(%rsp), %rdi call increment addq %rbx, %rax addq $16, %rsp popq %rbx ret Resulting Stack Structure 351 Unused %rsp . . . Rtn address %rsp+8 Saved %rbx Modified example from earlier Now call_incr takes an argument, long x, which is used in computing its result x is in %rdi Need to save it somewhere: use a callee-saved register But call_incr is a callee, too! Have to save %rbx for whoever called call_incr2

58 Callee-Saved Example (step 2)
Stack Structure long call_incr2(long x) { long v1 = 351; long v2 = increment(&v1, 100); return x+v2; } 351 Unused %rsp . . . Rtn address %rsp+8 Saved %rbx call_incr2: pushq %rbx subq $16, %rsp movq %rdi, %rbx movq $351, 8(%rsp) movl $100, %esi leaq 8(%rsp), %rdi call increment addq %rbx, %rax addq $16, %rsp popq %rbx ret Pre-return Stack Structure %rsp . . . Rtn address Can you think of another way we could have done this? Push %rdi onto the stack Why did the compiler choose this way? In this case it’s a wash, but if we ever use %rbx again, then it was worth it

59 Why Caller and Callee Saved?
We want one calling convention to simply separate implementation details between caller and callee In general, neither caller-save nor callee-save is “best”: If caller isn’t using a register, caller-save is better If callee doesn’t need a register, callee-save is better If “do need to save”, callee-save generally makes smaller programs Functions are called from multiple places So… “some of each” and compiler tries to “pick registers” that minimize amount of saving/restoring

60 Procedures Stack Structure Calling Conventions
Passing control Passing data Managing local data Illustration of Recursion

61 Recursive Function pcount_r: movl $0, %eax testq %rdi, %rdi je .L6
pushq %rbx movq %rdi, %rbx andl $1, %ebx shrq %rdi call pcount_r addq %rbx, %rax popq %rbx .L6: rep; ret /* Recursive popcount */ long pcount_r(unsigned long x) { if (x == 0) return 0; else return (x & 1) + pcount_r(x >> 1); }

62 Recursive Function: Base Case
pcount_r: movl $0, %eax testq %rdi, %rdi je L6 pushq %rbx movq %rdi, %rbx andl $1, %ebx shrq %rdi call pcount_r addq %rbx, %rax popq %rbx .L6: rep; ret /* Recursive popcount */ long pcount_r(unsigned long x) { if (x == 0) return 0; else return (x & 1) + pcount_r(x >> 1); } Trick because some HW doesn’t like jumping to ret Register Use(s) Type %rdi x Argument %rax Return value First: look at the test: test if %rdi == 0 If it does, then jump to the ret Note: share ret between both cases, so set rax=0 before jump Rep; ret is just because some HW doesn‘t like jumping to ret

63 Recursive Function: Register Save
pcount_r: movl $0, %eax testq %rdi, %rdi je L6 pushq %rbx movq %rdi, %rbx andl $1, %ebx shrq %rdi call pcount_r addq %rbx, %rax popq %rbx .L6: rep; ret /* Recursive popcount */ long pcount_r(unsigned long x) { if (x == 0) return 0; else return (x & 1) + pcount_r(x >> 1); } . . . Register Use(s) Type %rdi x Argument <main+15> Saved %rbx ←%rsp

64 Recursive Function: Call Setup
pcount_r: movl $0, %eax testq %rdi, %rdi je L6 pushq %rbx movq %rdi, %rbx andl $1, %ebx shrq %rdi call pcount_r addq %rbx, %rax popq %rbx .L6: rep; ret /* Recursive popcount */ long pcount_r(unsigned long x) { if (x == 0) return 0; else return (x & 1) + pcount_r(x >> 1); } . . . Register Use(s) Type %rdi x >> 1 Recursive arg %rbx x & 1 Callee-saved rtn <main+?> Saved %rbx ←%rsp

65 Recursive Function: Call
pcount_r: movl $0, %eax testq %rdi, %rdi je L6 pushq %rbx movq %rdi, %rbx andl $1, %ebx shrq %rdi call pcount_r addq %rbx, %rax popq %rbx .L6: rep; ret /* Recursive popcount */ long pcount_r(unsigned long x) { if (x == 0) return 0; else return (x & 1) + pcount_r(x >> 1); } . . . Register Use(s) Type %rbx x & 1 Callee-saved %rax Recursive call return value rtn <main+?> Saved %rbx rtn <pcount_r+22> ←%rsp ...

66 Recursive Function: Result
pcount_r: movl $0, %eax testq %rdi, %rdi je L6 pushq %rbx movq %rdi, %rbx andl $1, %ebx shrq %rdi call pcount_r addq %rbx, %rax popq %rbx .L6: rep; ret /* Recursive popcount */ long pcount_r(unsigned long x) { if (x == 0) return 0; else return (x & 1) + pcount_r(x >> 1); } . . . Register Use(s) Type %rbx x & 1 Callee-saved %rax Return value rtn <main+?> Saved %rbx ←%rsp

67 Recursive Function: Completion
pcount_r: movl $0, %eax testq %rdi, %rdi je L6 pushq %rbx movq %rdi, %rbx andl $1, %ebx shrq %rdi call pcount_r addq %rbx, %rax popq %rbx .L6: rep; ret /* Recursive popcount */ long pcount_r(unsigned long x) { if (x == 0) return 0; else return (x & 1) + pcount_r(x >> 1); } . . . Register Use(s) Type %rax Return value ←%rsp rtn <main+?> Saved %rbx

68 Observations About Recursion
Works without any special consideration Stack frames mean that each function call has private storage Saved registers & local variables Saved return pointer Register saving conventions prevent one function call from corrupting another’s data Unless the code explicitly does so (e.g., buffer overflow - described in future lecture) Stack discipline follows call / return pattern If P calls Q, then Q returns before P Last-In, First-Out Also works for mutual recursion P calls Q; Q calls P

69 x86-64 Stack Frames Often (ideally), x86-64 functions need no stack frame at all Just a return address is pushed onto the stack when a function call is made A function does need a stack frame when it: Has too many local variables to hold in registers Has local variables that are arrays or structs Uses the address-of operator (&) to compute the address of a local variable Calls another function that takes more than six arguments Needs to save the state of caller-save registers before calling a procedure Needs to save the state of callee-save registers before modifying them

70 x86-64 Procedure Summary Important Points
Procedures are a combination of instructions and conventions Conventions prevent functions from disrupting each other Stack is the right data structure for procedure call / return If P calls Q, then Q returns before P Recursion handled by normal calling conventions Caller can store values in local stack frame and in callee-saved registers Put function arguments at top of stack Result return in %rax Caller Frame Arguments 7+ Return Addr %rbp (Optional) Old %rbp Saved Registers + Local Variables Argument Build %rsp

71 https://courses.cs.washington.edu/courses/cse351/videos/05/056.mp4
One more x86-64 example Example of passing more than 6 parameters and passing addresses of local variables The following example, along with a brief re-cap of x86-64 calling conventions is in this video: 5. Procedures and Stacks 6. x86-64 Calling Conventions

72 Return address to caller of call_proc
x86-64 Example (1) long int call_proc() { long x1 = 1; int x2 = 2; short x3 = 3; char x4 = 4; proc(x1, &x1, x2, &x2, x3, &x3, x4, &x4); return (x1+x2)*(x3-x4); } call_proc: subq $32,%rsp movq $1,16(%rsp) # x1 movl $2,24(%rsp) # x2 movw $3,28(%rsp) # x3 movb $4,31(%rsp) # x4 • • • Return address to caller of call_proc ←%rsp Note: Details may vary depending on compiler.

73 x86-64 Example (2) – Allocate local vars
long int call_proc() { long x1 = 1; int x2 = 2; short x3 = 3; char x4 = 4; proc(x1, &x1, x2, &x2, x3, &x3, x4, &x4); return (x1+x2)*(x3-x4); } call_proc: subq $32,%rsp movq $1,16(%rsp) # x1 movl $2,24(%rsp) # x2 movw $3,28(%rsp) # x3 movb $4,31(%rsp) # x4 • • • Return address to caller of call_proc x4 x3 x2 24 x1 16 8 ←%rsp

74 x86-64 Example (3) – setup params to proc
long int call_proc() { long x1 = 1; int x2 = 2; short x3 = 3; char x4 = 4; proc(x1, &x1, x2, &x2, x3, &x3, x4, &x4); return (x1+x2)*(x3-x4); } call_proc: • • • leaq 24(%rsp),%rcx # %rcx=&x2 leaq 16(%rsp),%rsi # %rsi=&x1 leaq 31(%rsp),%rax # %rax=&x4 movq %rax,8(%rsp) # arg8=&4 movl $4,(%rsp) # arg7=4 leaq 28(%rsp),%r9 # %r9=&x3 movl $3,%r8d # %r8 = 3 movl $2,%edx # %rdx = 2 movq $1,%rdi # %rdi = 1 call proc Return address to caller of call_proc x4 x3 x2 24 Arguments passed in (in order): rdi, rsi, rdx, rcx, r8, r9 x1 16 Arg 8 Arg 7 Same instructions as in video, just a different order. 8 ←%rsp

75 x86-64 Example (4) – setup params to proc
long int call_proc() { long x1 = 1; int x2 = 2; short x3 = 3; char x4 = 4; proc(x1, &x1, x2, &x2, x3, &x3, x4, &x4); return (x1+x2)*(x3-x4); } call_proc: • • • leaq 24(%rsp),%rcx leaq 16(%rsp),%rsi leaq 31(%rsp),%rax movq %rax,8(%rsp) movl $4,(%rsp) leaq 28(%rsp),%r9 movl $3,%r8d movl $2,%edx movq $1,%rdi call proc Note sizes Return address to caller of call_proc Arguments passed in (in order): rdi, rsi, rdx, rcx, r8, r9 x4 x3 x2 24 x1 16 Arg 8 Arg 7 8 ←%rsp

76 x86-64 Example (5) – call proc
long int call_proc() { long x1 = 1; int x2 = 2; short x3 = 3; char x4 = 4; proc(x1, &x1, x2, &x2, x3, &x3, x4, &x4); return (x1+x2)*(x3-x4); } call_proc: • • • leaq 24(%rsp),%rcx leaq 16(%rsp),%rsi leaq 31(%rsp),%rax movq %rax,8(%rsp) movl $4,(%rsp) leaq 28(%rsp),%r9 movl $3,%r8d movl $2,%edx movq $1,%rdi call proc Return address to caller of call_proc x4 x3 x2 x1 Arg 8 Arg 7 Return address to line after call to proc ←%rsp

77 x86-64 Example (6) – after call to proc
call_proc: • • • movswl 28(%rsp),%eax # %eax=x3 movsbl 31(%rsp),%edx # %edx=x4 subl %edx,%eax # %eax=x3-x4 cltq movslq 24(%rsp),%rdx # %rdx=x2 addq 16(%rsp),%rdx #%rdx=x1+x2 imulq %rdx,%rax # %rax=rax*rdx addq $32,%rsp ret long int call_proc() { long x1 = 1; int x2 = 2; short x3 = 3; char x4 = 4; proc(x1, &x1, x2, &x2, x3, &x3, x4, &x4); return (x1+x2)*(x3-x4); } movs__: move and sign extend cltq: sign extend %eax into %rax (special-case to save space) Return address to caller of call_proc x4 x3 x2 24 x1 cltq: convert %eax to quad-word: R[%rax] <- SignExtend(R[%eax]). Used to help support multiplication/division operations… 16 Arg 8 Arg 7 8 ←%rsp

78 x86-64 Example (7) – de-allocate local vars
long int call_proc() { long x1 = 1; int x2 = 2; short x3 = 3; char x4 = 4; proc(x1, &x1, x2, &x2, x3, &x3, x4, &x4); return (x1+x2)*(x3-x4); } call_proc: • • • movswl 28(%rsp),%eax movsbl 31(%rsp),%edx subl %edx,%eax cltq movslq 24(%rsp),%rdx addq 16(%rsp),%rdx imulq %rdx,%rax addq $32,%rsp ret Return address to caller of call_proc ←%rsp


Download ppt "Procedures & The Stack Announcements Lab 1 graded HW 2 out"

Similar presentations


Ads by Google