* New version of IRM New version of the IRM, updated to Medley. * moved to docs/medley-irm as discussed
142 lines
67 KiB
Plaintext
142 lines
67 KiB
Plaintext
INTERLISP-D REFERENCE MANUAL
|
||
VARIABLE BINDINGS AND THE STACK
|
||
"11"11. VARIABLE BINDINGS AND THE STACK
|
||
3
|
||
|
||
|
||
Medley uses ªdeep binding.º Every time a function is entered, a basic frame containing the new variables is put on top of the stack. Therefore, any variable reference requires searching the stack for the first instance of that variable, which makes free variable use somewhat more expensive than in a shallow binding scheme. On the other hand, spaghetti stack operations are considerably faster. Some other tricks involving copying freely-referenced variables to higher frames on the stack are also used to speed up the search.
|
||
The basic frames are allocated on a stack; for most user purposes, these frames should be thought of as containing the variable names associated with the function call, and the current values for that frame. The descriptions of the stack functions in below are presented from this viewpoint. Both interpreted and compiled functions store both the names and values of variables so that interpreted and compiled functions are compatible and can be freely intermixed, i.e., free variables can be used with no SPECVAR declarations necessary. However, it is possible to suppress storing of names in compiled functions, either for efficiency or to avoid a clash, via a LOCALVAR declaration (see the Local Variables and Special Variables section of Chapter 18). The names are also very useful in debugging, for they make possible a complete symbolic backtrace in case of error.
|
||
In addition to the binding information, additional information is associated with each function call: access information indicating the path to search the basic frames for variable bindings, control information, and temporary results are also stored on the stack in a block called the frame extension. The interpreter also stores information about partially evaluated expressions as described in the Stack and Interpreter section of Chapter 11.
|
||
Spaghetti Stack
|
||
1
|
||
|
||
The Bobrow/Wegbreit paper, ªA Model and Stack Implementation for Multiple Environmentsº (Communications of the ACM, Vol. 16, 10, October 1973.), describes an access and control mechanism more general than a simple linear stack. The access and control mechanism used by Interlisp is a slightly modified version of the one proposed by Bobrow and Wegbreit. This mechanism is called the ªspaghetti stack.º
|
||
The spaghetti system presents the access and control stack as a data structure composed of ªframes.º The functions described below operate on this structure. These primitives allow user functions to manipulate the stack in a machine independent way. Backtracking, coroutines, and more sophisticated control schemes can be easily implemented with these primitives.
|
||
The evaluation of a function requires the allocation of storage to hold the values of its local variables during the computation. In addition to variable bindings, an activation of a function requires a return link (indicating where control is to go after the completion of the computation) and room for temporaries needed during the computation. In the spaghetti system, one ªstackº is used for storing all this information, but it is best to view this stack as a tree of linked objects called frame extensions (or simply frames).
|
||
A frame extension is a variable sized block of storage containing a frame name, a pointer to some variable bindings (the BLINK), and two pointers to other frame extensions (the ALINK and CLINK). In addition to these components, a frame extension contains other information (such as temporaries and reference counts) that does not interest us here.
|
||
The block of storage holding the variable bindings is called a basic frame. A basic frame is essentially an array of pairs, each of which contains a variable name and its value. The reason frame extensions point to basic frames (rather than just having them ªbuilt inº) is so that two frame extensions can share a common basic frame. This allows two processes to communicate via shared variable bindings.
|
||
The chain of frame extensions which can be reached via the successive ALINKs from a given frame is called the ªaccess chainº of the frame. The first frame in the access chain is the starting frame. The chain through successive CLINKs is called the ªcontrol chainº.
|
||
A frame extension completely specifies the variable bindings and control information necessary for the evaluation of a function. Whenever a function (or in fact, any form which generally binds local variables) is evaluated, it is associated with some frame extension.
|
||
In the beginning there is precisely one frame extension in existence. This is the frame in which the top-level call to the interpreter is being run. This frame is called the ªtop-levelº frame.
|
||
Since precisely one function is being executed at any instant, exactly one frame is distinguished as having the ªcontrol bubbleº in it. This frame is called the active frame. Initially, the top-level frame is the active frame. If the computation in the active frame invokes another function, a new basic frame and frame extension are built. The frame name of this basic frame will be the name of the function being called. The ALINK, BLINK, and CLINK of the new frame all depend on precisely how the function is invoked. The new function is then run in this new frame by passing control to that frame, i.e., it is made the active frame.
|
||
Once the active computation has been completed, control normally returns to the frame pointed to by the CLINK of the active frame. That is, the frame in the CLINK becomes the active frame.
|
||
In most cases, the storage associated with the basic frame and frame extension just abandoned can be reclaimed. However, it is possible to obtain a pointer to a frame extension and to ªhold onº to this frame even after it has been exited. This pointer can be used later to run another computation in that environment, or even ªcontinueº the exited computation.
|
||
A separate data type, called a stack pointer, is used for this purpose. A stack pointer is just a cell that literally points to a frame extension. Stack pointers print as #ADR/FRAMENAME, e.g., #1,13636/COND. Stack pointers are returned by many of the stack manipulating functions described below. Except for certain abbreviations (such as ªthe frame with such-and-such a nameº), stack pointers are the only way you can reference a frame extension. As long as you have a stack pointer which references a frame extension, that frame extension (and all those that can be reached from it) will not be garbage collected.
|
||
Two stack pointers referencing the same frame extension are not necessarily EQ, i.e., (EQ (STKPOS 'FOO) (STKPOS 'FOO)) = NIL. However, EQP can be used to test if two different stack pointers reference the same frame extension (see the Equality Predicates section of Chapter 9).
|
||
It is possible to evaluate a form with respect to an access chain other than the current one by using a stack pointer to refer to the head of the access chain desired. Note, however, that this can be very expensive when using a shallow binding scheme such as that in Interlisp-10. When evaluating the form, since all references to variables under the shallow binding scheme go through the variable's value cell, the values in the value cells must be adjusted to reflect the values appropriate to the desired access chain. This is done by changing all the bindings on the current access chain (all the name-value pairs) so that they contain the value current at the time of the call. Then along the new access path, all bindings are made to contain the previous value of the variable, and the current value is placed in the value cell. For that part of the access path which is shared by the old and new chain, no work has to be done. The context switching time, i.e. the overhead in switching from the current, active, access chain to another one, is directly proportional to the size of the two branches that are not shared between the access contexts. This cost should be remembered in using generators and coroutines (see the Generators section below).
|
||
Stack Functions
|
||
1
|
||
|
||
In the descriptions of the stack functions below, when we refer to an argument as a stack descriptor, we mean that it is one of the following:
|
||
A stack pointer An object that points to a frame on the stack. Stack pointers are returned by many of the stack manipulating functions described below.
|
||
NIL Specifies the active frame; that is, the frame of the stack function itself.
|
||
T Specifies the top-level frame.
|
||
A symbol Specifies the first frame (along the control chain from the active frame) that has the frame name LITATOM. Equivalent to (STKPOS LITATOM -1).
|
||
A list of symbols Specifies the first frame (along the control chain from the active frame) whose frame name is included in the list.
|
||
A number N Specifies the Nth frame back from the active frame. If N is negative, the control chain is followed, otherwise the access chain is followed. Equivalent to (STKNTH N).
|
||
In the stack functions described below, the following errors can occur: The error Illegal stack arg occurs when a stack descriptor is expected and the supplied argument is either not a legal stack descriptor (i.e., not a stack pointer, symbol, or number), or is a symbol or number for which there is no corresponding stack frame, e.g., (STKNTH -1 'FOO) where there is no frame named FOO in the active control chain or (STKNTH -10 'EVALQT). The error Stack pointer has been released occurs whenever a released stack pointer is supplied as a stack descriptor argument for any purpose other than as a stack pointer to re-use.
|
||
Note: The creation of a single stack pointer can result in the retention of a large amount of stack space. Therefore, one should try to release stack pointers when they are no longer needed (see the Releasing and Reusing Stack Pointers section below).
|
||
In Lisp there is a fixed ammount of space allocated for the stack. When most of this space is exhausted, the STACK OVERFLOW error occurs and the debugger will be invoked. You will still have a little room on the stack to use inside the debugger. If you use up this last little bit of stack you will encounter a ªhardº stack overflow. A ªhardº stack overflow will put you into URaid (see the documentation on URaid).
|
||
Searching the Stack
|
||
1
|
||
|
||
(STKPOS(STKPOS (Function) NIL NIL ("11") 4) FRAMENAME N POS OLDPOS) [Function]
|
||
Returns a stack pointer to the Nth frame with frame name FRAMENAME. The search begins with (and includes) the frame specified by the stack descriptor POS. The search proceeds along the control chain from POS if N is negative, or along the access chain if N is positive. If N is NIL, -1 is used. Returns a stack pointer to the frame if such a frame exists, otherwise returns NIL. If OLDPOS is supplied and is a stack pointer, it is reused. If OLDPOS is supplied and is a stack pointer and STKPOS returns NIL, OLDPOS is released. If OLDPOS is not a stack pointer it is ignored.
|
||
(STKNTH(STKNTH (Function) NIL NIL ("11") 4) N POS OLDPOS) [Function]
|
||
Returns a stack pointer to the Nth frame back from the frame specified by the stack descriptor POS. If N is negative, the control chain from POS is followed. If N is positive the access chain is followed. If N equals 0, STKNTH returns a stack pointer to POS (this provides a way to copy a stack pointer). Returns NIL if there are fewer than N frames in the appropriate chain. If OLDPOS is supplied and is a stack pointer, it is reused. If OLDPOS is not a stack pointer it is ignored.
|
||
Note: (STKNTH 0) causes an error, Illegal stack arg; it is not possible to create a stack pointer to the active frame.
|
||
(STKNAME(STKNAME (Function) NIL NIL ("11") 4) POS) [Function]
|
||
Returns the frame name of the frame specified by the stack descriptor POS.
|
||
(SETSTKNAME(SETSTKNAME (Function) NIL NIL ("11") 4) POS NAME) [Function]
|
||
Changes the frame name of the frame specified by POS to be NAME. Returns NAME.
|
||
(STKNTHNAME(STKNTHNAME (Function) NIL NIL ("11") 4) N POS) [Function]
|
||
Returns the frame name of the Nth frame back from POS. Equivalent to (STKNAME (STKNTH N POS)) but avoids creation of a stack pointer.
|
||
In summary, STKPOS converts function names to stack pointers, STKNTH converts numbers to stack pointers, STKNAME converts stack pointers to function names, and STKNTHNAME converts numbers to function names.
|
||
Variable Bindings in Stack Frames
|
||
1
|
||
|
||
The following functions are used for accessing and changing bindings. Some of functions take an argument, N, which specifies a particular binding in the basic frame. If N is a literal atom, it is assumed to be the name of a variable bound in the basic frame. If N is a number, it is assumed to reference the Nth binding in the basic frame. The first binding is 1. If the basic frame contains no binding with the given name or if the number is too large or too small, the error Illegal arg occurs.
|
||
(STKSCAN(STKSCAN (Function) NIL NIL ("11") 5) VAR IPOS OPOS) [Function]
|
||
Searches beginning at IPOS for a frame in which a variable named VAR is bound. The search follows the access chain. Returns a stack pointer to the frame if found, otherwise returns NIL. If OPOS is a stack pointer it is reused, otherwise it is ignored.
|
||
(FRAMESCAN(FRAMESCAN (Function) NIL NIL ("11") 5) ATOM POS) [Function]
|
||
Returns the relative position of the binding of ATOM in the basic frame of POS. Returns NIL if ATOM is not found.ÿÿ |