CD Notes
Guide to parameter passing mechanisms: pass-by-value, pass-by-reference, etc.
Introduction to Parameter Passing
When you call a function and hand it some data, how exactly does that data get from the caller to the callee? This seemingly simple question has several fundamentally different answers, each with distinct implications for program behavior. Understanding parameter passing mechanisms is crucial because the compiler must generate correct code for each mechanism, and choosing the wrong one leads to subtle bugs.
Think of it this way: when you photocopy a document and give someone the copy, they can scribble all over it without affecting your original. But if you give them the original document itself, any changes they make are permanent. This is essentially the difference between pass-by-value and pass-by-reference — and compilers must implement these semantics precisely.
Pass by Value
Pass by value is the simplest and most common mechanism. The caller evaluates the argument expression and copies the resulting value into the parameter variable in the callee's activation record. The callee works with its own independent copy.
void double_it(int x) {
x = x * 2; // Modifies the LOCAL copy only
printf("Inside: %d\n", x); // Prints 10
}
int main() {
int num = 5;
double_it(num);
printf("Outside: %d\n", num); // Prints 5 — unchanged!
}How the compiler implements this: The calling code evaluates num (gets value 5), then either moves it into a register (on x86-64, the first integer argument goes in EDI) or pushes it onto the stack. The callee sees this value in its own storage location — any modifications affect only that location.
Advantages: Simple semantics, no aliasing surprises, safe by default. Disadvantages: Expensive for large objects (entire struct gets copied).
Pass by Reference
Pass by reference gives the callee direct access to the caller's variable. Instead of copying the value, the compiler passes the memory address. Any modification through the reference affects the original variable immediately.
How the compiler implements this: The caller passes the address of the argument, and every access to the parameter inside the callee is compiled as an indirect memory access (dereference):
Advantages: Efficient for large objects (no copy), enables output parameters. Disadvantages: Aliasing possible (two references to same location), harder to reason about.
Pass by Pointer (Simulated Reference in C)
C does not have reference parameters, so programmers simulate pass-by-reference using explicit pointers:
This is semantically identical to pass-by-reference, but syntactically explicit. The programmer must remember to pass &count (not count) and use *ptr (not ptr) inside the function.
Pass by Value-Result (Copy-In/Copy-Out)
This mechanism — used in Ada and some Fortran implementations — copies the value in on entry and copies it back out on exit:
Call f(x):
1. Copy x's value into parameter p (copy-in)
2. Execute function body using p
3. Copy p's final value back into x (copy-out)This usually behaves identically to pass-by-reference, but differs when aliasing occurs:
procedure demo(a: in out Integer; b: in out Integer) is
begin
a := 1;
b := 2;
end;
-- Call: demo(x, x) where x = 0
-- Pass by reference: a=1, then b=2, final x=2
-- Pass by value-result: a_local=1, b_local=2, copy back: x=2 or x=1 (order-dependent!)Pass by Name (Thunks)
Pass by name, historically used in ALGOL 60, is the most exotic mechanism. Instead of passing a value or address, the compiler passes a small piece of code (called a thunk) that computes the argument's current value each time the parameter is accessed:
| temp | = a; // Evaluates thunk for a |
| a | = b; // Evaluates thunk for b, assigns to thunk for a |
| b | = temp; // Assigns to thunk for b |
| // Call | swap(i, A[i]) |
This mechanism is rarely used today except in lazy evaluation languages like Haskell, where arguments are only evaluated when their value is actually needed.
Calling Conventions on Modern Platforms
A calling convention is a contract between caller and callee about exactly how parameters are passed, where the return value lives, and which registers must be preserved. The compiler must follow these conventions precisely for separate compilation to work.
x86-64 System V ABI (Linux, macOS)
| Integer arguments | RDI, RSI, RDX, RCX, R8, R9 (first 6 in registers) |
| Float arguments | XMM0-XMM7 (first 8 in registers) |
| Additional args | Pushed onto stack (right-to-left) |
| Return value | RAX (integer), XMM0 (float) |
| Callee-saved | RBX, RBP, R12-R15 (must be restored if used) |
| Caller-saved | RAX, RCX, RDX, RSI, RDI, R8-R11 (may be clobbered) |
Windows x64 ABI
| Integer arguments | RCX, RDX, R8, R9 (first 4 in registers) |
| Float arguments | XMM0-XMM3 (first 4 in registers) |
| Shadow space | 32 bytes always reserved on stack (for debugging) |
| Return value | RAX (integer), XMM0 (float) |
Worked Example: Tracing Parameter Passing
Consider this code on x86-64 Linux:
int compute(int a, int b, int c) {
return a * b + c;
}
int main() {
int result = compute(3, 4, 5);
}The compiler generates:
How Different Languages Choose
| Language | Mechanism | Notes |
|---|---|---|
| C | Value only | Pointers for simulated reference |
| C++ | Value and Reference | & for reference, default is value |
| Java | Value of reference | Objects passed by value of pointer |
| Python | Value of reference | Like Java — object identity shared |
| Fortran | Reference | Historical default |
| Ada | Value, Reference, Value-Result | Depends on in, out, in out |
| Rust | Move or Borrow | Ownership system enforces safety |
Key Takeaways
Parameter passing is where the abstract concept of "giving data to a function" meets the concrete reality of registers, memory addresses, and stack frames. The compiler translates each mechanism into specific machine instructions — value passing copies data, reference passing passes addresses, and each platform's calling convention dictates the exact registers and stack layout used. Understanding these mechanisms explains why modifications inside a function sometimes affect the outside world and sometimes do not, and helps you write correct code in any language.
Exam Focus
Revise definitions, diagrams, examples, and short-answer points for Parameter Passing: Communication Between Functions.
Interview Use
Prepare one clear explanation, one practical example, and one common mistake for this Compiler Design topic.
Search Terms
compiler-design, compiler design, compiler, design, runtime, environment, parameter, passing
Related Compiler Design Topics