On Feb 17, 2025, at 10:36 AM, Peter Corlett via cctalk
<cctalk(a)classiccmp.org> wrote:
On Mon, Feb 17, 2025 at 09:11:17AM -0500, Paul Koning via cctalk wrote:
[...]
int f1(int i) {
int j;
int f2(int x) { int y; y = j*2; ... f1(x+1); }
f2(...);
}
f1(42);
The local variables go into stack frames, one for each call of each
function. So when recursive calls are made as in this example, there are
multiple stack frames belonging to calls of f1 and of f2. Each f1 frame
has a j in it. So how does the code for f2 find "the right f1 frame" to
resolve the reference to j that I showed?
The answer is to have a display, which is a vector of pointers, indexed by
"static scope", in other words by textual nesting level. In this case that
vector would have three entries: one for the program, f1, and f2. When a
call to f1 is made, the stack frame is created. In the stack frame the
previous value of the display entry for f1 is saved, and that entry is
then set to the stack frame address. On function return, the previous
display entry is restored.
To resolve the reference to j in f2, the code would load the display entry
for f1 (the second entry in the display vector), and add the offset to j
in the stack frame.
That's the very old-school approach for weird CISC architectures which were
mainly programmed in assembly language or with naivee compilers. It's most
definitely not a *good* way to do it, performance-wise.
Could be. Optimizing compilers weren't much of a thing in the mid-1960s.
I just found a good descrption in EWD 101
starting with "simple variable
addressing" (page 2). Fortunately that is in English.
The D register mentions points to the display; the addressing modes M<num>[i] are
references to frame offset i via display entry "num", and MA[i] is indexed
addressing with offset i from the A register. (That's odd, I would have expected the
B register which is the default stack pointer.)
paul