IAE Academy

GATE CS

Runtime environments

Activation records and storage allocation

Runtime environments describe how a compiled program is laid out in memory while it runs: where code, static data, the stack and the heap live, what an activation record holds, and how a procedure finds its parameters, locals and non-local names. Typical questions ask you to trace the stack through calls, count activations, follow access links, and predict the output under different parameter-passing and scoping rules.

0 previous-year questions · 0 solved · 5 practice questions

Storage organisation and activation records

The compiler asks the operating system for a block of memory in which the target program runs. It is divided into areas with different lifetimes. Code and static data have a size fixed at compile time. The stack and the heap change size at run time; by common convention they sit at opposite ends of the free space and grow towards each other, the stack usually growing towards lower addresses.

Area

Holds

Size known when

Lifetime

Code

Machine instructions

Compile time

Whole run

Static data

Global variables, static variables, constants

Compile time

Whole run

Stack

Activation records of live procedure calls

Run time (grows and shrinks)

One call, last in first out

Heap

Data created on request (malloc, new)

Run time

Until freed or collected

Every execution of a procedure is an activation. The activation tree records the calls of a run: one node per activation, the root is the activation of the main program, and a node's children are the activations it calls, left to right in the order of the calls. Calls are properly nested: a procedure returns only after every procedure it called has returned. So at any moment the live activations are exactly the nodes on the path from the root to the current node, and the stack holds their records in that order.

The information for one activation is kept in an activation record (frame). The usual fields, in one common layout from the caller's end to the callee's end (the exact order varies between compilers), are:

Field

Purpose

Actual parameters

Values or addresses supplied by the caller

Returned value

Where the callee leaves its result for the caller

Control link

Points to the caller's activation record, so it can be restored on return

Access link

Points to the activation record of the lexically enclosing procedure, used to reach non-local names

Saved machine status

Return address and the register values the caller needs back

Local data

Variables declared in the procedure

Temporaries

Intermediate values of expression evaluation

EXAM TIP

The control link follows the dynamic (calling) chain; the access link follows the static (lexical nesting) chain. They are different pointers and often point to different records. A language without nested procedure definitions (such as C) needs no access link.

EXAM TIP

Not in an activation record: global variables and static locals (they are in static data) and objects created on the heap (a record may hold only a pointer to one).

Run-time stack, calling and return sequences

LIVE ACTIVATIONS

Holds under stack allocation, where every live activation has exactly one record on the stack. Counts the records on the stack at one instant; the maximum over the run is the peak stack depth. Includes the main program's record.

A calling sequence is the code that sets up an activation; a return sequence undoes it. The work is split between caller and callee. Below is a typical division of the work; real conventions differ in detail.

Calling sequence (in order)

  1. Caller evaluates the actual parameters and places them at the start of the callee's record.

  2. Caller saves the return address and the machine status it needs, and sets the callee's control link to its own record.

  3. Caller transfers control to the callee.

  4. Callee saves registers it will overwrite, sets the frame pointer to its own record and allocates space for locals and temporaries on the stack.

  5. Callee runs its body.

Return sequence (in order)

  1. Callee places the return value in the agreed field or register.

  2. Callee pops its locals and temporaries and restores the saved registers.

  3. Callee jumps to the return address.

  4. Caller picks up the return value and continues; the callee's record is gone from the stack.

Variable-length data (for example an array whose size is decided on entry) can still be placed on the stack, after the fixed-size part of the record, and is reached through a pointer stored in the fixed part. Because the record is popped on return, nothing in it may be used after the call ends.

WORKED EXAMPLE

A program runs main, which calls f(3). The procedure f(n) does the following: if , it calls f(n-1) and then calls g(); if it does nothing. g calls no one. Draw the activation tree in call order, and find the total number of activations (including main) and the peak number of activation records on the stack.

