macaroni is better than spaghetti
TRANSCRIPT
Hacaronl l s Be t te r than S p a g h e t t i
by Guy Lewis Steele J r .
Massachusetts I n s t i t u t e o f Technology A r t i f i c i a l I n t e l l i g e n c e L a b o r a t o r y
545 Technology Square Cambridge, Rassachusetts 0Z139
N~s t rac t : We p r e s e n t a s t a c k I m p l e m e n t a t i o n o f
m u l t l p l e environments s l m l l a r In p r i n c i p l e to t h a t o f Bobrow and Wegbrett, but based on a model which p rov ides both s t a t l c and dynamlc scoplng. We note some o f the pra91nattc consequences of t h i s choice o f models; one i s tha t no unnecessary con t ro l s tack i s r e t a i n e d f o r ce r ta in important const ruc t ions such as "upward f u n a r g s ' and co rou t t nes . We a l so d i scuss the c o r r e c t t r ea tmen t o f e x i t f u n c t i o n s , and the need f o r "en t r y func t ions" I f dynamic sw i t ch ing o f c o n t r o l contexts Is to be cons is tent .
I n t r o d u c t i o n
The s tack Imp lementa t ion o f env i ronments d e s c r i b e d In [BW] was des lgned t o combine the f l e x i b i l i t y o f g e n e r a l i z e d t r e e - s t r u c t u r e d environments w i th the e f f i c i e n c y o f e x i s t i n g s tack hardware. I t was based on the dynamic-binding model then p reva len t among LISP systems. Since t ha t t lme a number o f problems w l th dynamic b lnd lng have come to l i g h t . We propose here a r e v t s l o n o f t h e s p a g h e t t i s tack Imp lementa t ion whlch r e t a i n s much the same s t o r a g e management t e c h n i q u e s w h i l e s u b s t i t u t i n g a b e t t e r mode l o f e n v i r o n m e n t a n d c o n t r o l r e l a t i o n s h i p s . This model assumes s t a t l c ( l e x l c a l ) b tnd lng as the prlmary scoplng d i s c i p l i n e , but has p rov is ions f o r dynamic b lnd ing as w e l l . We w i l l no t argue the r e l a t l v e m e r i t s o f l e x l c a l and dynamic s c o p l n g h e r e ; f o r t h a t t he r e a d e r i s r e f e r r e d t o [ I m p e r a t i v e ] and [ D e c l a r a t i v e ] . However, where r e l e v a n t we w111 note some o f the p ragma t i c consequences o f the d i f f e r e n c e s between the models.
I n t e r n a l Storage S t r u c t u r e s
We in t roduce f i v e klnds o f s torage o b j e c t s . Three o f these are " s tack -a l l oca ted f rames ' , to be managed by the s t o r a g e r e c l a m a t i o n t e c h n i q u e s discussed In [BW]. Each such frame has th ree f l e l d s SIZE, mAX, and USE f o r use by the s t o r a g e manager . The o t h e r two a r e " h e a p - a l l o c a t e d o b j e c t s " which a r e managed by t he garbage c o U e c t o r in t he u s u a l way, e x c e p t t h a t when such an o b j e c t i s r e c l a i m e d t h e USE f i e l d o f any frame p o i n t e d to by t h e o b j e c t must be d e c r e m e n t e d , p o s s i b l y r e s u l t i n g In t h e r e c l a m a t i o n o f the frame. These new ob jec ts are:
( l ) Access frames. These are analogous to the bas ic frames o f [BW]. An access frame conta ins values o f s t a t i c a l l y scoped v a r i a b l e s and an AL INK p o i n t e r to another access frame (or NIL i f t h l s i s the h i g h e s t access f rame o f the s t a t i c env i ronment) . Not ice tha t , by the very nature o f s t a t i c access, these frames need not con ta in names f o r each o f the v a r i a b l e s ( though they may, i f d e s l r e d , f o r debugging purposes; bu t see a lso the ramarks in [ D e c l a r a t i v e ] ) , but on l y the va lues.
...... -l xl use I s,zE] . A~INK " t . . . . . . . . VALUES[I] / VALUES[Z] /
(Z) C o n t r o l f rames. These are analogous to the f rame e x t e n s i o n s o f [BW]. C o n t r o l f r ames c o n t a l n va r l ous temporary q u a n t i t i e s , a CLINK p o l n t e r to a p rev lous c o n t r o l f rame, and an ALINK po in te r to the current access frame.
,.x I use I s'zE CLINK -" AL]NK e,
Tf.HPORARY 1 TEMPORARY Z
( 3 ) Dynamic b l n d l n g f rames. These are s l m l l a r to c o n t r o l f rames, but a lso con ta in name-value p a i r s f o r dynamic v a r i a b l e lookup. A dynamic b ind ing frame may also have an a d d i t i o n a l DLINK p o i n t e r to the previous dynamic b ind ing frame. The DLINK p o i n t e r i s no t l o g i c a l l y necessary , s ince the same frame can be found by f o l l o w i n g the CLINK chain, but can speed up the search in the case o f deep access, or the contex t swi tch in the case o f shal low access.
60
---':'3. ~ = I USE I SlZE CLINK @
ALINK •
DLINK 4k
DYNAHIC NAME 1
OYNARIC VALUE 1
. , .
DYNAHIC NAME M
OYNAMIC VALUE M
TEMPORARY 1
TEMPORARY 2
..o
TERPORARY N
3~
(4) Funarg ob jec t s . Each funarg ob jec t con ta ins a p o i n t e r to a piece of code and an ALINK p o i n t e r t o the access frame w i t h r espec t t o which t he code i s c losed.
(5 ) Escape f u n c t i o n o b j e c t s . These are o b j e c t s s i m i l a r to those created by Land ln 's J - o p e r a t o r [ L a n d t n ] , Reynold 's escape cons t ruc t [Reyno lds ] , o r SCHEHE's CATCH cons t ruc t [SCHEHE]. An escape f u n c t i o n ob jec t conta ins a CLINK p o i n t e r to some c o n t r o ! f rame, and a l so a c o r r e s p o n d i n g DLINK p o i n t e r s I f such p o i n t e r s are used by dynamic b i nd ing frames.
[ 0L,N I CLI.K
Our m o d e l d i c t a t e s t h a t t h e r e be a d i s t i n g u i s h e d r e g l s t e r CURCLINK which po in t s to the c o n t r o l frame being run In . ( In the mu l t i p rocess tng case, t h e r e would be a CURCLINK f o r each p rocess , b u t we s h a l l n o t go i n t o t h e d e t a i l s o f m u l t t p r o c e s s t n g h e r e . ) I f DLINKs are be ing used, t he re must a lso be a CURDLINK. We s h a l l assume the use o f DLINKs, and de te rmine whether the c u r r e n t c o n t r o l frame ls a dynamic b ind ing frame by means o f the t e s t CURDLINK = CURCLINK. I f DLINKs a re n o t used, a l l code r e l a t i n g to them shou ld be I g n o r e d here , and another means prov ided f o r d i s t i n g u i s h i n g dynamic b ind ing frames from o rd i na ry c o n t r o l f rames.
A l l c o n t r o l frames which are not c u r r e n t are " s e a l e d ' , and must conta in a c o n t i n u a t i o n p o i n t , a p o i n t e r t o the code to execute on r e t u r n to t h a t c o n t r o l f rame. In [BW] t h i s c o n t i n u a t i o n p o i n t i s shown in a f i x e d l o c a t i o n In the frame e x t e n s i o n , b u t I t i s o f t e n more c o n v e n i e n t t o push t h e c o n t i n u a t i o n po in t onto the end j u s t be fo re s e a l i n g I t , and pop I t on unseal ing I t ( p o s s i b l y r e p l a c i n g i t w i t h a re tu rned v a l u e ) .
We now p r e s e n t s c e n a r i o s f o r t he common o p e r a t i o n s .
C a l l i n g a f u n c t i o n . We have a funarg ob jec t f o r a f u n c t i o n we wish to c a l l , we know how many arguments t o g i ve i t , and we w i l l ca l cu l a t e the arguments on t h e f l y .
C a l l Functton(FNARG, Number of Args) l e t A = A l l o c a t e Access Frame(Number of Args) t_nn
begin A.ALINK ~ Grab Access Frame(FNARG.ALINK); f o r I from 1 to Number of Args d_£
A.VALUE3[I] ~- Ca lcu la te Argument ( i ) ; Release Access Frame(CURCLINK.ALINK); CURCLINK.ALINK~- A; go t o(FNARG.CODE)
end
Note t h a t no c o n t r o l frame i s pushed; our model p reserves the impor tan t p rope r t y o f t a i l - r e c u r s i o n . 3ee "Push Return Address" below.
A p p l i c a t i o n . Th i s b reaks down I n t o two c a s e s : o r d i n a r y funargs and escape func t i ons .
Apply(FUN, ARGLIST) • case type(FUN) In
Funarg: Apply Funarg(FUN, ARGLIST);
Escape Funct ion: App ly Escape Function(FUN, ARGLIST);
esac
A p p l y i n g a f u n a r g . We have a f una rg o b j e c t f o r a f u n c t i o n and a computed l i s t o f arguments.
App ly Funarg(FNARG, ARGLIST) • l e t A = A l l o c a t e Access Frame(length(ARGLlST)) l_p_n begi.
A.ALINK ~- Grab Access Frame(FNARG.ALINK); comment This fo._.E loop steps [ and X in
p a r a l l e l ; for" I from 1, X i_nnARGLIST do
A.VALUE3[I] *- X; Release Access Frame(CURCLINK.ALINK); CURCLINK.ALINK~- A; go t_.£o(FNARG.CODE)
end
C rea t i ng a funarg . We have a piece o f code and wish t o c lose i t In the cu r ren t envi ronment .
Cons Funarg(CODE) • l e t F = A l l o c a t e Funarg Ob jec t ( ) i_nn
begin F.CODE ~- CODE; F.ALINK *- Grab Access Frame(CURCLINK.ALINK); F
end
Note t h a t a funarg po in ts on ly to chains o f access f rames, and r e t a i n s no c o n t r o l f rames. Under t h e dynamic b i nd ing model o f [BW], funargs r e t a i n frame ex tens ions even though they w i l l never be used.
61
E v a l u a t i n g a sub-combina t ion . As ment ioned above and discussed more thoroughly in [ D e c l a r a t i v e ] , the l o g i c a l l y c o r r e c t t lme to push " c o n t r o l s t ack " i s when commencing eva luat ion of a sub-combinat ion, not when c a l l i n g a func t ion . ThUS:
Push Return Address(Continuation Point ) • begin
Push(Continuat ion Point , CURCLINK); le__tt C = A l l oca te Control Frame(.) Ln
begin C.CLINK~- CURCLINK; E.ALINK *- Grab Access Frame(CURCLINK.ALINK); CURCLINK~- C
end en__.dd
Dynamic B ind ing . We have a se t o f v a r i a b l e names (which may be po in te rs to atomic symbols, addresses o f value c e i l s , or anything else tha t can be given to the dynamic lookup rou t ine to locate a va lue) and a corresponding set o f values. Here we s h a l l assume the deep access method, in which the va lues are p laced on the s tack . As f o r the c r e a t i o n o f an escape func t ion , at t h i s po in t we should be running in a f resh c o n t r o l f rame, which w i l l be conve r t ed i n t o a dynamic b ind ing frame.
Blnd Dynamic Vartables(NANELIST, VALUELIST) • begin
PUSH(CURDLINK, CURCLINK); comment This code l s f o r deep access; f o r N In NAMELIST, V LnVALUELIST do
begln PUSH(N, CURCLINK); PUSH(V, CURCLINK);
end; CURDLINK~- CURCLINK
end
R e t u r n f rom f u n c t i o n . We have computed one o r more v a l u e s , and w i s h t o r e t u r n f rom t h e c u r r e n t c o n t r o l f rame. (See [ D e c l a r a t i v e ] f o r a d iscussion of why i t i s on ly p r i m i t i v e operators which ever do t h i s , and how such operators can be accomodated In a LISP- l i k e language.)
Return from Contro l Frame(VALUES) = l e t A = CURCLINK.ALINK,
C = CURCLINK.CLINK I n begln
comment I f we use shal low access f o r dynamic va r i ab les , t h i s i s the place t o r e s t o r e the o ld bindings i f t h i s i s a dynamic b indings frame;
l_ff CURDLINK = CURCLINK t h e n begin
Undo Dynamic B ind ings( ) ; CURDLINK(- CURCLINK.DLINK
end; comment Because we are running in the current
con t ro l frame, CURCLINK is the on ly reference to i t ;
Delete SegNent(CURCLINK); Release Access Frame(A); CURCLINK~- C;
Ensure Runnab t l i t y ( ) ; comment The cont inuat ion po in t Is on the top
of the new con t ro l frame, and we s h a l l rep lace i t w i th the returned va lues;
l e t X = Pop(CURCLINK) 1._nn begin
f o r Y In VALUES do PUSH(Y, CURCLINK); go t_9(x)
en_dd end
C r e a t i n g an escape f u n c t i o n . We want to c r e a t e a new access frame w i t h a va lue in I t which is an escape f u n c t i o n . We should have j u s t en te red a f resh c o n t r o l frame (which w i l l be the case i f the escape expression Is a sub- form o f a c o m b i n a t i o n , f o r example); I f not , we must f i r s t have sealed the c u r r e n t c o n t r o l frame w i t h a r e t u r n address and created a new one. The actua l c rea t ion and b ind ing o f the escape func t ion Is s i m i l a r to the process o f en te r i ng a func t i on ; i t requ i res the c rea t i on o f an access frame, but not of a con t ro l frame.
Blnd Escape Funct ion() • le__tt E = A l l oca te Escape Function Ob jec t ( ) ,
A = A l l o c a t e Access Frame(l) l_nn begin
E.CLINK *- Grab Control Frame(CURCLINK.CLINK); comment The cur rent frame cannot be a dynamic
b lndtng frame; E.DLINK~- CURDLINK; A.VALUES[I] *- E; A.ALINK~- CURCLINK.ALINK; CURCLINK.ALINK4- A
end
A p p l y l n g an escape f u n c t i o n . We have an escape f u n c t l o n o b j e c t and a l i s t o f the v a l u e ( s ) t o be re tu rned .
Apply Escape Functlon(ESCFUN, ARGLIST) • begln
comment I f shal low dynamic b ind ing ls used, I t takes some e f f o r t to change CLINKs, and there is a lso the mat ter o f e x i t func t ions (see below);
Switch Dynamic Context(CURCLINK, ESCFUN.CLINK); Release Contro l Frame(CURCLINK); CURCLINK (- Grab Control Frame(ESCFUN.CLINK); CURDLINK(- ESCFUN.DLINK; Ensure R u n n a b l l l t y ( ) ; le_~tX = POP(CURCLINK) l_nn
begin f o r Y In ARGLIST d_9o PUSH(Y, CURCLINK); go Lo(X)
end en__dd
Here are some of the u t l l l t y rou t lnes assumed by the func t i ons Just presented. Thel r pr imary purpose Is to enforce a reference-count storage d i s c i p l i n e on the frames.
Grab A c c e s s Frame(A) • i f A ~ NIL t h e n A.USE +- A.USE + 1
62
6rab Con t ro l Frame(C) • i f C # NIL then C.USE 4- C.USE + 1
Release Access Frme(A) , l_ff A ~ NIL then
begin A.USE ~- A.USE - 1; i_ff A.USE = 0 then
le.._.t.t B = A.ALINK l_nn begin
Delete Segment(A); Relea se Access F r u e ( B )
end end
Release Con t ro l Frame(C) • i f C * NIL then
begin C.USE ~- C.USE - 1; I f C.USE = 0 then
1 e t a = C.ALINK, B = C.CLINK t_nn begin
Delete Segment(C); Release Access F rme(A ) ; Release Cont ro l Frame(B)
end en__d_d
Ensure R u n n a b l l l t y ( ) • i t" CURCLINK.USE > 1 the.~n
begin CURCLINK.USE ~- CURCLINK.USE - 1; CURCLINK 4- Copy $egmenZ(CURCLINK); CURCLINK.ALINK.USE ( - CURCLINK.ALINK.USE + 1
end e l se i f Not Enough Room to Run() then
le_~t C = Copy 3egment(CURCLINK) i n begin
Delete Segment(CURCLINK); CURCLINK~- C
end
The p r i m i t i v e s "A l l oca te Access Frame" and " A l l o c a t e C o n t r o l Frame" are assumed to i n i t i a l i z e the U~E f i e l d o f the newly a l l o c a t e d frame to 1. See [BW] f o r a d e s c r i p t i o n o f the opera t ion o f the p r i m i t i v e "De le te Segment' .
E x i t f unc t i ons
E x l t f u n c t i o n s , as d e s c r i b e d i n [BW], were n o t i m p l e m e n t e d i n I n t e r L I S P [ T e t t e l m a n ] , w h i c h c o n t a i n s , t o ou r know ledge , t he o n l y w o r k i n g Implementa t ion o f spaghe t t i s tacks. ( In f a c t , the o n l y language we know o f which implements g e n e r a l e x i t f u n c t i o n s Is ITS TECO! [TECO]) Th is may be because they are o f marg ina l u t i l i t y , or because f o r some purposes they are e a s i l y s imulated by the user . Le t us denote a dynamic v a r i a b l e by p r e f i x i n g i t w i t h a co lon ; then we might w r i t e :
((L/~BDA ( : V ) ( (L~BDA (GO001)
(COND ((NULL :V) 60001) (T (FUNCALL :V G0001)) ) )
<form>)) NIL)
(The HacLISP f u n c t i o n FUNCALL Is the same as t he In te rL ISP f unc t i on APPLY*; i t Is e s s e n t i a l l y a way o f d e f e a t i n g t he c o n v e n t i o n t h a t t h e f u n c t i o n p o s i t i o n o f a c o m b i n a t i o n i s " e v a l u a t e d N i n a d i f f e r e n t way from the argument p o s i t i o n s . Thus the f i r s t argument to FUNCALL is c a l l e d as a f u n c t i o n on the r e s t of the arguments.)
Now t h i s code w i l l b i nd :V d y n a m i c a l l y t o NIL and then execute <form>. I f :V i s ever se t non- n i l , then I t w i l l be run as an " e x i t f u n c t i o n " on the va lue o f the form.
This does no t behave p r e c i s e l y as an e x i t f u n c t i o n as desc r i bed in [BW] wou ld , because e x i t f u n c t i o n s a r e supposed t o be run even when t h e system i s sk ipp ing out o f the frame, as f o r a non- l o c a l go to o r an e r r o r . There a re , however , a c o u p l e o f d i f f i c u l t i e s h e r e . One i s t h a t t h e s p a g h e t t i s tack model does not p rescr ibe what va lue to feed to the e x i t f unc t ion In the case o f a non- l o c a l e x i t ( the desc r i p t i ons of "Setexfn" and " E x i t Access Frame" do not cover t h i s case) . I t p robab l y ough t t o r e c e i v e a b i t o f i n f o r m a t i o n d e s c r i b i n g wh,~ther t he e x i t Is normal or n o n - l o c a l . A n o t h e r pr( ,b lem i s t h a t c o n t r o l frames may be n o n - l o c a l l y e n l e r e d ! E x i t f u n c t i o n s are desc r i bed as b e i n g u s e f u l f o r c l o s i n g f i l e s , f o r example; i t wou ld seem t h a t i f such a frame were re -en te red , thanks to the use o f an "escape" f u n c t i o n , we migh t want t o re-open the f i l e !
This l a t t e r problem Is s i m i l a r t o t h a t o f hand l i ng shal low-access dynamica l l y bound v a r i a b l e s du r i ng a c o n t r o l con tex t swi tch. I t i s necessary to f i n d some p o i n t In the c o n t r o l t r e e which Is an a n c e s t o r ( a c t u a l l y a descendant , c o n s i d e r i n g t he d i r e c t i o n o f the po in te r s ) o f both the o ld and the new c o n t e x t , and then t r a v e l a path from o ld c o n t e x t t o "Jo in p o i n t " to new con tex t , updat ing v a r i a b l e s [ G r e e n b l a t t l ] [ B a k e r l ] . One needs to do a s i m i l a r t h i n g f o r " e x i t f u n c t i o n s ' ; one should t r a v e l f rom o l d c o n t e x t t o j o i n p o i n t , r u n n i n g a l l e x i t f u n c t i o n s , and then from j o i n po in t to new c o n t e x t , runn ing a l l e n t r y func t ions . (Presumably the e x i t f u n c t i o n and e n t r y func t ion f o r a g iven frame should be i n v e r s e s . ) In a simple case, such as a n o n - l o c a l e x i t , the new con tex t Is the Join p o i n t , and e n t r y f u n c t i o n s are not needed; but they are necessary in t h e g e n e r a l case , such as f o r generators (see be low) .
The s imu la t i on descr ibed above doesn ' t work f o r n o n - l o c a l e x i t s , and so the e n t r y / e x i t - f u n c t i o n f a c i l i t y must be b u i l t - I n . However, i t i s p robab l y b e t t e r t o r e q u i r e the user t o e x p l i c i t l y s p e c i f y when one l s necessary, r a the r than a l l o w i n g him to p i c k any f rame and patch one i n . Thus one m i g h t ~ i t e :
(EXITBIND (LNIBDA (VALUE NORMALP)
(PROGN (3ETQ SAVED-FP03 (FILEPOS THE-FILE))
(CLOSE THE-FILE) VALUE))
(LNIBDA ( ) (PROGN (OPEN THE-FILE)
(FILEPO3 THE-FILE SAVED-FPOS)))
<body>)
63
This would execute (body) In a frame marked as having exlt and entry functions. We have represented the exlt function as taking the value end a switch. We are not sure what arguments the entry function should receive; there is also the matter of whether the entry function should be invoked on the first (normal) entry to the construct. Although exit functions, as a generallzation of the context-swltching mechanism, wlll probably prove Invaluable In future languages, we dld not Include handling of exlt functions In the routines presented above because of these present u n c e r t a i n t i e s . {Note Binding P r i n c i p l e }
Omitted Features
We have purposely omitted certain features from our model which were provided in [BN]. We omit relatlve evaluation because we do not want to be constrained to store names In access frames. Hore accurately, we do not wish to commit ourselves to a partlcular format for names; names may be represented as atomic symbols, addresses of value cells, effective addresses of machine instructions, etc. A name ls any entity whlch'allows one to identify the value represented by the name. It would be possible to provide relative evaluation for dynamlcally-bound variables, because for those we do effectively have to use a single, system-wide representation of names.
Hore generally, we suggest that the user not be given access to frame pointers, but only to primitives such as funargs and escape functions whlch may be implemented In terms of frames. This Is to reduce the dependence of user programs on a particular implementation.
As a corollary, the concept of a framename i s unnecessary , because t h e r e i s no need to r e f e r d l r e c t l y t o f rames. The p r i m i t i v e o p e r a t i o n s we p resen t here have no need to search dynam ica l l y f o r frames of a p a r t i c u l a r name. {Note Debugging}
Generators and Corouttnes
Of In terL ISP users who make n o n - t r i v i a l use o f t he s p a g h e t t i - s t a c k f e a t u r e , a lmos t a l l use i t a l m o s t e x c l u s i v e l y t h r o u g h t he g e n e r a t o r s and co rou t t nes f e a t u r e . [Kap lan ] For some reason, the c o r o u t t n e Implementat ion descr ibed in [BW] r e q u i r e d t h e u s e r t o s u p p l y t o t he "Resume" p r i m i t i v e a f u n c t i o n w i t h w h i c h t o c o n t i n u e t h e c u r r e n t c o r o u t i n e on resumpt ion. This was p robab ly so t h a t r e s u m p t i o n cou ld pass more than one v a l u e between c o r o u t l n e s . Th is has v e r y much the f l a v o r o f t he " c o n t i n u a t i o n - p a s s i n g s t y l e " o f coding. We p resen t here our ve rs ion o f t ha t approach:
Start(FNARG, ARGLIST) • beg in
CURPROC e- FNARG; Apply(FNARG, ARGLIST);
end
Resume(FNARG, ARGLIST, BACKFN) • begin
CURPROC.CODE ~- BACKFN.CODE; Release Access Frame(CURPROC.ALINK); CURPROC.ALINK 4- Grab Access Frame(BACKFN.ALINK); CURPROC 4- FNARG; Apply(FNARG, ARGLIST);
en___dd
This implementat ion of co rou t tnes has g r e a t appea l , because i t makes i t c l ea r t ha t t he re i s o n l y one " r e a l " th read o f c o n t r o l running among a l l the c o r o u t t n e s . A co rou t l ne funarg does no t r e t a i n any c o n t r o l s tack ; the user must summarize the s t a t e o f t he c o r o u t l n e In the funa rg BACRFN, which r e t a i n s o n l y s t a t i c a c c e s s f r a m e s . ( R e c a l l t h e c o n t i n u a t i o n - p a s s i n g v e r s i o n o f an o r d i n a r y f u n c t i o n , in which there Is no " r e a l " c o n t r o l s tack , b u t t h e c o n t r o l s t a t e i s s i m u l a t e d by means o f funarg env i ronments. [ I m p e r a t i v e ] [ D e c l a r a t i v e ] )
The usua l approach, however, I s t o a l l o w each "Resume" to spec i f y a va lue which becomes the v a l u e o f the "Resume m c a l l o f the resumed process. We see no d i f f i c u l t y w i th func t i ons r e t u r n i n g more than one va lue , and so the re i s no d isadvantage to u s i n g t h i s t echn ique ( o t h e r than t h a t i t r e q u i r e s t h e r e to be more than one " r e a l m c o n t r o l s t a c k ) . We t h e r e f o r e r e w r i t e the co rou t l ne p r i m i t i v e s In terms o f escape f u n c t i o n s :
3tart(FNARG, ARGLIST) • beg in
comment I n i t i a l l y CURPROC must be set to an escape f unc t i on ;
Swap Out Co rou t i ne ( ) ; CURCLINK ~- A l l o c a t e Cont ro l Frame(); CURCLINK.CLINK(- NIL; CURCLINK.ALINK4- NIL; CURPROC ~- A l l o c a t e Escape Funct ion O b j e c t ( ) ; App ly Funarg(FNARG, ARGLIST);
end
Resume(ESCFUN, ARGLIST) • beg in
Swap Out Co rou t t ne ( ) ; CURPROC ~- ESCFUN; comment A c t u a l l y , r a t he r than c a l l i n g A p p l y
Escape Funct ion, we ought to do what i t does but a l so set CURPROC.CLINK (and a lso CURPROC.DLINK, f o r neatness, though i t l s n ' ¢ necessary) to NIL so t ha t CURPROC w i l l no t unnecessa r i l y r e t a i n c o n t r o l f rames;
App ly Escape Functlon(ESCFUN, ARGLIST); end
Swap Out Co rou t i ne ( ) • beg in
Release Con t ro l Frame(CURPROC.CLINK); CURPROC.CLINK~
Grab Cont ro l Frame(CURCLINK.CLINK); comment The cu r ren t c o n t r o l frame must no t be a
dynamic b ind ing frame; CURPROC.DLINK~- CURDLINK;
end
64
I f escape functions are used only for this purpose, it is easy to see that the control frames do not form trees, but o n l y a forest of stacks. {Note Stack Groups}
Each of these implementations of coroutines has the property that no more control stack is retained for the coroutlnes than necessary; the first retains none, and the second only the relevant control stack for each coroutine. The implementation in [BW] had the problem that every coroutine retained the control stack of its creator in order to retain the environment also. {Note ENVEVAL Trick}
Storage Allocation
In the simple cases, control and access frames are allocated alternately as the control stack grows, and de-allocated alternately as t h e control stack shrinks. This is true even if the structures are not parallel; the control stack may grow linearly while the access stack becomes a "leafy tree". As long as no funargs are created, the access frames can be de-allocated alternately with the control frames.
If downward funargs are used, then when the control stack shrinks back, it may be possible to de-allocate an access frame except for the fact that a funarg object points to it, and it would require a garbage c o l l e c t i o n to de te rmine t h a t the f u n a r g object is actually garbage. {Note Stack Allocation} In the case of an upward funarg, it would actually be impossible to de-allocate an access frame. Finally, i n t h e c a s e of an Iterative (tail- recurslve) loop no control frames are allocated or de-allocated, but access frames may be alternately allocated and released at a great rate.
These cases will lead to fragmentation of the stack area of control and access frames are actually allocated alternately. Ne suggest a11ocatlng control frames from the bottom up and access frames from the top down to avoid this fragmentation. In the simple case, each end of the stack area will grow and shrink as a stack. In the funarg cases, the control stack will remain stack- llke without fragmentation as the access frame area becomes possibly fragmented. In the iteration case, the control stack will be stacklike while the access frames chase around a r i ng bu f f e r , in e f f e c t , a t t he o t h e r end. {Note Reusing Access Frames}
[ B a k e r Z ] i n d i c a t e s t h a t a h y b r i d s t o r a g e a l l o c a t i o n scheme using both garbage c o l l e c t i o n and reference counts may be inferior to a system using pure garbage c o l l e c t i o n . I f so, i t would be b e t t e r t o use h i s garbage c o l l e c t o r and s imply a l l o c a t e a l l f rames in the heap. In p r a c t i c e t h i s may depend c r i t i c a l l y on the p a r t i c u l a r hardware used.
Acknowledgements
Daniel Bobrow, Alice Hartley, Ron Kaplan, and Narren Teltelman were most patient in describing the Implementation and use of spaghetti stacks in InterLISP. Comments by Gerald Jay Sussman, Jon Doyle, Johan de Kleer, and Richard Stallman were a l s o h e l p f u l .
Notes
{Note B ind ing P r i n c i p l e ) Our dec is ion to use a spec ia l " b i n d i n g - t y p e "
c o n s t r u c t f o r e x i t f unc t ions was guided by a genera l princlple: anything you can do by assignment, except communicating with other processes, can probably be accompllshed more clearly by binding, because the extent of the effect is contained in an obvious way. The use of "8etexfn" in [BN] may be compared to the use of the ALTER statement in COBOL. The use of a binding primitive also avoids the n e c e s s i t y to compose user and system e x i t f u n c t i o n .
{Note Debugging} Framenames, l i k e v a r i a b l e names in access
f rames, may be use fu l f o r debugging, but should no t be r e q u i r e d f o r the c o r r e c t e x e c u t i o n o f t he user program. One shou ld draw a d i s t i n c t i o n between programs such as debuggers and c o m p i l e r s , wh ich depend on the p a r t i c u l a r r e p r e s e n t a t i o n o f o t h e r p rograms, and programs which depend on t h e i r own particular representation. Programs o f the latter kind cannot easily take advantage of new Implementatlon techniques which Involve different representations.
{Note Stack Groups} The fact that generators are the primary use
of spaghetti stacks In InterLISP leads us to specu la te t h a t the less genera l "s tack group" model p r o p o s e d b y G r e e n b l a t t f o r t h e LISP m a c h i n e [ G r e e n b l a t t Z ] may be adequate f o r many purposes . This model p rov ides m u l t i p l e sets o f c o n t r o l s tacks , w i t h o u t a l l o w i n g f o r a genera l t r ee s t r u c t u r e . This model has now been implemented on the LISP Machine a t NIT.
{Note ENVEVAL Trick} Ron Kaplan [Kaplan] tells us that a certain
system in InterLISP creates several generators as it is being loaded for use at run time, and that speclal pains must be taken, using the ENVEVAL primitive, so that the control stack of the loader w i l l n o t be r e t a i n e d by the c o n t r o l s tacks o f t he gene ra to r s .
{Note Stack A l l o c a t i o n } Some l a n g u a g e s , such as ECL [ C o n r a d ]
[ N e g b r e l t ] , p r o v i d e f o r s t a c k a l l o c a t i o n o f a r b i t r a r y , da ta ob jec ts , which might a l l e v i a t e t h i s problem.
{Note Reuslng Access Frames} In many Iteratlve sltuatlons it is easy for
a compiler to detect that a provided access frame will be thrown away on tall-recurslon to another function; if the frame for the called function is t he same s i z e ( o r s m a l l e r ) than the one f o r t he c a l l i n g f u n c t i o n , the compi ler may arrange to re -use the frame r a t h e r than a l l o c a t i n g a new one. This Is s i m i l a r t o some o f the ideas i n [ D a r l t n g t o n and Burs ta11 ] , but more l i k e l y to be a p p l i c a b l e because the comp i l e r can o f ten determine a b s o l u t e l y t h a t the frame i s t o be d iscarded.
65
References
[Baker l ] Baker, Henry B., Jr. Shallow Binding in LISP 1.5. AI Working Paper 138. HIT AI Lab (January i977).
[Baker2] Baker, Henry B., Jr . L is t Processing in Real Time on a Ser ia l Computer. AI Working Paper 139. HIT AI Lab (February 1977). To appear in Comm. ACH.
[ ~ ] Bobrow, Danlel O. and Wegbrelt, Ben. "A Model and Stack Implementation of Multiple Environments. = CACM 4, I0 (October 1973) pp. 591-603.
[Conrad] Conrad, William R. Internal Representations of ECL Data Types. Technical Report 5-75. Center for Research In Computing Technology, Harvard U. (Cambridge, March 1975).
[Darllngton and Burstall] Darllngton, J., and Burs ta l l , R.M. "A System whlch Automatically Improves Programs." Acta Informatlca 6 (1976), 41-60.
[ Declarat ive ] Steele, Guy Lewls Jr . LAMBDA: The Ul t imate Declarative. AI Nemo 379. MIT AI Lab (Cambridge, November 1976).
[ O r e e n b l a t t l ] O r e e n b l a t t , R lchard . The LISP machine. Artlflclal I n te l l i gence Working Paper 79, HIT (Cambridge, November 1974).
[Oreenblatt2] Personal communications with Rlchard Oreenblatt (March 1978).
[ Imperat ive] Steele, Guy Lewis Jr . , and Sussman, Gerald Jay. LMIBDA: The Ultimate Imperative. AI Memo 353. MIT AI Lab (Cambridge, March 1976).
[Landln] Landtn, Peter J. "A Correspondence between ALGOL 60 and Church's Lambda-Notation." CACM 8, 2-3 (February and March 1965).
[Kaplan] Persona l communicat ions (February 1977).
w i th Ron Kap lan
[Reynolds] Reynolds, John C. =Definitional Interpreters for Higher Order Programming Languages." ACM Conference Proceedings 1072.
[SCHEME] Sussman, Gerald Jay, and Steele, Guy Lewis Jr. SCHEME: An Interpreter for Extended Lambda Calculus. AI Memo 349. NIT AI Lab (Cambridge, December 1975).
[TECO] There Is no published reference for ITS TECO, but those with ARPANET access can re t r ieve the f l l e ".INFO.;TECO ORDER" ~ MIT-AI. See the FN command therein.
[Teltelman] Teltelman, Warren, et al. InterLISP Reference Manual. Revised edition. Xerox Palo Alto Research Center (Palo Alto, 1975).
[Wegbrelt] Wegbrelt, Ben, et al. ECL Programmer's Manual. Technical Report Z3-74. Center for Research In Computing Technology, Harvard U. (Cambridge, December 1974).
66