donn@CS.UTAH.EDU (Donn Seeley) (10/05/88)
Version: GNU C version 1.28 (68k, MIT syntax) compiled by GNU C version 1.27.
Machine type: HP9000/350 (25 MHz 68020) running Berkeley 4.3 Unix
Behavior:
The semantics of setjmp() and longjmp() under Berkeley Unix on
the VAX have traditionally been very liberal. When the BSD
longjmp() performs a non-local goto to a function instance which
previously called setjmp(), all local variables are restored to
the values they had when control was last in that function.
Sun and (most if not all) AT&T implementations are faster but
sacrifice intuitiveness -- variables with explicit storage
class 'register' are not restored as in the Berkeley version
unless the compiler ignores the 'register' specification and
puts them on the stack; otherwise, register variables are
changed to their values as of the last call to setjmp() (with
the proper argument). Sun flatly states that '[register
variable] values are unpredictable'. With the advent of ANSI
C, even the guarantee of restoration of automatic variables
disappears -- the new keyword 'volatile' must be used to
distinguish variables whose value should be restored at
longjmp() time. The draft still requires that if the value of
a variable does not change between the call to setjmp() and the
point where control leaves the function for the last time
before the longjmp() call, its value is predictable, so in most
implementations, variables will have some sane value after
longjmp(), although not necessarily the expected value. (Of
course my draft is not getting any newer...)
The rules on using setjmp() are terribly arcane and are often
misunderstood by programmers. I recently undertook to examine
all the uses of setjmp() in our version of the Berkeley source
tree and found plenty of code that would not survive under AT&T
(or Sun!) setjmp() semantics (including /bin/sh and /bin/csh),
but there was still more code that quite reasonably did not
anticipate the ANSI C setjmp() semantics. Since we compile our
Unix code with GCC and -traditional, we can't make use of the
'volatile' keyword even if we would like to, and even if we
were willing to hack all our makefiles to turn off optimization
on Unix source files that contain setjmp() calls, we couldn't
protect naive user code from complicated disasters. The best
alternative seemed to be a compiler trick -- since GCC already
detects functions that call setjmp(), why not extend it with a
flag that causes functions that call setjmp() to resume after
longjmp() with all their variables restored? We can then make
this flag standard when the compiler is invoked as /bin/cc and
naively written functions that call setjmp() are once again
safe, as they are under Berkeley Unix on the VAX. (The list
of source files that can't hack ANSI C setjmp() has been sent
to Berkeley...)
Here's an edited typescript showing what happens when a
function in traditional C meets a compiler that uses ANSI C
setjmp() semantics:
------------------------------------------------------------------------
Script started on Tue Oct 4 03:44:05 1988
% cat sj.c
#include <setjmp.h>
jmp_buf reslab;
char setintr;
foo()
{
int reenter;
reenter = 0;
(void) setjmp(reslab);
reenter++;
if (reenter == 1)
foo2();
}
% cc-1.28 -v -O -S sj.c
gcc version 1.28
/usr/src/gnu/gcc-1.28/cpp -nostdinc -v -I/usr/include -undef -D__GNU__ -D__GNUC__ -Dmc68000 -Dhp300 -Dunix -D__OPTIMIZE__ -traditional -D__HAVE_FPU__ sj.c /tmp/cc000986.cpp
GNU CPP version 1.28
/usr/src/gnu/gcc-1.28/cc1 /tmp/cc000986.cpp -quiet -dumpbase sj.c -fwritable-strings -fno-defer-pop -O -traditional -version -o sj.s
GNU C version 1.28 (68k, MIT syntax) compiled by GNU C version 1.27.
% cat sj.s
#NO_APP
.text
.even
.globl _foo
_foo:
link a6,#0
pea _reslab
jbsr _setjmp
addqw #4,sp
jbsr _foo2 # So what happened to reenter?!?
unlk a6
rts
.comm _setintr,2
.comm _reslab,68
%
------------------------------------------------------------------------
Suggested fixes:
The fix we chose limits optimization somewhat by forcing all
local variables and parameters to reside on the stack; since
the AT&T / Sun longjmp() restores the stack, this guarantees
that local variable and parameter values are restored. The
compiler flag we selected is -fno-opt-setjmp; it does NOT
suppress all optimization in a function that calls setjmp(),
but merely restricts register allocation (thanks to RMS for
this suggestion). The effect is not unlike declaring all
local variables and parameters with 'volatile'.
Changes to c-tree.h:
------------------------------------------------------------------------
*** /tmp/,RCSt1000999 Tue Oct 4 03:53:39 1988
--- c-tree.h Tue Oct 4 00:59:11 1988
***************
*** 68,73 ****
--- 68,75 ----
extern tree make_index_type ();
+ extern void change_regvars_to_stackvars();
+
extern tree double_type_node, long_double_type_node, float_type_node;
extern tree char_type_node, unsigned_char_type_node, signed_char_type_node;
***************
*** 78,83 ****
--- 80,86 ----
extern int current_function_returns_value;
extern int current_function_returns_null;
+ extern int current_function_uses_setjmp;
extern void yyerror();
extern int lineno;
***************
*** 122,124 ****
--- 125,131 ----
/* Nonzero means do some things the same way PCC does. */
extern int flag_traditional;
+
+ /* Nonzero means don't optimize functions that contain calls to setjmp. */
+
+ extern int flag_no_opt_setjmp;
------------------------------------------------------------------------
Changes to c-decl.c:
------------------------------------------------------------------------
*** /tmp/,RCSt1001021 Tue Oct 4 03:56:58 1988
--- c-decl.c Tue Oct 4 00:58:43 1988
***************
*** 31,36 ****
--- 31,37 ----
#include "flags.h"
#include "c-tree.h"
#include "c-parse.h"
+ #include "rtl.h"
/* In grokdeclarator, distinguish syntactic contexts of declarators. */
enum decl_context
***************
*** 175,180 ****
--- 176,187 ----
int current_function_returns_null;
+ /* Set to 0 at beginning of a function definition, set to 1 if
+ flag_no_opt_setjmp is set and a call to setjmp or _setjmp is
+ seen. */
+
+ int current_function_uses_setjmp;
+
/* Set to nonzero by `grokdeclarator' for a function
whose return type is defaulted, if warnings for this are desired. */
***************
*** 199,204 ****
--- 206,215 ----
int flag_traditional;
+ /* Nonzero means don't optimize functions that contain calls to setjmp. */
+
+ int flag_no_opt_setjmp;
+
/* Nonzero means warn about implicit declarations. */
int warn_implicit;
***************
*** 241,246 ****
--- 252,259 ----
flag_cond_mismatch = 1;
else if (!strcmp (p, "-fno-asm"))
flag_no_asm = 1;
+ else if (!strcmp (p, "-fno-opt-setjmp"))
+ flag_no_opt_setjmp = 1;
else if (!strcmp (p, "-traditional"))
flag_traditional = 1, dollars_in_ident = 1;
else if (!strcmp (p, "-ansi"))
***************
*** 3299,3302 ****
--- 3312,3338 ----
/* Let the error reporting routines know that we're outside a function. */
current_function_decl = NULL;
+
+ /* Recover after processing a function that calls setjmp. */
+ current_function_uses_setjmp = 0;
+ }
+
+ /* Support for -fno-opt-setjmp: convert register parameters and
+ variables to their stack forms. */
+
+ void
+ change_regvars_to_stackvars ()
+ {
+ struct binding_level *level;
+ tree decl;
+
+ if (! current_binding_level)
+ return;
+ for (level = current_binding_level;
+ level != global_binding_level;
+ level = level->level_chain)
+ for (decl = level->names; decl; decl = TREE_CHAIN (decl))
+ if ((TREE_CODE (decl) == VAR_DECL || TREE_CODE (decl) == PARM_DECL)
+ && GET_CODE (DECL_RTL (decl)) == REG)
+ put_var_into_stack (decl);
}
------------------------------------------------------------------------
Changes to expr.c:
------------------------------------------------------------------------
*** /tmp/,RCSt1001026 Tue Oct 4 03:57:25 1988
--- expr.c Tue Oct 4 03:08:56 1988
***************
*** 22,27 ****
--- 22,28 ----
#include "config.h"
#include "rtl.h"
#include "tree.h"
+ #include "c-tree.h"
#include "flags.h"
#include "insn-flags.h"
#include "insn-codes.h"
***************
*** 3635,3640 ****
--- 3636,3649 ----
{
frame_pointer_needed = 1;
may_call_alloca = 1;
+ }
+
+ if (flag_no_opt_setjmp && is_setjmp && ! current_function_uses_setjmp)
+ {
+ /* If a call to setjmp is seen, try to preserve values of variables
+ by forcing register variables onto the stack. */
+ current_function_uses_setjmp = 1;
+ change_regvars_to_stackvars ();
}
/* Don't let pending stack adjusts add up to too much.
------------------------------------------------------------------------
Changes to stmt.c:
------------------------------------------------------------------------
*** /tmp/,RCSt1001031 Tue Oct 4 03:57:38 1988
--- stmt.c Tue Oct 4 03:23:06 1988
***************
*** 1430,1435 ****
--- 1430,1438 ----
struct nesting *thisblock = block_stack;
tree type = TREE_TYPE (decl);
+ /* No register variables if current_function_uses_setjmp is set. */
+ extern int current_function_uses_setjmp;
+
/* External function declarations are supposed to have been
handled in assemble_variable. Verify this. */
***************
*** 1467,1473 ****
&& TREE_CODE (type) == REAL_TYPE)
&& ! TREE_VOLATILE (decl)
&& ! TREE_ADDRESSABLE (decl)
! && (TREE_REGDECL (decl) || ! obey_regdecls))
{
/* Automatic variable that can go in a register. */
DECL_RTL (decl) = gen_reg_rtx (DECL_MODE (decl));
--- 1470,1477 ----
&& TREE_CODE (type) == REAL_TYPE)
&& ! TREE_VOLATILE (decl)
&& ! TREE_ADDRESSABLE (decl)
! && (TREE_REGDECL (decl) || ! obey_regdecls)
! && ! current_function_uses_setjmp)
{
/* Automatic variable that can go in a register. */
DECL_RTL (decl) = gen_reg_rtx (DECL_MODE (decl));
------------------------------------------------------------------------
Here's the difference that the new option makes to the example
above (thanks to Mike Hibler for culling this example from the
tangled csh sources):
------------------------------------------------------------------------
% cc-1.28 -fno-opt-setjmp -v -O -S sj.c
gcc version 1.28
/usr/src/gnu/gcc-1.28/cpp -nostdinc -v -I/usr/include -undef -D__GNU__ -D__GNUC__ -Dmc68000 -Dhp300 -Dunix -D__OPTIMIZE__ -traditional -D__HAVE_FPU__ sj.c /tmp/cc000992.cpp
GNU CPP version 1.28
/usr/src/gnu/gcc-1.28/cc1 /tmp/cc000992.cpp -quiet -dumpbase sj.c -fwritable-strings -fno-defer-pop -fno-opt-setjmp -O -traditional -version -o sj.s
GNU C version 1.28 (68k, MIT syntax) compiled by GNU C version 1.27.
% cat sj.s
#NO_APP
.text
.even
.globl _foo
_foo:
link a6,#-4
clrl a6@(-4)
pea _reslab
jbsr _setjmp
addqw #4,sp
addql #1,a6@(-4) # Voila
moveq #1,d1
cmpl a6@(-4),d1
jne L2
jbsr _foo2
L2:
unlk a6
rts
.comm _setintr,2
.comm _reslab,68
% exit
script done on Tue Oct 4 03:45:52 1988
------------------------------------------------------------------------
Here is some boilerplate for the internals document and the
manual page (format as appropriate):
------------------------------------------------------------------------
`-fno-opt-setjmp'
Do not optimize local variables or parameters in functions
that call `setjmp'. Without this option, local variables or
parameters that are declared without the `volatile' keyword
may be unpredictable after a longjmp(). This is especially
useful with `-traditional' since the latter flag disables the
use of `volatile'.
------------------------------------------------------------------------
While we're at it, it'd nice if something like the following
could be added to the entry for -fno-defer-pop:
------------------------------------------------------------------------
Without this option, programs like `adb' which can't handle
the fancy `gdb' or `dbx' symbol table entries or which must
deal with stripped binaries may be unable to print argument
lists in stack traces.
------------------------------------------------------------------------
This was obvious to some of us, but we've had complaints from
users that this was not at all obvious to them...
Enjoy,
Donn Seeley University of Utah CS Dept donn@cs.utah.edu
40 46' 6"N 111 50' 34"W (801) 581-5668 utah-cs!donn