Solution
  1. Calls in order: main, f(3), f(2), f(1), f(0). Then f(0) returns, f(1) calls g, f(1) returns, f(2) calls g, f(2) returns, f(3) calls g.

  2. Tree: main has child f(3); f(3) has children f(2), g; f(2) has children f(1), g; f(1) has children f(0), g; f(0) is a leaf.

  3. Count: main 1, f four times (n = 3, 2, 1, 0), g three times. Total = 1 + 4 + 3 = 8.

  4. Stack at the deepest point is the longest root-to-node path: main, f(3), f(2), f(1), f(0) has 5 nodes. The path main, f(3), f(2), f(1), g also has 5. No path is longer.

Answer: 8 activations in total; peak stack of 5 activation records.

Static, stack and heap allocation

Static allocation binds every name to a fixed address at compile time. Stack allocation gives each activation its own record on the run-time stack. Heap allocation hands out and takes back memory in any order, at the program's request or by the language's automatic management.

Property

Static

Stack

Heap

Addresses fixed at compile time

Yes

Only offsets within a record

No

Recursion

Not possible (one record per procedure)

Possible (one record per activation)

Possible if activation records are placed in the heap

Local values kept between calls

Yes

No, record is popped

Heap data persists until freed, independent of calls

Size of data decided at run time

No

Yes (variable-length data after the fixed part of the record), but it lives only as long as the activation

Yes

Data outlives the creating procedure

Yes

No

Yes

Allocation and release

None at run time

Push and pop, very cheap

Search a free list; may fragment

Dangling reference risk

None

A pointer to a local after return

A pointer after free

Stack allocation is valid when a procedure's locals are not needed after it returns and no activation outlives the one that created it. If a procedure may return a pointer to a local, or if data must be created by one call and used after it ends, those data must live in the heap or in static storage. The heap manager keeps a list of free blocks and picks one by a policy such as first fit (first block large enough) or best fit (smallest block large enough). Freed blocks next to each other can be merged (coalescing); free memory split into small unusable pieces is external fragmentation.

Language feature

Run-time support it needs

Recursion

A separate record per activation: stack (or heap) allocation; static allocation cannot support it

Data whose size is known only at run time

Stack (if it dies with the call) or heap

Data that outlives the call that created it

Heap (or static storage)

Nested procedures with static scoping

Access links or a display

Dynamic scoping

A search of the calling chain (deep access) or a central table of current bindings (shallow access)

EXAM TIP

A static (static in C) local variable is stored in static data, not in the activation record. That is why it keeps its value between calls, even in a language that otherwise uses the stack.

Under static (lexical) scoping, a use of a name refers to the declaration found by searching the enclosing blocks in the program text, innermost first. Under dynamic scoping, it refers to the most recent binding made by a still-active call, found by searching the calling chain.

Example of the difference:

int x = 10;
void g() { printf("%d", x); }
void f() { int x = 20; g(); }
int main() { f(); return 0; }

With static scoping g sees the global x and prints 10. Read as if the language used dynamic scoping (C itself uses static scoping), g sees the x of its caller f and prints 20.

Convention for nesting depth. The outermost (main) program has depth 1, a procedure declared directly inside it has depth 2, and so on. A variable's depth is the depth of the procedure that declares it. The formulas below use only differences of depths, so they give the same counts if the outermost level is called depth 0 instead.

ACCESS-LINK HOPS FOR A VARIABLE

Static scoping with access links. A procedure at nesting depth uses a variable whose nearest enclosing declaration is at depth . Start at the current record and follow access links this many times; 0 means the variable is local.

ACCESS LINK OF A CALLEE

Procedure (depth ) calls procedure (depth ), which is visible from , so . Follow this many access links from 's record; the record reached is the most recent activation of 's lexical parent, and 's access link is set to it. Cases: ( declared inside ) gives 0, so the link points to itself; gives 1, so the link equals 's own access link; gives links.

A display is an array with one entry per nesting depth. points to the most recent live activation record of a procedure at depth . A name declared at depth is reached in one step through , so the cost of a non-local access does not grow with the depth difference. On entry to a procedure at depth , the old value of is saved in the new record and is set to the new record; on return the saved value is restored. A display trades a little work at each call for faster non-local access.

