Compilation 2016 Activation Records Aslan Askarov aslan@cs.au.dk Revised from slides by E. Ernst
(Abstract) computer organization • Program memory } • code segment – contains program text size fixed prior to runtime • data segment – contains static program data (globals) } • stack – for locals and function arguments size changes at runtime • heap – for dynamically allocated memory • Processor registers • Operations for moving data between registers/memory
Call stack of a program contiguous region in memory Higher addresses in memory that the program can use Purpose of the stack: • store local variables the stack grows from higher • pass arguments memory addresses to low • store return values ones (could be otherwise • save registers depending on architecture) • … Low addresses in memory stack limit – set by OS
Call stack of a program contiguous region in memory Higher addresses in memory that the program can use Purpose of the stack: • store local variables the stack grows from higher • pass arguments memory addresses to low • store return addresses ones (could be otherwise • save registers depending on architecture) • … Q: what happens if we push past beyond stack limit? Low addresses in memory stack limit – set by OS
Stack frames Function 1 calls Function 2 that calls Function 3 active functions, because they haven’t returned yet Function 1 Function 1 Idea : the maximum amount of memory that each function Function 1 needs for its locals, temps, etc can be (usually) pre- Function 2 computed by the compiler Function 2 Function 2 Let’s increase the stack by that much at once instead Function 2 of many small increases Function 2 Call the region of the stack corresponding to each Function 3 active function that function’s stack frame ( also called Function 3 activation record) Function 3 Function 3 stack manipulations by each of the functions
Stack frames Function 1 calls Function 2 that calls Function 3 active functions, because they haven’t returned yet Function 1 activation record (or stack frame) for Function 1 Function 1 Function 1 Function 2 Function 2 stack frame for Function 2 Function 2 Function 2 Function 2 Function 3 Function 3 stack frame for Function 3 Function 3 Function 3 stack manipulations by each of the functions
Frame pointer and Stack pointer activation record for Function 1 Stack pointer (SP): points to the “top” FP of the stack Frame pointer (FP): the value of SP at the activation time the frame got activated record for Function 2 SP Not allocated: garbage
Frame layout: calling conventions • Cross-language calls argument 2 argument 1 important: using libraries FP • Reasonable to follow a returnAddr standard: ‘calling convention’ localvar1 localvar2 • Specifies stack frame layout, … storedR1 register usage, routine entry, … temp1 exit code … SP • Likely C bias Not allocated: garbage
Typical frame layout (prev. frame) • Fits RISC architectures (such as arg_k … MIPS) well arg_1 staticLink FP • Note ‘ staticLink ’… localVar_1 … • Consider o ff sets from FP and SP: localVar_m returnAddr are all known at compile time? temp_1 … • FP could be virtual, if frame size is temp_p saved_R1 fixed … saved_Rt … (args for next) SP
Textbook Tiger frame layout old FP (prev. frame) • Fits x86, with simple register arg_k … usage strategy arg_1 staticLink FP • Worth noting saved_FP localVar_1 • return address pushed … localVar_m automatically by call returnAddr temp_1 instruction … temp_p • FP non-virtual, always saved SP nextArg_k • SP adjusted at runtime: nextArg_2 SP arguments pushed SP nextArg_1
Textbook Tiger frame slots old FP Memory[saved_FP], Memory[staticLink]? (prev.frame) ... Memory[FP+8*(2+k)] arg_k ... Memory[FP+8*(2+1)] arg_1 Memory[FP+16] staticLink Memory[FP+8] returnAddr Top of frame: FP saved_FP Memory[FP] known early Memory[FP-8*1] localVar_1 ... Memory[FP-8*m] localVar_m Memory[FP-8*(m+1)] temp_1 Bottom: known ... Memory[FP-8*(m+p)] later temp_p SP SP ... SP Memory[FP-who_cares] SP (args for next)
The Frame Pointer • Relative concepts: caller/callee frames, routines • Allocate new frame (same figure): • push FP FP (prev.frame) • FP := SP • SP := SP - frameSize ... SP • If SP fixed, may replace FP by SP - frameSize (‘virtual’ FP)
Saving Registers • Re: a frame/routine may be a caller or a callee • Roles for registers: ‘Caller-save’ vs. ‘callee-save’ • E.g. on x86-64 System V ABI we have • RBP , RBX, and R12–R15 are callee-save registers, i.e., preserved across function calls • “Don’t mess with R12-15” / “Don’t rely on others” • Scenario: Nice if value in R11 is dead before call • Scenario: Put long-lived value in r12 (think: loop!)
Passing Parameters • Pre-1960: Use globals, no recursion • 1970’ies: Pass parameters on stack • Later: 4-6 parameters in registers, rest on stack • Experience: Few routines >6 parameters! • Our approach: System V ABI (di ff erent from textbook!) • 6 params in registers, the remaining on stack
Why is register passing useful?! (prev.frame) • Scenario: • Calling f( a 1 .. a n ) which calls g( z ) ... saved_Ri • As much as possible kept in registers ... staticLink • Tempting claim: “No di ff erence!” localVar_1 f frame • Concept: Leaf routines ... localVar_m • How rare? returnAddr ... • Most invocations are leaves! saved_a1 ... • May not even need stack frame staticLink localVar_1 g frame ...
Managing Return Addresses • Return address not statically known (.. why?) • Solution: Store return address • Traditional approach: Push at call instruction • Alternatives: store in register at call instruction • Non-leaf routines still have to write to the stack
example3.c from Aleph One tutorial
Forcing Memory Storage • Using registers a good default, may fail... • Address of variable taken (‘&’, pass-by-reference) • Variable used from nested function • Variable too large for register (use several?) • Pointer arithmetics used (C arrays) • Spilling
Static links: concept • The previous frame is not necessarily the one that is lexically scoped • Record information about enclosing frames • Pass nearest enclosing frame as hidden argument (static link) • Keep ‘display’: global array of currently nearest frame at all nesting levels • Lambda lifting: lots of arguments • We use static links
Static Links: Example Stack type tree = {key: string, left: tree, right: tree} [prettyprint] function prettyprint(tree: tree): string = let var output := " " [show] function write(s: string) = output := concat(output, s) function show(n: int, t: tree) = ⋮ let wanted function indent(s: string) = ⋮ ( for i:=1 to n do write(" ") ; output := concat(output,s) ; write("\n")) [show] in if t=nil then indent(".") else ( indent(t.key) ; show(n+1, t.left) [indent] ; show(n+1, t.right)) end saved in FP [write] show(0,tree); output
Static Links: Example Stack type tree = {key: string, left: tree, right: tree} [prettyprint] function prettyprint(tree: tree): string = let var output := " " [show] function write(s: string) = output := concat(output, s) function show(n: int, t: tree) = ⋮ let wanted function indent(s: string) = static link ⋮ ( for i:=1 to n do write(" ") ; output := concat(output,s) ; write("\n")) [show] in if t=nil then indent(".") else ( indent(t.key) ; show(n+1, t.left) [indent] ; show(n+1, t.right)) end saved in FP [write] show(0,tree); output
Static Links: Call Nested type tree = {key: string, left: tree, right: tree} Case “call nested”: function prettyprint(tree: tree): string = let Static link for show var output := " " from prettyprint is the frame of function write(s: string) = prettyprint itself, etc. output := concat(output, s) function show(n: int, t: tree) = let function indent(s: string) = ( for i:=1 to n do write(" ") ; output := concat(output,s) ; write("\n")) SL = FP in if t=nil then indent(".") else ( indent(t.key) ; show(n+1, t.left) ; show(n+1, t.right)) end in SL = FP show(0,tree); output
Static Links: Call Same type tree = {key: string, left: tree, right: tree} Case “call same”: function prettyprint(tree: tree): string = let Static link for show var output := " " from show is the given static link function write(s: string) = output := concat(output, s) function show(n: int, t: tree) = let function indent(s: string) = ( for i:=1 to n do write(" ") ; output := concat(output,s) ; write("\n")) in if t=nil then indent(".") else ( indent(t.key) SL = SL ; show (n+1, t.left) ; show (n+1, t.right)) end in show(0,tree); output
Recommend
More recommend