{Begin SubSec Stack Functions} {Title Stack Functions} {Text {index *BEGIN* stack functions} In the descriptions of the stack functions below, when we refer to an argument as a stack descriptor,{index stack descriptor} we mean that it is either a stack pointer or one of the following abbreviations: {Begin Unlabeledlist abbreviations} {Item {lisp NIL} means the active frame; that is, the frame of the stack function itself.} {Item {lisp T} means the top-level frame.} {Item Any other literal atom is equivalent to {lisp (STKPOS {arg ATOM} -1)}.} {Item A number is equivalent to {lisp (STKNTH {arg NUMBER})}.} {End Unlabeledlist abbreviations} In the stack functions described below, the following errors can occur: The error {lisp ILLEGAL STACK ARG}{index ILLEGAL STACK ARG Error} occurs when a stack descriptor is expected and the supplied argument is either not a legal stack descriptor (i.e., not a stack pointer, litatom, or number), or is a litatom or number for which there is no corresponding stack frame, e.g., {lisp (STKNTH -1 'FOO)} where there is no frame named {lisp FOO} in the active control chain or {lisp (STKNTH -10 'EVALQT)}. The error {lisp STACK POINTER HAS BEEN RELEASED}{index STACK POINTER HAS BEEN RELEASED Error} 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 {PageRef Tag ReleasingStackPointers}. {FnDef {FnName STKPOS} {FnArgs NAME N POS OLDPOS} {Text Returns a stack pointer to the {arg N}th frame with frame name {arg NAME}. The search begins with (and includes) the frame specified by the stack descriptor {arg POS}. The search proceeds along the control chain from {arg POS} if {arg N} is negative, or along the access chain if {arg N} is positive. If {arg N} is {lisp NIL}, {lisp -1} is used. Returns a stack pointer to the frame if such a frame exists, otherwise returns {lisp NIL}. If {arg OLDPOS} is supplied and is a stack pointer, it is reused. If {arg OLDPOS} is supplied and is a stack pointer and {LISP STKPOS} returns {lisp NIL}, {arg OLDPOS} is released. If {arg OLDPOS} is not a stack pointer it is ignored. Note: {lisp (STKPOS 'STKPOS)} causes an error, {lisp ILLEGAL STACK ARG}; it is not permissible to create a stack pointer to the active frame. }} {FnDef {FnName STKNTH} {FnArgs N POS OLDPOS} {Text Returns a stack pointer to the {arg N}th frame back from the frame specified by the stack descriptor {arg POS}. If {arg N} is negative, the control chain from {arg POS} is followed. If {arg N} is positive the access chain is followed. If {arg N} equals 0, {fn STKNTH} returns a stack pointer to {arg POS} (this provides a way to copy a stack pointer). Returns {lisp NIL} if there are fewer than {arg N} frames in the appropriate chain. If {arg OLDPOS} is supplied and is a stack pointer, it is reused. If {arg OLDPOS} is not a stack pointer it is ignored. Note: {lisp (STKNTH 0)} causes an error, {lisp ILLEGAL STACK ARG}; it is not possible to create a stack pointer to the active frame. }} {FnDef {FnName STKNAME} {FnArgs POS} {Text Returns the frame name of the frame specified by the stack descriptor {arg POS}. }} {FnDef {FnName SETSTKNAME} {FnArgs POS NAME} {Text Changes the frame name of the frame specified by {arg POS} to be {arg NAME}. Returns {arg NAME}. }} {FnDef {FnName STKNTHNAME} {FnArgs N POS} {Text Returns the frame name of the {arg N}th frame back from {arg POS}. Equivalent to {lisp (STKNAME (STKNTH {arg N} {arg POS}))} but avoids creation of a stack pointer. }} In summary, {fn STKPOS} converts function names to stack pointers, {fn STKNTH} converts numbers to stack pointers, {fn STKNAME} converts stack pointers to function names, and {fn STKNTHNAME} converts numbers to function names. {FnDef {FnName DUMMYFRAMEP} {FnArgs POS} {Text Returns {lisp T} if the user never wrote a call to the function at {arg POS}, e.g. in Interlisp-10, {fn DUMMYFRAMEP} is {lisp T} for {lisp *PROG*LAM}, {lisp *ENV*}, and {lisp FOOBLOCK} frames (see block compiler, {PageRef Tag BlockCompiler}). {Note what about in Interlisp-D?} {Note does (DUMMYFRAMEP X) = (NOT (REALFRAMEP X T)) always ?? yes} }} {fn REALFRAMEP} and {fn REALSTKNTH} can be used to write functions which manipulate the stack and work on either interpreted or compiled code: {FnDef {FnName REALFRAMEP} {FnArgs POS INTERPFLG} {Text Returns {arg POS} if {arg POS} is a "real" frame, i.e. if {arg POS} is not a dummy frame and {arg POS} is a frame that does not disappear when compiled (such as {fn COND}); otherwise {lisp NIL}. If {arg INTERPFLG}={lisp T}, returns {arg POS} if {arg POS} is not a dummy frame. For example, if {lisp (STKNAME {arg POS})}={lisp COND}, {lisp (REALFRAMEP {arg POS})} is {lisp NIL}, but {lisp (REALFRAMEP {arg POS} T)} is {arg POS}. }} {FnDef {FnName REALSTKNTH} {FnArgs N POS INTERPFLG OLDPOS} {Text Returns a stack pointer to the {arg N}th (or -{arg N}th) frames for which {lisp (REALFRAMEP {arg POS} {arg INTERPFLG})} is {arg POS}. }} The following functions are used for accessing and changing bindings.{index bindings in a basic frame} Some of functions take an argument, {arg N}, which specifies a particular binding in the basic frame. If {arg N} is a literal atom, it is assumed to be the name of a variable bound in the basic frame. If {arg N} is a number, it is assumed to reference the {arg N}th 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 {index ILLEGAL ARG Error}{lisp ILLEGAL ARG} occurs. {note what is "basic frame" in Interlisp-D ?? Looking at backtraces, it seems that there is a frame which contains the function arguments (the basic frame?), and another frame that contains internal PROG vars for compiled fns (the frame extension?). However, the functions below don't treat these frames consistantly. Most of them seem to be able to access both fn arguments and internal variables, but some of them only access the fn arguments. And then there is STKNARGS, which in Interlisp-D takes a third arg INCLUDEPOS, which access only the fn args (if INCLUDEPOS=NIL), or access both fn args and internal args (if INCLUDEPOS~=NIL). I think that these functions should be made more consistant. (perhaps all of these fns should take an INCLUDEPOS arg.)} {FnDef {FnName STKSCAN} {FnArgs VAR IPOS OPOS} {Text Searches beginning at {arg IPOS} for a frame in which a variable named {arg VAR} is bound. The search follows the access chain. Returns a stack pointer to the frame if found, otherwise returns {lisp NIL}. If {arg OPOS} is a stack pointer it is reused, otherwise it is ignored. }} {FnDef {FnName FRAMESCAN} {FnArgs ATOM POS} {Text Returns the relative position of the binding of {arg ATOM} in the basic frame of {arg POS}. Returns {lisp NIL} if {arg ATOM} is not found. }} {FnDef {FnName STKARG} {FnArgs N POS {anonarg}} {Text Returns the value of the binding specified by {arg N} in the basic frame of the frame specified by the stack descriptor {arg POS}. {arg N} can be a literal atom or number. }} {FnDef {FnName STKARGNAME} {FnArgs N POS} {Text Returns the name of the binding specified by {arg N}, in the basic frame of the frame specified by the stack descriptor {arg POS}. {arg N} can be a literal atom or number. }} {FnDef {FnName SETSTKARG} {FnArgs N POS VALUE} {Text Sets the value of the binding specified by {arg N} in the basic frame of the frame specified by the stack descriptor {arg POS}. {arg N} can be a literal atom or a number. Returns value. }} {FnDef {FnName SETSTKARGNAME} {FnArgs N POS NAME} {Text Sets the {arg NAME} of the binding specified by {arg N} in the basic frame of the frame specified by the stack descriptor {arg POS}. {arg N} can be a literal atom or a number. Returns {arg NAME}. {note strange behavior in Interlisp-D: for compiled frames, SETSTKARGNAME works, and returns NIL. for non-compiled frames, SETSTKARGNAME doesn't change name, returns old name.} }} {FnDef {FnName STKNARGS} {FnArgs POS {anonarg}} {Text Returns the number of arguments bound in the {index basic frame}basic frame of the frame specified by the stack descriptor {arg POS}. }} {FnDef {FnName VARIABLES} {FnArgs POS} {Text Returns a list of the variables bound at {arg POS}. As an example of the use of {fn STKNARGS} and {fn STKARGNAME}, {fn VARIABLES} could be defined by: {lispcode (VARIABLES [LAMBDA (POS) (for N from 1 to (STKNARGS POS) collect (STKARGNAME N POS])} }} {FnDef {FnName STKARGS} {FnArgs POS {anonarg}} {Text Returns a list of the {it values} of variables bound at {arg POS}. }} {note In Interlisp-D: VARIABLES only returns fn args, whereas STKARGS also returns values of internal PROG vars} The following functions are used to evaluate an expression in a different environment, and/or to alter the flow of control. {FnDef {FnName ENVEVAL} {FnArgs FORM APOS CPOS AFLG CFLG} {Text Evaluates {arg FORM} in the environment specified by {arg APOS} and {arg CPOS}. That is, a new active frame is created with the frame specified by the stack descriptor {arg APOS} as its {term ALINK}, and the frame specified by the stack descriptor {arg CPOS} as its {term CLINK}. Then {arg FORM} is evaluated. If {arg AFLG} is not {lisp NIL}, and {arg APOS} is a stack pointer, then {arg APOS} will be released. Similarly, if {arg CFLG} is not {lisp NIL}, and {arg CPOS} is a stack pointer, then {arg CPOS} will be released. }} {FnDef {FnName ENVAPPLY} {FnArgs FN ARGS APOS CPOS AFLG CFLG} {Text {fn APPLY}s {arg FN} to {arg ARGS} in the environment specified by {arg APOS} and {arg CPOS}. {arg AFLG} and {arg CFLG} have the same interpretation as with {fn ENVEVAL}. }} {FnDef {FnName STKEVAL} {FnArgs POS FORM FLG {anonarg}} {Text Evaluates {arg FORM} in the access environment of the frame specified by the stack descriptor {arg POS}. If {arg FLG} is not {lisp NIL} and {arg POS} is a stack pointer, releases {arg POS}. The definition of {fn STKEVAL} is {lisp (ENVEVAL {arg FORM} {arg POS} NIL {arg FLG})}. }} {FnDef {FnName STKAPPLY} {FnArgs POS FN ARGS FLG {anonarg}} {Text Similar to {fn STKEVAL} but applies {arg FN} to {arg ARGS}. }} {FnDef {FnName RETEVAL} {FnArgs POS FORM FLG {anonarg}} {Text Evaluates {arg FORM} in the access environment of the frame specified by the stack descriptor {arg POS}, and then returns from {arg POS} with that value. If {arg FLG} is not {lisp NIL} and {arg POS} is a stack pointer, then {arg POS} is released. The definition of {fn RETEVAL} is equivalent to {lisp (ENVEVAL {arg FORM} {arg POS} (STKNTH -1 {arg POS}) {arg FLG} T)}, except that {fn RETEVAL} does not create a stack pointer. }} {FnDef {FnName RETAPPLY} {FnArgs POS FN ARGS FLG {anonarg}} {Text Similar to {fn RETEVAL} except applies {arg FN} to {arg ARGS}. }} {FnDef {FnName RETFROM} {FnArgs POS VAL FLG} {Text Return from the frame specified by the stack descriptor {arg POS}, with the value {arg VAL}. If {arg FLG} is not {lisp NIL}, and {arg POS} is a stack pointer, then {arg POS} is released. An attempt to {fn RETFROM} the top level (e.g., {lisp (RETFROM T))} causes an error, {lisp ILLEGAL STACK ARG}. {fn RETFROM} can be written in terms of {fn ENVEVAL} as follows: {lispcode (RETFROM (LAMBDA (POS VAL FLG) (ENVEVAL (LIST 'QUOTE VAL) NIL (if (STKNTH -1 POS (if FLG then POS)) else (ERRORX (LIST 19 POS))) NIL T)))} }} {FnDef {FnName RETTO} {FnArgs POS VAL FLG} {Text Like {fn RETFROM}, except returns {it to} the frame specified by {arg POS}. }} {FnDef {FnName EVALV} {FnArgs VAR POS RELFLG} {Text Evaluates {arg VAR}, where {arg VAR} is assumed to be a litatom, in the access environment specifed by the stack descriptor {arg POS}. If {arg VAR} is unbound, {fn EVALV} returns {lisp NOBIND}{index NOBIND Litatom} and does not generate an error. If {arg RELFLG} is non-{lisp NIL} and {arg POS} is a stack pointer, it will be released after the variable is looked up. While {fn EVALV} could be defined as {lisp (ENVEVAL {arg VAR} {arg POS} NIL {arg RELFLG})} it is in fact somewhat faster.} } The following functions and variables are used to manipulate stack pointers. {FnDef {FnName STACKP} {FnArgs X} {Text Returns {arg X} if {arg X} is a stack pointer, otherwise returns {lisp NIL}. }} {FnDef {FnName RELSTK} {FnArgs POS} {Text Release the stack pointer {arg POS} (see {PageRef Tag ReleasingStackPointers}). If {arg POS} is not a stack pointer, does nothing. Returns {arg POS}. }} {FnDef {FnName RELSTKP} {FnArgs X} {Text Returns {lisp T} is {arg X} is a released stack pointer, {lisp NIL} otherwise. }} {FnDef {FnName CLEARSTK} {FnArgs FLG} {Text If {arg FLG} is {lisp NIL}, releases all active stack pointers, and returns {lisp NIL}. If {arg FLG} is {lisp T}, returns a list of all the active (unreleased) stack pointers. }} {VarDef {Name CLEARSTKLST} {Text A variable used by top-level {fn EVALQT}. Every time {fn EVALQT} is re-entered (e.g., following errors, or control-D), {var CLEARSTKLST} is checked. If its value is {lisp T}, all active stack pointers are released using {fn CLEARSTK}. If its value is a list, then all stack pointers on that list are released. If its value is {lisp NIL}, nothing is released. {var CLEARSTKLST} is initially {lisp T}. {Note is CLEARSTKLST reset to T after the clearing process?} }} {VarDef {Name NOCLEARSTKLST} {Text A variable used by top-level {fn EVALQT}. If {var CLEARSTKLST} is {lisp T} (see above) all active stack pointers {it except} those on {var NOCLEARSTKLST} are released. {var NOCLEARSTKLST} is initially {lisp NIL}. {Note is NOCLEARSTKLST ignored if CLEARSTKLST is a list?} }} Thus if one wishes to use multiple environments that survive through control-D, either {var CLEARSTKLST} should be set to {lisp NIL}, or else those stack pointers to be retained should be explicitly added to {var NOCLEARSTKLST}. {FnDef {FnName COPYSTK} {FnArgs POS1 POS2} {Text (Interlisp-10) Copies the stack, including basic frames, from the frame specified by the stack descriptor {arg POS1} to the frame specified by the stack descriptor {arg POS2}. That is, copies the frame extensions and basic frames in the access chain from {arg POS2} to {arg POS1} (inclusive). {arg POS1} must be in the access chain of {arg POS2}, i.e., "above" {arg POS2}. Returns the new {arg POS2}. This provides a way to save an entire environment including variable bindings. }} {FnDef {FnName MAPDL} {FnArgs MAPDLFN MAPDLPOS} {Text Starts at {arg MAPDLPOS} and applies {arg MAPDLFN}, a function of two arguments, to the function {it name} at each frame, and the frame (stack pointer) itself, until the top of the stack is reached. Returns {lisp NIL}. For example, {lispcode [MAPDL (FUNCTION (LAMBDA (X POS) (if (IGREATERP (STKNARGS POS) 2) then (PRINT X)]} will print all functions of more than two arguments. }} {FnDef {FnName SEARCHPDL} {FnArgs SRCHFN SRCHPOS} {Text Similar to {fn MAPDL}, except searches the pushdown list starting at position {arg SRCHPOS} until it finds a frame for which {arg SRCHFN}, a function of two arguments applied to the {it name} of the frame and the frame itself, is not {lisp NIL}. Returns {lisp ({arg NAME} . {arg FRAME})} if such a frame is found, otherwise {lisp NIL}. }} {index backtrace} {FnDef {FnName BACKTRACE} {FnArgs IPOS EPOS FLAGS FILE PRINTFN} {Text Performs a backtrace beginning at the frame specified by the stack descriptor {arg IPOS}, and ending with the frame specified by the stack descriptor {arg EPOS}. {arg FLAGS} is a number in which the options of the {fn BACKTRACE} are encoded. If a bit is set, the corresponding information is included in the backtrace. bit 0 - print arguments of non-{lisp SUBR}s. bit 1 - print temporaries of the interpreter. bit 2 - print {lisp SUBR} arguments and local variables. bit 3 - omit printing of {lisp UNTRACE:} and function names. bit 4 - follow access chain instead of control chain. bit 5 - print temporaries, i.e. the blips. For example: if {arg FLAGS}={lisp 47Q}, everything is printed; if {arg FLAGS}={lisp 21Q}, follows the access chain, prints arguments. {arg FILE} is the file that the backtrace is printed to. {arg FILE} must be open. {arg PRINTFN} is used when printing the values of variables, temporaries, blips, etc. {arg PRINTFN}={lisp NIL} defaults to {lisp PRINT}. {Begin Note} Date: 18 Sept. 1981 7:40 pm PDT (Friday) From: BURTON.PA BACKTRACE prints UNTRACE: before it does anything else. It's documented in the manual but not in the VM as doing this. It seems to me to be a bad idea. Any reason not to change it? {End Note} }} {FnDef {FnName BAKTRACE} {FnArgs IPOS EPOS SKIPFNS FLAGS FILE} {Text Prints a backtrace from {arg IPOS} to {arg EPOS} onto {arg FILE}. {arg FLAGS} specifies the options of the backtrace, e.g., do/don't print arguments, do/don't print temporaries of the interpreter, etc., and is the same as for {fn BACKTRACE}.{foot {fn BAKTRACE} calls {fn BACKTRACE} with a {arg PRINTFN} of {fn SHOWPRINT}{index SHOWPRINT FN} ({PageRef Fn SHOWPRINT}), so that if {var SYSPRETTYFLG}={lisp T}{index SYSPRETTYFLG Var}, the values will be prettyprinted. }{comment endfootnote} {arg SKIPFNS} is a list of functions. As {fn BAKTRACE} scans down the stack, the stack name of each frame is passed to each function in {arg SKIPFNS}, and if any of them return non-{lisp NIL}, {arg POS} is skipped (including all variables). {fn BAKTRACE} collapses the sequence of several function calls corresponding to a call to a system package into a single "function" using {var BAKTRACELST} as described below. For example, any call to the editor is printed as {lisp **EDITOR**}, a break is printed as {lisp **BREAK**}, etc. {fn BAKTRACE} is used by the {breakcom BT}, {breakcom BTV}, {breakcom BTV+}, {breakcom BTV*}, and {breakcom BTV!} commands, with {arg FLAGS}={lisp 0}, {lisp 1}, {lisp 5}, {lisp 7}, and {lisp 47Q} respectively. }} {VarDef {Name BAKTRACELST} {Text Used for telling {fn BAKTRACE} (therefore, the {breakcom BT}, {breakcom BTV}, etc. commands) to abbreviate various sequences of function calls on the stack by a single key, e.g. {lisp **BREAK**}, {lisp **EDITOR**}, etc. }} The operation of {fn BAKTRACE} and format of {var BAKTRACELST} is described so that the user can add his own entries to {var BAKTRACELST}. Each entry on {var BAKTRACELST} is a list of the form {lisp ({arg FRAMENAME} {arg KEY} . {arg PATTERN})} or {lisp ({arg FRAMENAME} ({arg KEY{sub 1}} . {arg PATTERN{sub 1}}) {ellipsis} ({arg KEY{sub N}} . {arg PATTERN{sub N}}))}, where a pattern is a list of elements that are either atoms, which match a single frame, or lists, which are interpreted as a list of alternative patterns, e.g. {lisp (PROGN **BREAK** EVAL ((ERRORSET BREAK1A BREAK1) (BREAK1)))} {fn BAKTRACE} operates by scanning up the stack and, at each point, comparing the current frame name, with the frame names on {var BAKTRACELST}, i.e. it does an {fn ASSOC}. If the frame name does appear, {fn BAKTRACE} attempts to match the stack as of that point with (one of) the patterns. If the match is successful, {fn BAKTRACE} prints the corresponding key, and continues with where the match left off. If the frame name does not appear, or the match fails, {fn BAKTRACE} simply prints the frame name and continues with the next higher frame (unless the {arg SKIPFNS} applied to the frame name are non-{lisp NIL} as described above). Matching is performed by comparing atoms in the pattern with the current frame name, and matching lists as patterns, i.e. sequences of function calls, always working up the stack. For example, either of the sequence of function calls "{lisp {ellipsis} BREAK1 BREAK1A ERRORSET EVAL PROGN {ellipsis}}" or "{lisp {ellipsis} BREAK1 EVAL PROGN {ellipsis}}" would match with the sample entry given above, causing {lisp **BREAK**} to be printed. Special features: {Begin UnlabeledList Special features} {Item The litatom {lisp &} can be used to match any frame. } {Item The pattern "{lisp -}" can be used to match nothing. {lisp -} is useful for specifying an optional match, e.g. the example above could also have been written as {lisp (PROGN **BREAK** EVAL ((ERRORSET BREAK1A) -) BREAK1)}. } {Item It is not necessary to provide in the pattern for matching dummy frames, i.e. frames for which {fn DUMMYFRAMEP} (see {PageRef Fn DUMMYFRAMEP}) is true, e.g. in Interlisp-10, {lisp *PROG*LAM}, {lisp *ENV*}, {lisp NOLINKDEF1}, etc. When working on a match, the matcher automatically skips over these frames when they do not match. } {Item If a match succeeds and the {arg KEY} is {lisp NIL}, nothing is printed. For example, {lisp (*PROG*LAM NIL EVALA *ENV)}. This sequence will occur following an error which then causes a break if some of the function's arguments are {lisp LOCALVARS}. } {End UnlabeledList Special features} {note need much better description of matching!! more examples} {index *END* stack functions} }{End SubSec Stack Functions} {Begin SubSec Releasing and Reusing Stack Pointers} {Title Releasing and Reusing Stack Pointers} {Text {index releasing stack pointers} {Tag ReleasingStackPointers} The creation of a single stack pointer can result in the retention of a large amount of stack space. Furthermore, this space will not be freed until the next garbage collection, {it even if the stack pointer is no longer being used}, unless the stack pointer is explicitly released or reused. If there is sufficient amount of stack space tied up in this fashion, a {lisp STACK OVERFLOW}{index STACK OVERFLOW Error} condition can occur, even in the simplest of computations. For this reason, the user should consider releasing a stack pointer when the environment referenced by the stack pointer is no longer needed. The effects of releasing a stack pointer are: (1) The link between the stack pointer and the stack is broken by setting the contents of the stack pointer to the "released mark" (currently unboxed 0). A released stack pointer prints as {lisp #{arg ADR}/#{arg 0}}. (2) If this stack pointer was the last remaining reference to a frame extension; that is, if no other stack pointer references the frame extension and the extension is not contained in the active control or access chain, then the extension may be reclaimed, and is reclaimed immediately. The process repeats for the access and control chains of the reclaimed extension so that all stack space that was reachable only from the released stack pointer is reclaimed. A stack pointer may be released using the function{index RELSTK FN} {fn RELSTK}, but there are some cases for which {fn RELSTK} is not sufficient. For example, if a function contains a call to {fn RETFROM} in which a stack pointer was used to specify where to return to, it would not be possible to simultaneously release the stack pointer. (A {fn RELSTK} appearing in the function following the call to {fn RETFROM} would not be executed!) To permit release of a stack pointer in this situation, the stack functions that relinquish control have optional flag arguments to denote whether or not a stack pointer is to be released ({arg AFLG} and {arg CFLG}). Note that in this case releasing the stack pointer will {it not} cause the stack space to be reclaimed immediately because the frame referenced by the stack pointer will have become part of the active environment. {index reusing stack pointers} Another way of avoiding creating new stack pointers is to {it reuse} stack pointers that are no longer needed. The stack functions that create stack pointers ({fn STKPOS}, {fn STKNTH}, and {fn STKSCAN}) have an optional argument which is a stack pointer to reuse. When a stack pointer is reused, two things happen. First the stack pointer is released (see above). Then the pointer to the new frame extension is deposited in the stack pointer. The old stack pointer (with its new contents) is the value of the function. Note that the reused stack pointer will be released even if the function does not find the specified frame. Note that even if stack pointers are explicitly being released, {it creation} of many stack pointers can cause a garbage collection of stack pointer space. Thus, if the user's application requires creating many stack pointers, he definitely should take advantage of reusing stack pointers. }{End SubSec Releasing and Reusing Stack Pointers} {Begin Note} Does this stuff belong in the manual? --- mjs --------- of SETALINK, SETCLINK, ALINK and CLINK: --------- only SETCLINK is in Interlisp-D --------- only SETALINK, SETCLINK are in Interlisp-10 Date: 17 Oct 1978 2205-EDT From: LEWIS at BBN-TENEXD Subject: set a/c link It is now written and will be in the next lisp.mac (alice currently has a hold on the file for a bug fix). I made one change. The fns take a 3rd arg, FLG. If FLG=non-nil then NEWFRAME will be released if the change succeeds. eg, if you want to do the smash and release all the stack pointers, put still catch things if the smash fails, you could say (if not (RELSTK (SETALINK FOO FUM T)) then (SMASHLINKERROR FOO FUM)) or some such. Daryle p.s. A test case I was using was setalink[foo;fum] followed by setclink[fum;foo]. How's that for a case of lisp incest? ------- Date: 18 Oct 1978 1504-EDT Sender: LEWIS at BBN-TENEXD Subject: Re: Documentation for SetAlink, etc Two points: (1) set a/c link does not generate an error when attempting to create a circular structure. They just return NIL. (2) if alink and clink are really provided, they should have macros that expand into the stknth forms. ------- Date: 19 Oct 1978 12:04 pm (Thursday) From: bobrow at PARC-MAXC Subject: Revised Documentation for SetAlink, etc This is the revised documentation for setalink, and setclink based on Daryl's last comment. There are four primitive functions for manipulating the ALINK and CLINK of a frame. Combined with the functions for manipulating the structures of a single frame (stknargs, stkname, setstkname, stkarg, setstkarg, stkargname, setstkargname) and those for setting up a new frame with specified access and control links (enveval, envapply), they form a complete set of primitives for manipulating the control structures of Lisp. Although all the other functions can be written in terms of these primitives, the user will usually want to use the higher level functions described above. alink[pos, opos) returns a stack pointer to the frame which is referred to by the access link of the frame specified by pos. If opos is supplied, and is a stackpointer, it is reused. Returns T if the access link points to the top level. Same as stknth(1,pos,opos) clink[pos, opos) returns a stack pointer to the frame which is referred to by the control link of the frame specified by pos. If opos is supplied, and is a stackpointer, it is reused. Returns T if the control link points to the top level. Same as stknth(-1,pos,opos) setalink[pos, newpos, relflg] sets the alink of the frame specified by pos to be the frame specified by newpos. Returns NIL, and does not set the link if the access chain of the frame specified by newpos contains the frame specified by pos. This prevents the user from accidentally creating a circular access chain. Returns pos otherwise, and if relflg is nonNil, and newpos is a stackpointer, then newpos is released. setclink[pos, newpos, relflg] sets the control of the frame specified by pos to be the frame specified by newpos. Returns NIL, and does not set the link if the control chain of the frame specified by newpos contains the frame specified by pos. This prevents the user from accidentally creating a circular control chain. Returns pos otherwise, and if relflg is nonNil, and newpos is a stackpointer, then newpos is released. {End Note} {Begin Note} Date: 24 Nov. 1980 12:11 pm PST (Monday) From: Masinter OriginalFrom: Dave Dyer Subject: Manipulation of compiled frames There are some shady areas involving manipulation of compiled frames with stack manipulation primitives. Consider the case of a compiled function which contains several nested levels of PROG frames or LAMBDA expressions. Lower level functions could obtain stack pointers to any of these frames, and various inconsistancies or disasters could arise depending on the implementation details of the compiled code. For instance, what should happen if someone does a SETALINK that separates a PROG frame from its emclosing LAMBDA? I believe that there is no "reasonable" behavior that is independant of implentation dependant details, so I propose that these manipulations be disallowed. For all frames internal to a compiled function, SETALINK SETCLINK and SETSTKARGNAME are illegal. For the entry frame of a function, SETSTKARGNAME is illegal. These restrictions should be enforced by the stack primitives. In any case, the compiled code should behave as though they were enforced, eg. make no use of the binding names, alinks or clinks. ------- Date: 24 Nov 1980 12:18 PST From: Masinter at PARC-MAXC Subject: Re: Manipulation of compiled frames To: Dave Dyer I believe that it is reasonable for you to not implement (i.e., cause an appropriate error ILLEGAL STACK ARG) on the following operations on a frame F which was not generated with a function call (rather than with a PROG/LAMBDA). It doesn't seem reasonable for the operations not to work correctly undetected. RETFROM(F --) SETALINK(F --) (Note that Interlisp-D does not implement SETALINK) SETCLINK(F --) RETTO(G ..) where there exists an F such that G is F's CLINK ------ There should be no problem with setting the ALINK or CLINK of some other frame to point to F (e.g., ENVEVAL(form F1 F2) should be OK). I don't quite understand the problem with SETSTKARGNAME, either. {End Note} {Begin Note} Date: 25 Sept. 1981 4:25 pm PDT (Friday) From: Masinter.PA Subject: anonymous frames In-reply-to: DDYER's message of 24 Sep 1981 1700-PDT Interlisp-D does not build separate stack frames for variables bound in internal PROGs and LAMBDAs. This is at variance with Interlisp-10 and the Interlisp VM. It is, however, extremely convenient and does little violence to the semantics of most programs. The place where this makes a difference in user's code are places which explicitly do STKNTHs (rather than REALSTKNTHs) and which then attempt to RETFROM such anonymous frames, as in: (BUG [LAMBDA NIL (PRINT (PROG (A) (RETFROM (STKNTH -1) NIL) (RETURN T]) which works differently in a variety of situations; for example, when interpreted if the interpreter builds frames which the compiler does not, this could result in different results. Even in Interlisp-10, (PROG(A) (PROG (B) (RETFROM (STKNTH -2) NIL)) (RETURN T] works differently interpreted and compiled in Interlisp-10. I propose that the above construct be declared "illegal" or at least to have "undefined results" in "standard Interlisp". Date: 25 Sep 1981 2252-PDT From: Dave Dyer Subject: bytecompiler bug, retfrom and retto The correctness and desirability of the construction in "BUG" is separate from the ill-formed-ness of the optimization the bytecompiler does. For example (DEFINEQ (BUG2 NIL (PRINT (PROG (A) (RETTO 'BUG2 NIL) (RETURN T)] ..should execute the same interpreted and compiled, but compiles incorrectly using the current bytecompiler. So I still claim that the "UNBIND" optimization in SCANOPT is incorrect and should be removed. It would still be incorrect unless you redefine RETTO and RETFROM as valid only where the frame being exited is a LAMBDA. I thing that would be an unacceptable restriction. Regarding RETTO and RETFROM, if one redefined RETFROM as illegal from a *PROG*LAM and RETTO as illegal to a PROG, differences between compiled and interpreted code would be eliminated. Naturally the restriction would have to be enforced, which is not difficult. I would favor making that restriction part of the spec. --- Since the current implementation of POSSIBILITIES violates this proposed change, and is certainly at least bad practice, it should be changed. Who owns ASSIST right now? If the current copy is the correct one, I will fix it and resubmit it as the common source. Date: 28 Sep 1981 09:27 PDT From: Masinter at PARC-MAXC Subject: Re: bytecompiler bug, retfrom and retto In Interlisp-10, (DEFINEQ (BUG3 NIL (PRINT (PROG (A) (DECLARE (LOCALVARS . T)) (RETTO 'BUG3 NIL) (RETURN T)] will also work differently compiled and interpreted, since compiled there will be no PROG frame. Also: (DEFINEQ (BUG4 (X) (PRINT (SELECTQ X (NIL (RETTO 'BUG4 NIL) T) T] (BUG4 NIL) This points out a well-known longstanding flaw in the design of spaghetti stacks, that leaves the results ambiguous when running compiled vs. interpreted except when returning FROM functions, or TO (STKNTH 1 function.frame). Date: 28 Sept. 1981 10:49 am PDT (Monday) From: Teitelman.PA Subject: Re: anonymous frames The BUG program is a good reason why we adopted REALSTKNTH - to allow individual implementations flexibility in exactly what would be on the stack. I agree with Larry that this construct should be declared undefined, in the sense that it is machine dependent, and maybe even subject to change within a particular implementation. ----------------------------- I agree that we can avoid some problems via REALSTKNTH, but the problem is that it is difficult to state they conditions which allow one to say what is legal. Given a frame, it is difficult to give the conditions when it is legal to RETTO it. It only makes sense to RETTO a frame if the frame was the result of doing a function call; at the time that you have your hands on such a frame, there is no way of detecting that condition. It may be that we need to add a notion in the of "(RETTOP frame)" which determines whether it is legal to RETTO its argument, i.e., if the frame has a continuation point. (STKNTH 1 '*PROG*LAM) is not RETTOP, since presumably the "caller" of the PROG need not store a continuation point. {End Note}