Path: utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!swrinde!zaphod.mps.ohio-state.edu!mips!daver!bungi.com!news From: jkp@sauna.hut.fi (Jyrki Kuoppala) Newsgroups: comp.sys.nsc.32k Subject: A summary of the setjmp problem Message-ID: <9102112058.AA28454@cs.hut.fi> Date: 11 Feb 91 20:58:19 GMT Sender: news@daver.bungi.com Organization: Helsinki University of Technology, Finland. Lines: 134 Approved: news@daver.bungi.com Many persons have instructed me to use 'volatile' or said that 'ANSI C says that setjmp need not save registers, so you must use volatile or take the local variables' address'. I have apparently been unclear in my postings (or people just didn't read all of them ;-) because people many have not understood what I was talking about - please read this message for hopefully a better explanation. Here's what I sent to bug-gcc: gcc 1.39, (emacs 18.57) When porting emacs to a system (Minix 1.3 on NS 32532) whose library function setjmp doesn't save the register I had the following trouble. I tried to compile it with and without -traditional. It crashed in a bit different place than without -traditional, but crashed anyway. The result of the emacs crash was that a subroutine clobbered arguments in the caller routine - and the caller routine didn't call setjmp(). Like this: void foo (x, y) int x; int y; { int count = x - y; /* the compiler puts this in a register variable like r5; say count is 5 */ /* do various things, not using r5 */ fprintf (stderr, "Done various things, count = %d\n, count", count); /* count is 5 here */ bar(); /* call bar() */ fprintf (stderr, "Done bar(), count = %d\n, count", count); /* Oh wow, count is a wild number like 846378 here ! */ baz(count); /* crash in baz() because r5 (count) is bogus */ } I am told that ANSI C doesn't require setjmp to save the automatic variables not declared volatile - but if setjmp doesn't save them, problems arise in the function longjmp is called: the registers used in the function calling longjmp are left clobbered because the function calling longjmp doesn't execute the function prologue to restore them from the stack. Here's an example program exhibiting the behaviour I had: #include #include jmp_buf env; void foo() { /* called from main(), no registers used here so nothing is saved in prologue */ if (setjmp (env)) /* at first setjmp returns 0, so we go call bar() */ sleep(1); else { bar(); } } void bar() { register int c = 3; /* gcc uses r3 here so we save r3 in the stack and set it */ fprintf (stderr, "c = %d\n", c); /* print r3 */ longjmp(env, 1); /* do a longjmp to sleep(1) */ sleep (1); /* oops, we never get here where we would restore r3 from stack */ } main() { /* execution starts here */ register int a = 1; /* we allocate r3 for a */ foo(); /* call foo, by calling convetion foo must store r3 if it changes it */ fprintf (stderr, "a = %d\n", a); /* print value of a (r3), which magically is 3 */ } With gcc 1.39 and a setjmp that doesn't save r3, the program prints 'a = 3'. With a setjmp saving r3, it prints properly 'a = 1'. Suggestions for a solution: a) gcc should save all call-saved registers in the prologue of a function calling setjmp and restore them in the epilogue b) setjmp should always store call-saved registers. This is impossible if users override call-saved with -fcall-saved-REG compiler switch, so a) would seem a bit better. But of course we could save all the registers. Also, if you for some reason must use a system-provided setjmp this is out. c) gcc should presume all registers are clobbered after a call to setjmp and save the registers which may not be clobbered by the function just before setjmp / restore them after that (this doesn't really differ from b) and doesn't look like a good alternative). d) the longjmp library function should restore the registers saved to stack in the function longjmp is called in and all functions between the calls to setjmp and longjmp. I don't think this is possible in most machines, but that's what longjmp does on bsd-vax (LONGJMP_RESTORE_FROM_STACK in tm-vax.h and tm-tahoe.h). Gcc could perhaps be modified to save the info to restore the regs on all machines but this would make the calling convention incompatible and would cause some overhead. (by the way, LONGJMP_RESTORE_FROM_STACK doesn't seem to be used anywhere - the code to test it in flow.c is inside #if 0) The a) alternative would seem best to me, with a tm-file macro to turn it on on machines were setjmp doesn't save the registers. d) could be another alternative, but I don't really see how it's better and the incompatibility and overhead speak for a). Anyway, I don't see any reason not to save all registers in setjmp, but if the library on some machine saves doesn't save them the compiler should do it (like in a)). In the current situation the program foo { int a=1; bar(); printf ("%d\n", a);} could print just anything when setjmp/longjmp is called somewhere in bar() or a function called by bar() and there's a setjmp which doesn't save registers. //Jyrki