9.1 Program Memory Layout
A compiler does not emit code into a vacuum. Every generated address calculation, load/store instruction, and calling sequence assumes a runtime memory model: where code lives, where globals live, where stack frames grow, and where dynamic objects are allocated.
In practice, this model is often introduced as four coarse regions:
- code/text segment for instructions
- global/static segment for process-lifetime data
- stack for call-scoped frames
- heap for dynamically managed objects
The exact layout differs by platform and executable format, but this classification is enough to reason about correctness and performance in compiler backends.
Why Backends Care
Frontend phases mostly reason in terms of syntax, symbols, and types. Backends must turn those facts into concrete addresses and movement rules.
When lowering a local variable, the backend often picks a stack slot or register. When lowering a static object, it emits data into a global section and references it via relocation-aware symbols. When lowering heap allocation, it emits calls to runtime allocators and follows object-layout conventions.
This means memory layout affects:
- instruction selection and addressing modes
- ABI conformance and interop
- escape analysis and allocation strategy
- debugging metadata and stack unwinding
Lifetime Is the Real Axis
A common beginner mistake is to classify objects by "shape" instead of by lifetime and ownership contract.
- A local array may still be stack-allocated if its extent is known and non-escaping.
- A tiny object may still be heap-allocated if it escapes a function.
- A string literal is data, but usually process-lifetime static data, not heap data.
As compiler engineers, we repeatedly ask: who owns this storage, and when is it valid to access?
That question bridges frontend semantic guarantees and backend runtime safety.
Worked Example
Suppose a function returns &localVar. The parser is happy and the type checker may even accept the pointer type, but runtime validity is wrong: the stack slot dies when the frame is popped. The memory model reveals the bug.
This is why memory-layout reasoning belongs in compiler education even before deep optimization: it prevents classes of miscompilations and invalid generated code.