Stack – Understanding the Use of a Stack Pointer

stackx86

The stack pointer points to the top of the stack, which stores data on what we call a "LIFO" basis. To steal someone else's analogy, it's like a stack of dishes in which you put and take dishes at the top. The stack pointer, OTOH, points to the top "dish" of the stack. At least, that's true for x86.

But why does the computer/program "care" what the stack pointer's pointing to? In other words, what purpose does having the stack pointer and knowing where it points to serve?

An explanation understandable by C programmers would be appreciated.

Best Answer

What purpose does this stack actually serve, as opposed to explaining its structure?

You have many answers which accurately describe the structure of the data stored on the stack, which I note is the opposite of the question you asked.

The purpose that the stack serves is: the stack is part of the reification of continuation in a language without coroutines.

Let's unpack that.

Continuation is simply put, the answer to the question "what is going to happen next in my program?" At every point in every program something is going to happen next. Two operands are going to be computed, then the program continues by computing their sum, and then the program continues by assigning the sum to a variable, and then... and so on.

Reification is just a highfalutin word for making a concrete implementation of an abstract concept. "What happens next?" is an abstract concept; the way the stack is laid out is a part of how that abstract concept is turned into a real machine that really computes things.

Coroutines are functions that can remember where they were, yield control to another coroutine for a while, and then resume where they left off later, but not necessarily immediately after the just-called coroutine yields. Think of "yield return" or "await" in C#, which must remember where they were when the next item is requested or the asynchronous operation completes. Languages with coroutines or similar language features require more advanced data structures than a stack in order to implement continuation.

How does a stack implement continuation? Other answers say how. The stack stores (1) values of variables and temporaries whose lifetimes are known to be not greater than the activation of the current method, and (2) the address of the continuation code associated with the most recent method activation. In languages with exception handling the stack may also store information about the "error continuation" -- that is, what the program will do next when an exceptional situation occurs.

Let me take this opportunity to note that the stack does not tell you "where did I come from?" -- though it is often so used in debugging. The stack tells you where you are going to next, and what the values of the activation's variables will be when you get there. The fact that in a language without coroutines, where you are going next is almost always where you came from makes this kind of debugging easier. But there is no requirement that a compiler store information about where control came from if it can get away without doing so. Tail-call optimizations for example destroy information about where the program control came from.

Why do we use the stack to implement continuation in languages without coroutines? Because the characteristic of synchronous activation of methods is that the pattern of "suspend the current method, activate another method, resume the current method knowing the result of the activated method" when composed with itself logically forms a stack of activations. Making a data structure that implements this stack-like behaviour is very cheap and easy. Why is it so cheap and easy? Because chip sets have been for many decades specifically designed to make this sort of programming easy for compiler writers.

Related Topic