In article
supernews.com>,
Andrew Haley wrote:
>Anton Ertl
mips.complang.tuwien.ac.at> wrote:
>> If a Forth system supports local buffers (or another locals extension
>> that produces addresses of local memory, e.g., Gforth's
>> variable-flavoured locals), then it has to define the lifetime of the
>> memory the address points to. Typically the lifetime will be until
>> the end of the definition that the buffer was defined in.
>
>> This inhibits tail-call optimization; consider:
>
>> : bar ( addr -- )
>> ... ;
>
>> : foo { | [ 1 ] x } x bar ;
>
>> in the syntax proposed by Stephen Pelc or
>
>> : foo 1 lbuffer bar ;
>
>> in a syntax suggested by others. The allocated buffer must stay
>> alive across the call to BAR, because it is used in BAR, so the
>> deallocation of the buffer must happen after the call to BAR, and
>> BAR is no longer a tail-call and can therefore not be optimized.
>
>Sure it can. With one fairly obvious implementation of local buffers,
>you push onto the return stack the address of a thunk that will
>deallocate the buffer. At the end of the definition you can jump to
>bar; when bar exits it returns to the thunk, which does the
>deallocation.
This is automatical with coroutine calls
: R-ALLOT R> SWAP (R-ALLOT) >R CO (R-DALLOT) ;
Where (R-ALLOT) is a code word that adds to the return stack pointer.
The buffer is to be found in the calling word at a cell offset
from the return stack pointer, typical I.
This is intended for systems with a true return stack.
With an S-stack ala tforth/iforth (or a fake return stack) it is even
easier:
: R-ALLOT (S-ALLOT) CO (S-DALLOT) ;
>
>Andrew.
P.S. I'm opposed to allocating space on a 64 k return stack if there
is a 2 Gbyte data stack, but that is an other story.
Groetjes Albert