ProceduresandtheCallStack
Topics• Procedures• Callstack• Procedure/stackinstructions• Callingconventions• Register-savingconventions
WhyProcedures?Whyfunctions?Whymethods?
ProceduralAbstraction
int contains_char(char* haystack, char needle) {while (*haystack != '\0') {
if (*haystack == needle) return 1;haystack++;
}return 0;
}
ImplementingProcedures
Howdoesacallerpassarguments toaprocedure?
Howdoesacallergetareturnvaluefromaprocedure?
Wheredoesaprocedurestorelocalvariables?
Howdoesaprocedureknowwheretoreturn(whatcodetoexecutenextwhendone)?
Howdoproceduressharelimitedregistersandmemory?
3
CallChainyoo(…){
••who();••
}
who(…){
• • •ru();• • •ru();• • •
}
ru(…){
•••
}
yoo
who
ru
ExampleCallChain
ru
4
FirstTry(broken)
yoo:
jmp whoback:
stop:
who:
done:jmp back
Whatifwewanttocallafunctionfrommultipleplacesinthecode?
FirstTry(broken)
who:
jmp ruback2:
jmp ruback2:
stop:
ru:
done:jmp back2
12
65 3,7
89
4
Whatifwewanttocallafunctionfrommultipleplacesinthecode?
ImplementingProcedures
Howdoesacallerpassarguments toaprocedure?
Howdoesacallergetareturnvaluefromaprocedure?
Wheredoesaprocedurestorelocalvariables?
Howdoesaprocedureknowwheretoreturn(whatcodetoexecutenextwhendone)?
Howdoproceduressharelimitedregistersandmemory?
7
Alltheseneedseparatestorage percall!(notjustperprocedure)
Addr Perm Contents Managedby Initialized
2N-1 Stack RW Procedurecontext Compiler Run-time
Heap RW Dynamicdata structuresProgrammer,malloc/free,new/GC
Run-time
Statics RW Globalvariables/staticdatastructuresCompiler/
Assembler/Linker Startup
Literals R Stringliterals Compiler/Assembler/Linker Startup
Text X Instructions Compiler/Assembler/Linker Startup
0
MemoryLayout
CallStack
Memoryregionmanagedwithstackdiscipline
%rsp holdsloweststackaddress(addressof"top"element)
StackPointer:%rsp
stackgrowstoward
lower addresses
higheraddresses
Stack“Top”
Stack“Bottom”
9
CallChainExampleyoo(…){
••who();••
}
who(…){
• amI();• amI();•
}
amI(…){
•if(…){amI()
}•
}
yoo
who
amI
amI
amI
ExampleCallChain
amI
ProcedureamI isrecursive(callsitself)
14
yoo
who
amI
amI
amI
amI
yoo%rsp
Stack
yoo
yoo(…){
••who();••
}
15
yoo
who
amI
amI
amI
amI
yoo
%rsp
Stack
yoo
who
amI
amI
amI
yoo(…){
••who();••
}
who(…){
• • •amI();• • •amI();• • •
}
amI(…){
•if(…){amI()
}•
}
amI(…){
•if(…){amI()
}•
}
amI(…){
•if(…){amI()
}•
}
19
yoo
who
amI
amI
amI
amI
yoo
%rsp
Stack
yoo
who
yoo(…){
••who();••
}
who(…){
• amI();• amI();•
} amI
amI
amI
22
yoo
who
amI
amI
amI
amI
yoo
%rsp
Stack
yoo
who
amI
yoo(…){
••who();••
}
who(…){
• amI();• amI();•
}
amI(…){
•if(){amI()
}•
}
23
yoo
who
amI
amI
amI
amI
yoo%rsp
Stack
yooyoo(…){
••who();••
} amI
who
25
Stackframessupportprocedurecalls.Contents
LocalvariablesFunctionarguments(afterfirst6)ReturninformationTemporaryspace
ManagementSpaceallocatedwhen procedureisentered
“Setup”codeSpacedeallocatedbeforereturn
“Finish”code
Whynotjustgiveeveryprocedure apermanentchunkofmemorytoholditslocalvariables,etc?
26
%rspStackPointer
CallerFrame
Stack“Top”
Frameforcurrent
procedure
CodeExamples
long mult2(long a,long b){
long s = a * b;return s;
}
void multstore(long x, long y, long *dest)
{long t = mult2(x, y);*dest = t;
}
0000000000400550 :400550: mov %rdi,%rax # a400553: imul %rsi,%rax # a * b400557: retq # Return
0000000000400540 :400540: push %rbx # Save %rbx400541: mov %rdx,%rbx # Save dest400544: callq 400550 # mult2(x,y)400549: mov %rax,(%rbx) # Save at dest40054c: pop %rbx # Restore %rbx40054d: retq # Return
27
ProcedureControlFlowInstructions
Procedurecall:callq label1. Pushreturnaddressonstack2. Jumptolabel
Returnaddress: Addressofinstructionaftercall.Example:400544: callq 400550 400549: movq %rax,(%rbx)
Procedurereturn:retq1. Popreturnaddressfromstack2. Jumptoaddress
28
Call Example(step1)
0000000000400550 :400550: mov %rdi,%rax••400557: retq
0000000000400540 :••400544: callq 400550 400549: mov %rax,(%rbx)••
0x400544
0x120
•••
%rsp
0x120
0x128
0x130
%rip
29
0000000000400550 :400550: mov %rdi,%rax••400557: retq
0000000000400540 :••400544: callq 400550 400549: mov %rax,(%rbx)••
0x400550
0x118
0x400549
•••
%rsp
0x120
0x128
0x130
0x118
%rip
Call Example(step2)
30
0000000000400550 :400550: mov %rdi,%rax••400557: retq
0000000000400540 :••400544: callq 400550 400549: mov %rax,(%rbx)••
0x400557
0x118
0x400549
•••
%rsp
0x120
0x128
0x130
0x118
%rip
Return Example(step1)
31
0000000000400550 :400550: mov %rdi,%rax••400557: retq
0000000000400540 :••400544: callq 400550 400549: mov %rax,(%rbx)••
0x400549
0x120
•••
%rsp
0x120
0x128
0x130
%rip
Return Example(step2)
32
ProcedureDataFlow
Onlyallocatestackspacewhenneeded
%rdi
%rsi
%rdx
%rcx
%r8
%r9
%rax
Arg 7
• • •
Arg 8
Arg n
• • •HighAddresses
LowAddresses
Diane’sSilkDressCosts$8 9
First6argumentspassedinregisters
Returnvalue
Arg 1
Arg 6
Remainingargumentspassedonstack(inmemory)
StackFrame
35
ReturnAddress
SavedRegisters+
Local Variables
ExtraArgumentsfornextcall
…
ExtraArgumentstocallee
CallerFrame
Stackpointer%rsp
CalleeFrame
CommonStackFrame
36
ReturnAddressCallerFrame
Stackpointer%rsp
CalleeFrame
DataFlow Examples
long mult2(long a,long b){
long s = a * b;return s;
}
void multstore(long x, long y, long *dest)
{long t = mult2(x, y);*dest = t;
}
0000000000400550 :# a in %rdi, b in %rsi400550: movq %rdi,%rax # a 400553: imul %rsi,%rax # a * b# s in %rax400557: retq # Return
0000000000400540 :# x in %rdi, y in %rsi, dest in %rdx• • •400541: movq %rdx,%rbx # Save dest400544: callq 400550 # mult2(x,y)# t in %rax400549: movq %rax,(%rbx) # Save at dest• • •
37
Example:increment
long increment(long* p, long val) {long x = *p;long y = x + val;*p = y;return x;
}
increment:movq (%rdi), %raxaddq %rax, %rsimovq %rsi, (%rdi)ret
Register Use(s)%rdi Argumentp
%rsi Argumentval, y
%rax x,Returnvalue
38
ProcedureCallExample(initialstate)
39
call_incr:subq $16, %rspmovq $240, 8(%rsp)movl $61, %esileaq 8(%rsp), %rdicall incrementaddq 8(%rsp), %raxaddq $16, %rspret
long call_incr() {long v1 = 240;long v2 = increment(&v1, 61);return v1+v2;
}
InitialStackStructure
•••
Returnaddr %rsp
%rsi
%rdi
%rax
ProcedureCallExample(step1)
40
call_incr:subq $16, %rspmovq $240, 8(%rsp)movl $61, %esileaq 8(%rsp), %rdicall incrementaddq 8(%rsp), %raxaddq $16, %rspret
StackStructure
Allocatespaceforlocalvars
long call_incr() {long v1 = 240;long v2 = increment(&v1, 61);return v1+v2;
}
•••
Returnaddr
240UnusedCompilerallocatedextraspaceforalignmentà
Spaceforv1à
%rsp
%rsi
%rdi
%rax
ProcedureCallExample(step2)
call_incr:subq $16, %rspmovq $240, 8(%rsp)movl $61, %esileaq 8(%rsp), %rdicall incrementaddq 8(%rsp), %raxaddq $16, %rspret
StackStructure
41
Setupargs forcalltoincrement
long call_incr() {long v1 = 240;long v2 = increment(&v1, 61);return v1+v2;
}
Aside: movl isusedbecause61isasmallpositivevaluethatfitsin32bits.Highorderbitsof%rsi getsettozeroautomatically.Ittakesonelessbytetoencodeamovl thanamovq.
•••
Returnaddr
240Unused
%rsp
%rsi
61
%rdi
&v1
%rax
ProcedureCallExample(step3)
42
call_incr:subq $16, %rspmovq $240, 8(%rsp)movl $61, %esileaq 8(%rsp), %rdicall incrementaddq 8(%rsp), %raxaddq $16, %rspret
long call_incr() {long v1 = 240;long v2 = increment(&v1, 61);return v1+v2;
}
StackStructure
increment:movq (%rdi), %raxaddq %rax, %rsimovq %rsi, (%rdi)ret
•••
Returnaddr
240Unused
Returnaddr
%rsp
%rsi
61
%rdi
&v1
%rax
ProcedureCallExample(step4)
43
call_incr:subq $16, %rspmovq $240, 8(%rsp)movl $61, %esileaq 8(%rsp), %rdicall incrementaddq 8(%rsp), %raxaddq $16, %rspret
long call_incr() {long v1 = 240;long v2 = increment(&v1, 61);return v1+v2;
}
StackStructure
increment:movq (%rdi), %rax # x = *paddq %rax, %rsi # y = x+61movq %rsi, (%rdi) # *p = yret
•••
Returnaddr
301Unused
Returnaddr
%rsp
%rsi
301
%rdi
&v1
%rax
240
long increment(long* p, long val) {long x = *p;long y = x + val;*p = y;return x;
}
ProcedureCallExample (step5)
44
StackStructure
•••
Returnaddr
301Unused
call_incr:subq $16, %rspmovq $240, 8(%rsp)movl $61, %esileaq 8(%rsp), %rdicall incrementaddq 8(%rsp), %raxaddq $16, %rspret
increment:movq (%rdi), %rax # x = *paddq %rax, %rsi # y = x+61movq %rsi, (%rdi) # *p = yret
long call_incr() {long v1 = 240;long v2 = increment(&v1, 61);return v1+v2;
}
%rsp
%rsi
301
%rdi
&v1
%rax
240
ProcedureCallExample(step6)
StackStructure
45
long call_incr() {long v1 = 240;long v2 = increment(&v1, 61);return v1+v2;
}
call_incr:subq $16, %rspmovq $240, 8(%rsp)movl $61, %esileaq 8(%rsp), %rdicall incrementaddq 8(%rsp), %raxaddq $16, %rspret
•••
Returnaddr
301Unused
Update %rax:v1+v2
%rsp
%rsi
301
%rdi
&v1
%rax
541
ProcedureCallExample(step7)
StackStructure
46
De-allocatespaceforlocalvars
long call_incr() {long v1 = 240;long v2 = increment(&v1, 61);return v1+v2;
}
call_incr:subq $16, %rspmovq $240, 8(%rsp)movl $61, %esileaq 8(%rsp), %rdicall incrementaddq 8(%rsp), %raxaddq $16, %rspret
•••
Returnaddr
301Unused
%rsp
%rsi
301
%rdi
&v1
%rax
541
ProcedureCallExample(returnfromcall_incr)
48
main:...call call_incr...
StackStructurelong call_incr() {
long v1 = 240;long v2 = increment(&v1, 61);return v1+v2;
}
•••
Returnaddr
301Unused
%rsp
%rsi
301
%rdi
&v1
%rax
541
RegisterSavingConventionsyoo calls who:Caller Callee
Willregistercontentsstillbethereafteraprocedurecall?
Conventions:CallerSave
Caller savestemporaryvaluesinitsframebeforecallingCallee Save
Callee savestemporaryvaluesinitsframe beforeusing
yoo:• • •movq $12345, %rbxcall whoaddq %rbx, %rax• • •ret
who:• • •addq %rdi, %rbx• • •ret
49
?
x86-6464-bitRegisters:UsageConventions
51
%rax
%rbx
%rcx
%rdx
%rsi
%rdi
%rsp
%rbp
%r8
%r9
%r10
%r11
%r12
%r13
%r14
%r15Callee saved Callee saved
Callee saved
Callee saved
Callee saved
Callersaved
Callee saved
Stackpointer
CallerSaved
Returnvalue– Callersaved
Argument#4– Callersaved
Argument#1– Callersaved
Argument#3– Callersaved
Argument#2– Callersaved
Argument#6– Callersaved
Argument#5– Callersaved
Callee-SavedExample(step1)
call_incr2:pushq %rbxsubq $16, %rspmovq %rdi, %rbxmovq $240, 8(%rsp)movl $61, %esileaq 8(%rsp), %rdicall incrementaddq %rbx, %raxaddq $16, %rsppopq %rbxret
long call_incr2(long x) {long v1 = 240;long v2 = increment(&v1, 61);return x+v2;
}
InitialStackStructure
%rsp
...
Rtn address
ResultingStackStructure
240
Unused %rsp
...
Rtn address
%rsp+8
Saved%rbx
52
Callee-SavedExample(step2)
call_incr2:pushq %rbxsubq $16, %rspmovq %rdi, %rbxmovq $240, 8(%rsp)movl $61, %esileaq 8(%rsp), %rdicall incrementaddq %rbx, %raxaddq $16, %rsppopq %rbxret
Pre-returnStackStructure
%rsp
...
Rtn address
StackStructure
240
Unused %rsp
...
Rtn address
%rsp+8
Saved%rbx
53
long call_incr2(long x) {long v1 = 240;long v2 = increment(&v1, 61);return x+v2;
}
APuzzle
*p = d;return x - c;
movsbl %dl,%edxmovl %edx,(%rsi)movswl %di,%edisubl %edi,%ecxmovl %ecx,%eax
WritetheCfunctionheader,types,andorderofparameters.
Cfunctionbody:
assembly:
movsbl =movesign-extendingabytetoalong(4-byte)movswl =movesign-extendingaword(2-byte)toalong(4-byte)
/* Recursive popcount */long pcount_r(unsigned long x) {
if (x == 0) {return 0;
} else {return (x & 1)
+ pcount_r(x >> 1);}
}
RecursiveFunctionpcount_r:
movl $0, %eaxtestq %rdi, %rdije .L6pushq %rbxmovq %rdi, %rbxandl $1, %ebxshrq %rdicall pcount_raddq %rbx, %raxpopq %rbx
.L6:rep; ret
55
RecursiveFunction:BaseCasepcount_r:
movl $0, %eaxtestq %rdi, %rdije .L6pushq %rbxmovq %rdi, %rbxandl $1, %ebxshrq %rdicall pcount_raddq %rbx, %raxpopq %rbx
.L6:rep; ret
Register Use(s) Type%rdi x Argument
%rax Returnvalue Returnvalue
56
/* Recursive popcount */long pcount_r(unsigned long x) {
if (x == 0) {return 0;
} else {return (x & 1)
+ pcount_r(x >> 1);}
}
Trick becausesomeHWdoesn’tlikejumpingtoret
RecursiveFunction:RegisterSavepcount_r:
movl $0, %eaxtestq %rdi, %rdije .L6pushq %rbxmovq %rdi, %rbxandl $1, %ebxshrq %rdicall pcount_raddq %rbx, %raxpopq %rbx
.L6:rep; ret
←%rsp
...
Saved%rbx
57
/* 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
RecursiveFunction:CallSetuppcount_r:
movl $0, %eaxtestq %rdi, %rdije .L6pushq %rbxmovq %rdi, %rbxandl $1, %ebxshrq %rdicall pcount_raddq %rbx, %raxpopq %rbx
.L6:rep; ret
58
/* 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 Recursivearg
%rbx x & 1 Callee-saved ←%rsp
...
rtn
Saved%rbx
rtn
...
RecursiveFunction:Callpcount_r:
movl $0, %eaxtestq %rdi, %rdije .L6pushq %rbxmovq %rdi, %rbxandl $1, %ebxshrq %rdicall pcount_raddq %rbx, %raxpopq %rbx
.L6:rep; ret
59
/* 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 callreturnvalue –
←%rsp
...
rtn
Saved%rbx
RecursiveFunction:Resultpcount_r:
movl $0, %eaxtestq %rdi, %rdije .L6pushq %rbxmovq %rdi, %rbxandl $1, %ebxshrq %rdicall pcount_raddq %rbx, %raxpopq %rbx
.L6:rep; ret
60
/* 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 Returnvalue
...
rtn
Saved%rbx ←%rsp
rtn
Saved%rbx
RecursiveFunction:Completion
pcount_r:movl $0, %eaxtestq %rdi, %rdije .L6pushq %rbxmovq %rdi, %rbxandl $1, %ebxshrq %rdicall pcount_raddq %rbx, %raxpopq %rbx
.L6:rep; ret
61
/* 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 Returnvalue
...←%rsp
x86-64stackstorageexample(1)
62
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,%rspmovq $1,16(%rsp) # x1movl $2,24(%rsp) # x2movw $3,28(%rsp) # x3movb $4,31(%rsp) # x4• • •
Returnaddresstocallerofcall_proc ←%rsp
x86-64stackstorageexample(2)Allocatelocalvars
63
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,%rspmovq $1,16(%rsp) # x1movl $2,24(%rsp) # x2movw $3,28(%rsp) # x3movb $4,31(%rsp) # x4• • •
Returnaddresstocallerofcall_proc
←%rsp
x3x4 x2
x1
8
16
24
x86-64stackstorageexample(3)setupargs toproc
64
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 # &x2leaq 16(%rsp),%rsi # &x1leaq 31(%rsp),%rax # &x4movq %rax,8(%rsp) # ...movl $4,(%rsp) # 4leaq 28(%rsp),%r9 # &x3movl $3,%r8d # 3movl $2,%edx # 2movq $1,%rdi # 1call proc• • •
Argumentspassedin(inorder):rdi,rsi,rdx,rcx,r8,r9
Returnaddresstocallerofcall_proc
←%rsp
x3x4 x2
x1
8
16
24
Arg 8
Arg 7
call_proc:• • •movswl 28(%rsp),%eax # x3movsbl 31(%rsp),%edx # x4subl %edx,%eax # x3-x4cltq # sign-extend %eax->raxmovslq 24(%rsp),%rdx # x2addq 16(%rsp),%rdx # x1+x2imulq %rdx,%rax # *addq $32,%rspret
x86-64stackstorageexample(4)aftercalltoproc
65
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);
}
Returnaddresstocallerofcall_proc
←%rsp
x3x4 x2
x1
8
16
24
Arg 8
Arg 7
call_proc:• • •movswl 28(%rsp),%eaxmovsbl 31(%rsp),%edxsubl %edx,%eaxcltqmovslq 24(%rsp),%rdxaddq 16(%rsp),%rdximulq %rdx,%raxaddq $32,%rspret
x86-64stackstorageexample(5)deallocatelocalvars
66
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);
}
←%rspReturnaddresstocallerofcall_proc
ProcedureSummarycall, ret, push, popStackdisciplinefitsprocedurecall/return.*
IfPcallsQ:Q(andcallsbyQ)returnsbeforeP
Conventionssupportarbitraryfunctioncalls.Register-saveconventions.Stackframesavesextraargs orlocalvariables.Resultreturnedin%rax
*Take251tolearnaboutlanguageswhereitdoesn't. 67
ReturnAddress
SavedRegisters+
Local Variables
ExtraArgumentsfornextcall
…
ExtraArgumentstocallee
CallerFrame
Stackpointer%rsp
CalleeFrame
128-byteredzone
%rax
%rbx
%rcx
%rdx
%rsi
%rdi
%rsp
%rbp
%r8
%r9
%r10
%r11
%r12
%r13
%r14
%r15Callee saved Callee saved
Callee saved
Callee saved
Callee saved
Callersaved
Callee saved
Stackpointer
CallerSaved
Returnvalue– Callersaved
Argument#4– Callersaved
Argument#1– Callersaved
Argument#3– Callersaved
Argument#2– Callersaved
Argument#6– Callersaved
Argument#5– Callersaved