Languages that allow nested subprograms like Pascal, Modula, Ada, and even those that don't like C, C++ and Java have run-time activation of subprograms that are managed with a stack of Activation Record Instances (ARIs). Two pointers are used in activation records to enable management of the stack, and efficient access to non-local variables.
For example, consider the following subprogram structure:
program MAIN_2;
procedure BIGSUB;
procedure SUB1;
begin … end SUB1;
procedure SUB2;
procedure SUB3;
begin … end SUB3;
begin … end SUB2;
begin … end BIGSUB;
begin … end MAIN_2;
The static structure can be drawn as a tree:
When a calling sequence is to be managed, then the run-time system needs to put appropriate links in the ARI of each subprogram in order to manage the stack. Let us consider the calling sequence:
MAIN_2 calls BIGSUB calls SUB calls SUB3 calls SUB1.
Dynamic links are easy, since the dynamic link of a subprogram always points to the caller, which immediately precedes it on the stack. This link is used to reset top-of-stack when a subprogram exits. The stack at the end of the sequence simply has each ARI pointing to the previous one. After the sequence it is:
Notice that the ARI for MAIN_2 does not need a dynamic link
since it is the first on the stack.
Static links are set in the following way. The job is to
find the most recent occurrence of an ARI of the static parent of the
subprogram being called. This could be done by following the dynamic links from
one ARI to the next checking for some piece of data that uniquely identifies
the ARI. However, there is a simpler method using the nesting depth of
the subprograms. The compiler can determine this depth because it has had to
analyze the nesting of subprograms. In the example, MAIN_2 has depth 0, BIGSUB
has depth 1, SUB1 and SUB2 have depth 2 and SUB3 has depth 3. When MAIN_2 calls
BIGSUB or when BIGSUB calls SUB2 or SUB2 calls SUB3, the static links
essentially follow the dynamic ones since the static parent is always the same
as the caller. However, when SUB3 calls SUB1, the static link from SUB1 must
point to BIGSUB, not to SUB3 (refer to the static link tree above). This is
achieved by noting that the difference in the static depth of the caller (SUB3)
and the static depth of the declarer (BIGSUB) is 3 -1, or 2. If we take two
hops on the static chain from SUB3, we get to BIGSUB. Hence SUB1's static
parent is BIGSUB, and its static link points ot BIGSUB. Since SUB3 is already
on the stack, we can follow this chain to BIGSUB without direct knowledge of
SUB1's static parent.
The stack with static links is thus:
The actual access to non-locals starts with the static link,
since this is the static parent of the callee, but if there are references to
non-locals outside this scope, then they must be searched for by following
further static links. For instance, if a variable declared in MAIN_2 is
referenced in SUB1, then the static chain from SUB1 to BISGUB and then from
BIGSUB to MAIN_2 must be followed. Once the correct ARI has been located, then
the local offset of the variable within the ARI is used to find the variable's
location.