Activation Records and Function Calls: Stack Frames, Worked Trace and Exam Traps
Build a reliable stack-frame model with exact call, return, recursion and nested-procedure traces, plus a fully mapped 72-byte teaching layout.
KnowledgeGate Team
Exam prep & CS education

Students can often list parameters, local variables and a return address, yet still mix up an activation with its record, assume one function means one frame, or follow the wrong link in a nested call. A call-to-return model follows main -> f(3) -> g(5,3), with a 72-byte frame calculation and four recursive frames. The byte layout is an explicit teaching convention because real compilers and ABIs may use registers or arrange fields differently.
Related reading: activation record MCQs and recursion stacks.
Activation records and stack frames: the exact distinction
An activation is one execution of a function. Its activation record, or stack frame, holds that execution's runtime data. Recursion creates one record per call although every call runs the same compiled body.
Common fields are parameters, a return-value location, return address, dynamic link or saved caller FP, optional static link, saved registers, locals and temporaries. Parameters carry inputs; temporaries hold compiler-generated values during execution. Not every language or ABI stacks every field, and order is not universal.
Code and state are different. One copy of f can have frames for f(4), f(3) and f(2), each with its own parameter and return point. Use the GATE CS Exam Preparation Courses and Test Series catalog and Compiler Design map for context.
What a call sequence and return sequence actually do
Assume a simplified downward-growing stack. SP marks its top; FP gives fields a stable reference while SP moves. The caller evaluates arguments and records where execution continues. The callee preserves state, establishes its frame, allocates locals and starts the body.
The caller computes arguments.
Control transfers with a return address.
The callee saves the caller's frame reference and registers.
It allocates locals and executes.
It places the result in its agreed location.
Its epilogue releases locals, restores state and uses the return address.
A return address names an instruction such as main:after_f; a dynamic link identifies the caller's record. One restores control flow, the other frame state. LIFO means the newest active frame normally finishes first, which is why calls and recursion fit a stack.
Worked call trace: main -> f(3) -> g(5,3)
g(a, b):
t = a * b
return t - 1
f(x):
y = x + 2
return g(y, x)
main:
ans = f(3)event | top frame | older active frames | computed value |
|---|---|---|---|
Enter |
|
|
|
Set |
|
|
|
Enter |
|
|
|
Set |
|
|
|
Pop |
|
|
|
Pop |
| none | return |
Resume |
| none |
|
Pop only g. Suspended f resumes at f:after_g, receives and returns 14, then is popped. main resumes at main:after_f and stores ans=14.
While g runs, stack order from top is g, f, main. Then it is f, main, and finally main. The frames preserve f's x=3, y=5 while g uses separate values.