Implementing dynamic scoping. Deep access: to find a name, search the records along the control-link chain, starting from the current one, for the first record that declares it; the search can be long. Shallow access: keep one central storage slot per name holding its current value; a procedure that declares the name saves the old value on entry and restores it on exit, so every access takes one step.

EXAM TIP

A recursive call to the same procedure gives : one hop, so the new record's access link equals the caller's access link (same lexical parent), not the caller's record. Following the control link instead of the access link is the usual mistake in questions on nested procedures.

Parameter passing

Mechanism

What the callee gets

Effect on the caller's variable

Notes

Call by value

A copy of the value

Unchanged

Callee's parameter is an ordinary local

Call by reference

The address of the actual (an lvalue)

Changes at once; aliases are possible

If the actual is an expression, the address of a temporary holding its value is passed

Call by value-result (copy-restore)

A copy; at return the final value is copied back

Changes only at return

The copy-back address is usually computed at the call; the result depends on the order of copying back when two formals are bound to the same variable

Call by name

The actual expression is re-evaluated at every use of the formal

Changes when the formal is assigned

Implemented by passing a small routine (thunk) that evaluates the actual in the caller's environment

In call by reference, the address of the actual is computed once, at the call. In call by name, the expression is evaluated afresh each time the formal is used, using the current values of the variables in it. The two can differ whenever a variable used in the actual changes during the call, for example an array index that the callee assigns, or a global that the callee changes.

WORKED EXAMPLE

Array a is {5, 7, 2, 9, 4, 6, 1, 3} (indices 0 to 7) and i = 0. The procedure swap(x, y) is t = x; x = y; y = t;. Find i, a[0] and a[5] after swap(i, a[i]) under (a) call by reference and (b) call by name.

Solution
  1. (a) Reference: at the call, x is the address of i, y is the address of a[0] (since i is 0 then). The address bound to y never changes afterwards.

  2. t = x gives t = 0. x = y puts the value of a[0], which is 5, into i, so i = 5. y = t puts 0 into a[0].

  3. Result for (a): i = 5, a[0] = 0, a[5] = 6 (untouched).

  4. (b) Name: x is the expression i, y is the expression a[i], both re-evaluated at each use.

  5. t = x gives t = 0. x = y: evaluating y now is a[i] = a[0] = 5, so i = 5. y = t: y is now a[i] = a[5], so a[5] = 0.

  6. Result for (b): i = 5, a[0] = 5, a[5] = 0.

Answer: (a) i = 5, a[0] = 0, a[5] = 6. (b) i = 5, a[0] = 5, a[5] = 0.

EXAM TIP

Value-result and reference give the same result unless the callee reads or writes the actual through some other name while running (a global, or a second formal bound to the same variable). When two formals are bound to one variable, copying back left to right means the last formal's value wins; check which order the question states.

Garbage collection in brief

Heap data that the program can no longer reach is garbage. In a language with manual freeing, unreachable data that is never freed is a memory leak; freeing data that is still in use leaves a dangling pointer. A garbage collector reclaims unreachable heap data automatically. An object is reachable if it can be got to from a root (static data, stack variables, registers) by following pointers.

Method

Idea

Strength

Weakness

Reference counting

Each object holds a count of pointers to it; at zero it is freed at once

Work is spread over the run; freed immediately

Cannot free a cycle of objects that point to each other; cost on every pointer update

Mark and sweep

Mark everything reachable from the roots, then sweep the heap and free unmarked objects

Handles cycles

Program pauses; can leave the heap fragmented (unless followed by compaction)

Copying (stop and copy)

Copy reachable objects to a fresh half of memory, then swap halves

No fragmentation; cost depends on live data

Only half the heap memory is usable at a time

EXAM TIP

Reference counting fails on cycles: two objects pointing to each other keep counts of at least 1 even when nothing else can reach them, so they are never freed.

PREVIOUS-YEAR QUESTIONS

