Static and Dynamic links in Activation Records

 

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.

 

  1. The dynamic link points to the top of the ARI of the caller.
  2. The static link points to the bottom of the ARI of the callee's static parent.

 

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.