[comp.lang.forth] setjmp/longjmp vs. CATCH/THROW

wmb@MITCH.ENG.SUN.COM (07/06/90)

> You can push the jmp_buf on the return stack.

You can do so if you happen to know how many return stack cells are
necessary to hold the information in jmp_buf.  Since the size of
jmp_buf is not necessarily the same for all systems, the portable code
for doing so looks like:

    #JMP_BUF  BEGIN  JMP_BUF OVER CELLS+ @ >R  1- DUP  0<  UNTIL

and the popping code which is required at the end is of similar ilk.
And you can't build this code inside a colon definition because a
standard program cannot make detailed assumptions about exactly
what happens to the return stack when a colon definition is called.
In particular,  : FOO  R>  {push stuff on the return stack}  >R  ;
isn't guaranteed to be portable.

Note that you can't use a DO .. LOOP for the above code because you can't
shove stuff on the return stack inside a DO .. LOOP .  Which brings up the
other obvious problem with using the return stack in this fashion; it
"partitions" the return stack in the middle of a definition, so that
anything that is placed on the return stack before the jmp_buf stuff
is put there becomes inaccessible while the jmp_buf stuff is there.

CATCH/THROW avoids this partitioning effect by making the return stack
effects occur at a procedure boundary, where the return stack details
are already hidden from a standard program.

Specifying that the JMP_BUF information "must be" e.g. 2 cells worth of
data would work in some systems, and would simplify the jmp_buf save
code to
	JMP_BUF 2@ 2>R
but the restriction to 2 cells is impractical, because many systems will
need to save additional information, such as a floating point stack pointer
or a local variable frame pointer.

Mitch

peter@ficc.ferranti.com (Peter da Silva) (07/07/90)

In article <9007062100.AA05671@ucbvax.Berkeley.EDU> wmb%MITCH.ENG.SUN.COM@SCFVM.GSFC.NASA.GOV writes:
> > You can push the jmp_buf on the return stack.

> You can do so if you happen to know how many return stack cells are
> necessary to hold the information in jmp_buf.

True. You also need to know this to allocate a jmp_buf, so it's reasonable
to assume you know it or can find out:

	HERE
	allocate jmp_buf
	HERE SWAP - CONSTANT JMP_BUF_SIZE

> Which brings up the
> other obvious problem with using the return stack in this fashion; it
> "partitions" the return stack in the middle of a definition,

Since you're doing it to implement catch/throw you can assume that this is
a reasonable assumption.

> CATCH/THROW avoids this partitioning effect by making the return stack
> effects occur at a procedure boundary, where the return stack details
> are already hidden from a standard program.

But you can't use CATCH and THROW to (for example) implement coroutines.
This does require knowing what's inside a JMP_BUF, I admit, but that's
probably manageable:

	addr JMP_BUF !STACK_BASE
	addr JMP_BUF !RSTACK_BASE
	int JMP_BUF !STACK_SIZE
	int JMP_BUF !RSTACK_SIZE
	addr JMP_BUF !IP
	JMP_BUF longjmp

This is forth. If you want a language like lisp with no interesting
bits, I'd say go with lisp. For forth you should choose the mechanism with
the greatest amount of flexibility. Ten years ago I did this both ways,
and jmp_buf was the clear winner.
-- 
Peter da Silva.   `-_-'
+1 713 274 5180.
<peter@ficc.ferranti.com>