Worked frame-size and address calculation
Take an illustrative 64-bit, stack-passed convention. Immediately before the callee frame is built, SP=0x1000, and the stack grows towards lower addresses. Allocate two 8-byte parameters (16 bytes), an 8-byte return address, an 8-byte dynamic link or saved FP, two saved 8-byte registers (16 bytes), a local area rounded to 16 bytes, and one 8-byte temporary area.
Calculate every term:
16 + 8 + 8 + 16 + 16 + 8 = 72 bytes = 0x48 bytes
new SP = 0x1000 - 0x48 = 0x0FB8
FP = 0x0FE0From low to high:
field | inclusive address range |
|---|---|
temporary |
|
locals |
|
saved registers |
|
dynamic link |
|
return address |
|
parameter |
|
parameter |
|
As a cross-check, a starts at FP+16 because 0x0FE0+0x10=0x0FF0. Parameter b starts at FP+24 because 0x0FE0+0x18=0x0FF8. The 72-byte result and these offsets are correct only for this stated teaching convention, not for a named real ISA or ABI.
Recursion means many records for one function
Activation records sit inside a broader run-time model of memory regions, allocation, displays and parameter passing, developed in Run-Time Environment in Compiler Design. Frame construction, address arithmetic, call order and return state need a closer trace. With n <= 1 as the base condition, fact(4) creates four records; the linked run-time-environment example uses n == 0, so its same-named call creates five.
Define fact(n) = 1 when n <= 1; otherwise, fact(n) = n * fact(n-1). Calling fact(4) creates four simultaneously active fact records with parameter values 4, 3, 2 and 1. Each non-base call waits at its own multiplication.
The unwind keeps those values separate:
fact(1) = 1
fact(2) = 2 * 1 = 2
fact(3) = 3 * 2 = 6
fact(4) = 4 * 6 = 24One shared local n would fail because the waiting calls still need their distinct values 4, 3 and 2. If this teaching model assigns 32 bytes to each fact frame, four frames require 4*32=128 bytes. If a separate main frame uses 16 bytes, total active stack storage at the deepest point is 128+16=144 bytes.
Do not report the depth as three. The base-case call fact(1) is a real activation and has its own record, so the four parameter values prove that the recursion depth here is four.
Dynamic links versus static links in nested procedures
In an explicitly lexically scoped teaching language, P has x=10. Inside P, Q defines siblings R and S. R returns x+1. S(k) declares local x=99 and invokes procedure argument k. Then Q calls S(R), so S invokes R while active.
When R runs, the dynamic call chain is R -> S -> Q -> P, which answers who called whom. Its static or lexical chain is R -> Q -> P because Q, not S, is R's lexical parent.
Resolve x lexically: R has no local x; neither does Q; P supplies x=10. Therefore R returns 10+1=11, not 100. It cannot use S's x=99 because S is a dynamic caller, not a lexical ancestor.
Static links are one technique for non-local access. Displays or closure environments can enforce the same rule, so a literal static-link field is not universal.

Exam-style checks and the traps that change the answer
Questions ask you to identify fields, distinguish links, count records, calculate SP, or trace calls.
Use these rapid checks:
At the deepest point of
main -> A -> B -> C, there are4active records, includingmain.Six recursive calls at
40bytes each require6*40=240bytes for those recursive frames.With downward growth,
SP=4096and a48-byte frame giveSP=4096-48=4048.A link to the lexical parent is static; a link to the runtime caller is dynamic.
Repair traps. One function is not one frame, so count calls. Frame size is not just locals, so include every field and padding. A return address is not a frame pointer, so separate code and frame locations. Dynamic order is not lexical scope, so follow the required link.
The KnowledgeGate practice bank currently has more than 20 activation-record questions for applying these checks.
The short version and the next practice step
Use a five-step solver: write the call order; draw one frame per active call; fill parameters and locals; label return, dynamic and any static links separately; then pop in reverse order while carrying each result back.
Remember the anchors: g(5,3) returns 14; the 72-byte frame moves SP from 0x1000 to 0x0FB8; fact(4) creates four frames and returns 24; nested R follows its static chain to P.x=10 and returns 11.
Now redraw the three stack snapshots and four recursion frames without looking. For optional structured study, use GATE Guidance by Sanchit Sir, then use the GATE Test Series for broader practice.
Keep learning

Intermediate Code Generation in Compiler Design: TAC, Backpatching and DAGs
Connect expressions, short-circuit control flow and local optimisation through a single worked translation, from source code to resolved TAC and a reusable DAG.

Ambiguous Grammars and Inherent Ambiguity: Worked CFG Examples for GATE
Learn what two derivations really prove, resolve expression ambiguity with precedence, and trace the classic inherently ambiguous language through aabbcc.

Simplification of CFG: Remove Epsilon, Unit and Useless Productions Step by Step
Simplify one context-free grammar from nine nonterminals to six. See each intermediate grammar, complete unit closures, symbol checks and final derivations.

Phases of Compiler Explained: Worked Example from Tokens to Target Code
Follow one four-line program through lexical, syntax and semantic analysis, then see its intermediate code optimized to a final stored value of 24.0.