When a function is running, its status is recorded in an activation
record.
Contents.
Parameter storage.
Local variables.
Temporary storage needed by the compiler.
For methods of an object, a this pointer.
Assembler-level bookkeeping.
The PC value to jump to upon function return.
Other saved CPU registers.
Static pointer (or context pointer).
Points to the activation record needed to find non-local references.
The chain of static pointers gives the full referencing
environment.
Omitted when
The language has dynamic scope, which nothing does anymore.
Methods may not be nested, so the only non-local
references are global. This is the case for C and C++.
Dynamic pointer.
Points to the activation record of the caller.
Needed to implement returns, exceptions, and (if any) dynamic scope.
May be omitted if the value can be computed from the storage
structure.
Statically-organized.
Each function has a statically-allocated block of memory which
is used as an activation record while the function is running.
Used on some early languages (notably FORTRAN), but now considered
obsolete.
Will not support recursion, since a function can only have at most
one activation record at a time.
Organized as a stack.
When a function is called, a new activation record is pushed on
the stack. Popped after return.
Stacks may be linked, but are usually a linear block of memory
with a top pointer.
Usually considerable hardware support for this model.
Typical function call:
Push the arguments onto the stack.
Push the hardware return address (PC) into the frame and
jump to the function address.
Push the local and temp variables onto the stack.
Establish the static and dynamic pointers (see below).
Frame may be in different places on different calls.
Variables may not take the same location each time.
Like this.
Multiple copies of some variables may exist.
Important for recursive functions.
Finding a variable.
The compiler does not know the l-value of a variable, but
knows its offset from the start of its AV.
It knows how to find the start of the AV at run time.
To find an l-value, the compiler generates “find frame, add
constant”.
For local references, this is just "stack pointer + offset".
On most CPUs, the stack pointer is in a register, and these variables
can be fetched or stored in one instruction.
Finding a non-local variable.
In a language like C, there is only one non-local scope (the global
one). Offset from that instead of the stack pointer.
Note: Gcc (but not g++)
allows nested functions
as
an extension.
If there are nested enclosing scopes
The static link in each AV points to the AV of the
statically-enclosing scope.
The compiler can compute the difference in scope depth between
reference and declaration.
To find a non-local l-value, produce a translation
“follow the local pointer n times to give the correct
frame, then offset by the constant.”
Establishing the static pointer when fa calls fb.
From fa, the sequence of scopes searched to find a reference
can be given
Sn,Sn−1,Sn−2,…,S0,
where Sn is the scope defined by the body of fa, from which
fb is called.
The activation record for fa is An, starting the
static chain of activation records
An,An−1,An−2,…,A0. These frames hold
the variables in each of the scopes.
For fa to call fb, fb must visible, so it must appear in one of the
the scopes on that list. Say it appears in
Si,n≤i≤0.
Si is the scope that contains fb, but its local scope
(the scope of fb's body) is a different one, which we call
SFB. So fb's chain should be
AFB,Ai,Ai−1,…,A0.
Since subscripts are nesting depths, i−n
is just the difference in nesting depth between the call (within
fa) and the called function, fb. To find Ai,
follow the static chain from AFBi−n times to reach
Ai. This difference may be zero, in which case
Ai is AFB.
The static link of AFB should point to this Ai.
This creates the correct search chain,
AFB,Ai,Ai−1,…,A0.
Closures.
When a closure is created, it includes a static link to represent
the non-local part of its referencing environment.
Insert the same static link into the closure when you create it
that you would have put in its frame if you were calling it there
instead.
When you actually do call the closure, simply copy that link into
its new frame.
Languages which need a static link include Pascal, Ada and
Java (sort of).