{Begin SubSec Linked Function Calls}
{Title Linked Function Calls}
{Text


{Tag LinkedFunctionCalls}

{index *BEGIN* linked function calls}


{it
Note:  Linked function calls are not implemented in Interlisp-D.
}


Conventional (non-linked) function calls from a compiled function go through the function definition cell, i.e., the definition of the called function is obtained from its function definition cell{index function definition cell} at call time.
Thus, when the user breaks, advises, or otherwise modifies the definition of the function {lisp FOO}, every function that subsequently calls it instead calls the modified function.  For calls from the system functions, this is clearly {it not} a desirable feature.  For example, suppose that the user wishes to break on basic functions such as {fn PRINT}, {fn EVAL}, {fn RPLACA}, etc., which are used by the break package.  We would like to guarantee that the system packages will survive through user modification (or destruction) of basic functions (unless the user specifically requests that the system packages also be modified).
This protection is achieved by linked function calls.


For linked function calls, the definition of the called function is obtained at {it link} time, i.e., when the calling function is defined, and stored in the literal table of the calling function.
At {it call} time, this definition is retrieved from where it was stored in the literal table, {it not} from the function definition cell of the called function as it is for non-linked calls.

Note that while function calls from block compiled functions are {it usually} linked (i.e. the default for blocks is to link),{foot
In Interlisp-10, linked function calls are actually a little slower and take more space than non-linked calls, so that the user might want to include {lisp (NOLINKFNS . T)} in block declarations to override the default.
}{comment endfootnote}
and those from standardly compiled functions are {it usually} non-linked, linking function calls and blockcompiling are independent features of the Interlisp compiler, i.e., linked function calls are possible, and frequently employed, from standardly compiled functions.


Note that normal function calls require only the called function's name in the literals of the compiled code, whereas a {it linked} function call uses two literals and hence produces slightly larger compiled functions.


The compiler's decision as to whether to link a particular function call is determined by the variables {var LINKFNS}{index *PRIMARY* LINKFNS Var} and {index *PRIMARY* NOLINKFNS Var}{var NOLINKFNS}
as follows:


{Begin NumberedList compiler's decision}

{Item If the function appears on {var NOLINKFNS}, the call is not linked;}

{Item If block compiling and the function is one of the block functions,
the call is internal as described earlier;}

{Item If the function appears on {var LINKFNS}, the call is linked;}

{Item If {var NOLINKFNS}={lisp T}, the call is not linked;}

{Item If block compiling, the call is linked;}

{Item If {var LINKFNS}={lisp T}, the call is linked;}

{Item Otherwise the call is not linked.}

{End NumberedList compiler's decision}



Note that (1) takes precedence over (2), i.e., if a function appears on {var NOLINKFNS}, the call to it is {it not} linked, even if it is one of the functions in the block, i.e., the call will go outside of the block.


{var NOLINKFNS} is initialized to various system functions such as {fn ERRORSET}, {fn BREAK1}, etc.  {var LINKFNS} is initialized to {lisp NIL}.
Thus if the user does not specify otherwise,
all calls from a block compiled function
(except for those to functions on {index NOLINKFNS Var}{var NOLINKFNS}) will be linked; all calls from standardly compiled functions will not be linked.
However, when compiling system functions such as {fn HELP}, {fn ERROR}, {fn ARGLIST}, {fn FNTYP}, {fn BREAK1}, et al, {index LINKFNS Var}{var LINKFNS} is set to {lisp T} so that even though these functions are not block compiled, all of their calls will be linked.


If a function is not defined at link time, i.e., when an attempt is made to link to it, it is linked instead to the function {fn NOLINKDEF}{index NOLINKDEF FN}.
When the function is later defined, the link can be completed by relinking the calling function using {index RELINK FN}{fn RELINK} described below.
Otherwise, if a function is run which attempts a linked call that was not completed, {fn NOLINKDEF}{index NOLINKDEF FN} is called.
If the function is now defined, i.e., it was defined at some point after the attempt was made to link to it, {fn NOLINKDEF}{index NOLINKDEF FN} will quietly perform the link and continue the call.
Otherwise, it will call {fn FAULTAPPLY}{index FAULTAPPLY FN} and proceed as described in {PageRef Fn FAULTAPPLY}.


{fn CALLS}, {fn BREAK} on {lisp {arg FN1}-IN-{arg FN2}} and {fn ADVISE} {lisp {arg FN1}-IN-{arg FN2}} all work correctly for linked function
calls, e.g., {lisp (BREAK '(FOO IN FIE))}, where {lisp FOO} is called from {lisp FIE} via a linked function call.  Note that control-H will {it not} interrupt at linked function calls.



{Begin SubSec Relinking}
{Title Relinking}
{Text

{index relinking}

The function {fn RELINK} is available for relinking a compiled function, i.e., updating all of its linked calls so that they use the definition extant at the time of the relink operation.



{FnDef {FnName RELINK} {FnArgs FN}
{Text
{arg FN} is either the name of a function, a list of functions, an atom whose value is a list of functions, or the atom {atom WORLD}.{index WORLD  Litatom}  {fn RELINK} performs the corresponding relinking operations.  {fn RELINK} returns {arg FN}.

{lisp (RELINK 'WORLD)} is possible because the compiled code reader maintains on {index LINKEDFNS Var}{var LINKEDFNS} a list of all user functions containing any linked calls.  {index SYSLINKEDFNS Var}{var SYSLINKEDFNS} is a list of all {it system} functions that have any linked calls.  {lisp (RELINK 'WORLD)} performs both {lisp (RELINK LINKEDFNS)} and {lisp (RELINK SYSLINKEDFNS)}.




Note:  To relink a function in a block, one should relink the block, not the function.

{Begin Note}
Date: Fri, 1 Apr 83 15:42:47 PST
From: Christopher Schmidt <SCHMIDT@SUMEX-AIM.ARPA>

You might add a note to the chapter on BLOCKs and linked function calls, that to relink a function in a block, you relink the block, not the function.  I just spent an hour figuring out that I had to (RELINK 'EDITFBLOCK) to relink EDITE's call to EDITL.  (RELINK 'EDITE) doesn't seem to do the trick.
{End Note}
}}


{index relinking}

It is important to stress that linking takes place when a function is {it defined}.
Thus, if {lisp FOO} calls {lisp FIE} via a linked call, and a bug is found in {lisp FIE}, changing {lisp FIE} is not sufficient; {lisp FOO} must be relinked.
Similarly, if {lisp FOO1}, {lisp FOO2}, and {lisp FOO3} are defined (in that order) in a file, and each call the others via linked calls, when a new version of the file is loaded, {lisp FOO1} will be linked to the {it old} {lisp FOO2} and {lisp FOO3}, since those definitions will be extant at the time it is read and defined.  Similarly, {lisp FOO2} will link to the new {lisp FOO1} and {it old} {lisp FOO3}.  Only {lisp FOO3} will link to the new {lisp FOO1} and {lisp FOO2}.  The user would have to perform {lisp (RELINK '(FOO1 FOO2 FOO3))} following the {fn LOAD}.

}{End SubSec Relinking}



{Begin Note}
Date: 15 SEP 1980 1222-PDT
From: KAPLAN
Subject: Linked function calls, or BREAK considered harmful

In my opinion, the most serious logical deficiency in Interlisp-D right now is the absense of linked function calls.  That is much more serious than any of the other things on Larry's list, in terms of the useablity of the system.

As it stands now, the easiest way to crash a system is to try to use the Interlisp debugging facilities.  You just might happen to break a function that BREAK uses, or advise a function that ADVISE calls, or breakdown a BREAKDOWN function.  What you'll get is a stack overflow.

I am aware that the "right" solution to this problem might take a lot of design and implementation time--changing the compiler, the opcodes, etc etc. So I propose what I think is a very simple strategy that ought to be fairly easily implemented.

Basically, the idea is to have the compiler write out on the compiled file an expression that indicates which calls in which functions are to be linked.  That expression is evaluated when the file is loaded, and what it does is simply move the definition of all the functions that a linked call is to  some other canonical name (e.g. CONS gets moved to ;CONS;, unless ;CONS; is already defined) and then do a changename is the functions that the linked call is from.

The function call mechanism stays exactly the same.  The only change to system code would be to the compiler to keep track of what needs linking. The cost, of course, is that more atoms get used up.  But that is not as critical a resource (yet) as the need to be able to debug safely.

I think this sort of capability has much higher priority than, say, moving the keyboard handler from BCPL to Lisp.

Comments?

Date: 15 Sept. 1980 1:53 pm PDT (Monday)
From: Masinter
Subject: Re: Linked function calls, or BREAK considered harmful
In-reply-to: KAPLAN's message of 15 SEP 1980 1222-PDT

By the time you get done, you will be in the position that you might as well have done it right. The modules you will have to change to implement your proposal include: ALAP (the guy who writes out the code), BYTECOMPILER (are intra-block calls "pseudo-linked?", MAKEINIT (what happens to linked calls on files which are loaded in MAKEINIT rather than via LOAD?), ACODE (compiled code reader, CALLSCCODE, CHANGENAME, etc. plus also implementation of RELINK), COMPILE (what happens at RECOMPILE? where are the expressions which say what needs to be relinked?).

After you have solved all of these problems, you'll still have a kludge solution. What is the "right" answer?

Date: 15 SEP 1980 1421-PDT
From: KAPLAN
Subject: Re: Linked function calls

Why do CALLSCODE, CHANGENAME have to be changed?  The linkage for functions in the inital loadup can be done when the system is started up.  The only requiement is that there be a database somewhere indicating who gets linked to who.

My impression is that the "right" solution involves an opcode, microcode, a way of calling functions not through the atom, and so on.

If you think there is a better solution than mine for an equivalent amount of work, then I think we should up its priority on our queues.  If the right solution isn't all that hard, then let's do it.  My point is that this is really an important issue for the useability of the system. It should not be put on the shelf cause the proper solution is to complicated. 

Date: 15 Sept. 1980 2:35 pm PDT (Monday)
From: Masinter
In-reply-to: KAPLAN's message of 15 SEP 1980 1420-PDT

CALLSCCODE is defined to return in separate lists the real names of the functions called with linked-calls as well as the ones called with unlinked calls. 
If you BREAK((CONS IN MUMBLE)), the break package either has to be changed (wrong patch), or else CHANGENAME has to be aware that if MUMBLE contains a "linked" call to ;CONS; it has to change it to call ";CONS-IN-MUMBLE;".

I believe the "right" solution only involved marginally more work than a kludgy one, and that both a kludge solution and a correct solution called for enough work for the benefit gained that it was relatively low on my priority list.
{End Note}

{index *END* linked function calls}

}{End SubSec Linked Function Calls}