[comp.lang.c] Setjmp/longjmp and registers

jjw@celerity.UUCP (02/27/87)

x
From <1881@homxc.UUCP> (sorry, I don't have the poster's name):
>>>> The method used has a large effect on whether setjmp/longjmp can put
>>>> the correct values back into register variables (SYSVID says they
>>>> may be unpredictable :-(.

<651@mcgill-vision.UUCP> mouse@mcgill-vision.UUCP:
>>WHAT?!?!  I can't see any reason that longjmp can't get the registers
>>back - if it can't restore them easily on that architecture then just
>>have setjmp save them in the jmp_buf!  What's the problem?  This will
>>make longjmp basically useless, especially in the face of compilers
>>that optimize non-"register" variables into registers!

In <4160@utcsri.UUCP> greg@utcsri.UUCP (Gregory Smith) provides the sample
program:
>jmp_buf env;
>
>main(){
>	register int i;
>	int j;
>	i = j = 1;
>	if(setjmp(env)){
>		printf( "i==%d, j==%d\n", i, j );
>		exit(0);
>	}
>	i = j = 2;
>	func();
>}
>
>func(){
>	longjmp( env, 1 );
>}
And says:
>The above program should ideally report that i=2. Certainly it will say
>that j is 2. If i is saved in the jmp_buf, and restored by longjmp,
>then i will be reported as 1.

Note that for "compilers that optimize non-'register' variables into
registers" it can even report that j is 1 unless something is done.  Code
which relies on this behaviour is not infrequent. The most frequent use
seems to be in interactive processes which loop making requests of the user
and expect longjmp's out of subroutines which encounter errors.  Some of the
code in the (BSD 4.2) user commands even requires "ideal" restoration of
register variables.

The Celerity (RISC-like) architecture provides lots of registers and our
compilers do some very heavy optimizing, putting everything in sight into
registers, if possible.  The possible solutions seem to require that the
compiler recognize the setjmp:

1. If a function contains a setjmp it can keep all variables in memory.
   This can cause these functions to run much slower.

2. If a function contains a setjmp it can save all registers on any
   subroutine call where they can be picked up by the longjmp.  Some
   architectures (e.g. VAX) provide subroutine call functions which
   automatically store registers and some compilers store the registers
   on all subroutine calls where they can be found by longjmp.

We chose to save the registers where they can be found by longjmp but only
in subroutines which call setjmp.  The overhead of saving registers in all
cases was felt to be excessive.  However, we were faced with the problem of
signal handlers which perform longjmp's.  Since all registers are saved by
the kernel before calling a signal handler it is also necessary for the
lonjmp to be able to restore the registers from that saved state.  This
sounds easier than it is since there are potential "race" conditions
involving signal handlers which occur inside setjmp, inside longjmp, or
while saving the registers for a subroutine call.

----------
For those familiar with RISC architectures and who are wondering why there
is a problem since RISC architectures usually have stackable registers. The
Celerity architecture has stackable registers but its fastest registers are
not stackable.


					-- J. J. Whelan
					   Celerity Computing