Unwinding the stack
is the restoration/cleanup of ActivationRecord
s that must occur when control is transferred from one activation record to another; usually the caller's activation record. (When transferring control to
a subroutine--this is winding
the stack, but it usually isn't called that. When dealing with things like CoRoutine
s or FirstClassContinuation
s, this gets a lot more complicated; in this case ContinuationPassingStyle
is often used).
In general, this applies to ActivationRecord
s regardless of what data structure(s) are used to store them; but it especially applies to languages that keep them on TheStack
When one function calls another, several things must occur in the language implementation (this list includes both things that happen in the prologue of the called function, and in the call-site code in the caller):
- The actual parameters to the function must be put in the proper place (assume that they have already been computed)
- If local variables are cached in registers, they must be spilled (saved to TheStack or somewhere else in memory) so that the registers are available for the called function to use. Sometimes this is the responsibility of the caller, sometimes of the callee, sometimes both.
- In languages like C++ which allow objects on TheStack; constructors must be invoked.
When the function returns, the opposite things have to happen:
- The return value or thrown exception (if any) must be placed in the proper place.
- The previously spilled registers in the caller must be unspilled--put back in registers--so that subsequent computation in the caller can use them.
- In C++, destructors for stack objects must be invoked.
- In languages with UnwindProtect or similar ("finally" blocks in Java or C#), these clauses must be executed.
The latter set of operations is essentially what constitutes stack unwinding.
One longstanding issue with C/C++ and modern optimizing compilers is the interaction of setjmp/longjmp (and asynchronous signal handlers) with stack unwinding. Calling longjmp causes a "long distance" transfer of flow control, consuming many stack frames. Return values are a non-issue with longjmp(), but correct destructor semantics and register un-spilling are problems, as longjmp bypasses the compiler-emitted code to do this.
In C++, the presence of destructors especially complicates this. However, C++ has a feature--ExceptionHandling
--that largely replaces setjmp/longjmp() (the one issue that remains is calling longjmp() in a signal handler); so use of setjmp/longjmp in C++ is generally discouraged. If you must
use setjmp/longjmp in C++, you ought to avoid putting non-PlainOldData
objects directly on TheStack