0 questions from the papers

Try each question first, then open the answer. We placed each question under a topic ourselves; the exam paper does not label topics.

None of the papers covered here asked a question we placed under this topic.

PRACTICE

5 questions to test yourself

PRACTICE 11 mark · NAT

A program's main calls f(5), where int f(int n) { if (n <= 1) return 1; return f(n-1) + f(n-2); }. Left operands are evaluated first. What is the maximum number of activation records on the run-time stack at any instant, counting the record of main?

Answer and solution

Answer:

  1. The deepest point follows the leftmost chain: f(5), f(4), f(3), f(2), f(1).

  2. That is 5 activations of f; no other chain is longer, since each call goes at most one level deeper than its caller by calling f(n-1).

  3. Add main's record: 5 + 1 = 6.

PRACTICE 21 mark · NAT

With the same function f as in the previous question (f(n) returns 1 if , otherwise f(n-1) + f(n-2)), main calls f(6). How many activations of f occur in total, not counting main?

Answer and solution

Answer:

  1. Let be the number of activations in the call f(n). Then and .

  2. C(2) = 1 + 1 + 1 = 3.

  3. C(3) = 1 + 3 + 1 = 5.

  4. C(4) = 1 + 5 + 3 = 9.

  5. C(5) = 1 + 9 + 5 = 15.

  6. C(6) = 1 + 15 + 9 = 25.

PRACTICE 32 marks · MCQ

The procedure p(u, v) is u = u + 1; v = v + u;. In main, a = 3 and p(a, a) is called, then a is printed. Under call by value-result, the final values of the formals are copied back into a at return, u first and then v. Which option gives the value printed for (call by value, call by reference, call by value-result) respectively?

  1. (A)

    3, 8, 7

  2. (B)

    3, 8, 8

  3. (C)

    4, 8, 7

  4. (D)

    3, 7, 8

Answer and solution

Answer: (A)

  1. Call by value: p works on copies, so a stays 3.

  2. Call by reference: u and v are both the address of a. u = u + 1 makes a = 4. v = v + u is a = a + a = 8.

  3. Value-result: u and v start as copies, both 3. u = 4. v = 3 + 4 = 7. Copy back u (a = 4), then v (a = 7), so a = 7.

  4. The triple is 3, 8, 7.

PRACTICE 41 mark · MSQ

Which of the following statements about static allocation of activation records (every procedure gets one fixed area at compile time) are TRUE?

  1. (A)

    It cannot support a procedure that calls itself recursively, if each activation needs its own locals.

  2. (B)

    The size of every local variable must be known at compile time.

  3. (C)

    A local variable keeps its value from one call of the procedure to the next.

  4. (D)

    It allows a local array whose size is read from input on each call.

Answer and solution

Answer: (A), (B), (C)

  1. A is true: there is one record per procedure, so a second active call would overwrite the first.

  2. B is true: addresses and sizes are fixed before the program runs.

  3. C is true: the names are bound to the same locations for the whole run, so values stay.

  4. D is false: the size is known only at run time, which static allocation cannot provide.

PRACTICE 52 marks · NAT

In a Pascal-like language with nested procedures and static scoping, program P (depth 1) declares procedure R and then procedure Q (depth 2 each), so R is visible inside Q and everything nested in it. Procedure Q declares procedure S (depth 3). The calls made in a run are P calls Q, Q calls S, and S calls R. When S calls R, how many access links must be followed, starting from S's activation record, to reach the record that becomes R's access link?

Answer and solution

Answer:

  1. S is at depth and R at depth .

  2. Links to follow: .

  3. Check: S's access link leads to Q's record (1 link), Q's access link leads to P's record (2 links). P is R's lexical parent, so R's access link points to P's record.

OFFICIAL GATE 2027 SYLLABUS WORDING

Copied from the GATE 2027 syllabus; see all 2027 syllabi.

GATE CS

Runtime environments

Preparing in Kolkata? IAE Academy's course for GATE CSE.