[comp.os.minix] A patch to make gcc 1.39 work with Minix setjmp

jkp@cs.HUT.FI (Jyrki Kuoppala) (03/11/91)

As distributed, Minix (1.5.10 and I think also 1.3) setjmp library
routine doesn't save the registers which are not allowed to be
clobbered by the called function.  Generally, Unix saves all registers
in setjmp.  In my opinion, both of these methods seem to be about as
good, also there are some points to support the Minix style.

However, the compiler must be aware of when setjmp is called and must
generate code to save the registers which are not allowed to be
clobbered by the function.  Otherwise trouble is on the way.  This
kept me puzzled for a long time when I was porting emacs 18.57 to the
pc532, and didn't yet have a working debugger.

This diff makes gcc work right on those machines where the library
routine `setjmp' doesn't save registers and `longjmp' doesn't do stack
unwinding.  Normally on these machines / environments code using
`setjmp' fails if compiled with gcc, because call-saved registers are
not saved in the function calling `setjmp' if they are not used.  But
they might be used in functions called from the function calling
`setjmp' - the trouble is they never get restored when longjmp is
called.

This problem seems to exist at least on Minix, where the normal
`setjmp' library function doesn't save the registers.  There is no
problem with the normal Minix compilers (I think), because they (I'm
told at least ACK and Bruce Evans's bcc) save all regs in the prologue
of a function calling `setjmp'.

There really is no requirement to save all the registers in `setjmp'.
The tradition in Unix seems to be that all registers are saved - I'm
told that in BSD this might be because partly the same mechanism is
used for handling signals and handling `setjmp'.  Of course in signal
handling it's a must to save all the registers.

There's no big difference to do it either way.  Perhaps to do it with
a `setjmp' not saving registers is a bit more efficient, because then
some registers don't have to be saved twice (first in the function
prologue for those used in the function, then all registers in
`setjmp').  If `setjmp' is called several times in a function,
non-reg-saving `setjmp' is also a win.

This modification requires reliable detection of `setjmp' by gcc.
This is possible to do, because ANSI says that `setjmp' is a macro
defined in <setjmp.h>.  So `setjmp' can never be called via a pointer.
Gcc currently (as of version 1.39) detects calls to `setjmp' by
examining if the function called is named "setjmp" or "_setjmp" (see
expr.c).  If `setjmp' is defined differently in your <setjmp.h>, you
have to modify either the definition or gcc to make the detection
work.

To tell gcc that your setjmp does not save all regs, add the line:

#define NON_REG_SAVING_SETJMP

to your tm.h file.

I have submitted this patch to gcc maintainers, so it might be
included in a future gcc release if considered useful.  This should be
target machine-independent, but any bug reports are welcome.

*** orig/final.c	Mon Aug  6 22:12:00 1990
--- final.c	Mon Mar  4 21:15:05 1991
***************
*** 42,71 ****
--- 42,74 ----
     The code for the function prologue and epilogue are generated
     directly as assembler code by the macros FUNCTION_PROLOGUE and
     FUNCTION_EPILOGUE.  Those instructions never exist as rtl.  */
  
  #include <stdio.h>
  #include "config.h"
  #include "rtl.h"
  #include "regs.h"
  #include "insn-config.h"
  #include "recog.h"
  #include "conditions.h"
  #include "gdbfiles.h"
  #include "flags.h"
  #include "real.h"
  #include "output.h"
+ #ifdef NON_REG_SAVING_SETJMP
+ #include "hard-reg-set.h"
+ #endif
  
  /* Get N_SLINE and N_SOL from stab.h if we can expect the file to exist.  */
  #ifdef DBX_DEBUGGING_INFO
  #ifdef USG
  #include "stab.h"  /* If doing DBX on sysV, use our own stab.h.  */
  #else
  #include <stab.h>  /* On BSD, use the system's stab.h.  */
  #endif /* not USG */
  #endif /* DBX_DEBUGGING_INFO */
  
  /* .stabd code for line number.  */
  #ifndef N_SLINE
  #define	N_SLINE	0x44
  #endif
  
***************
*** 361,390 ****
--- 364,404 ----
     FILE is the file to write assembler code to.
     WRITE_SYMBOLS says which kind of debugging info to write (or none).
     OPTIMIZE is nonzero if we should eliminate redundant
       test and compare insns.  */
  
  void
  final_start_function (first, file, write_symbols, optimize)
       rtx first;
       FILE *file;
       enum debugger write_symbols;
       int optimize;
  {
    block_depth = 0;
  
    this_is_asm_operands = 0;
+ 
+ #ifdef NON_REG_SAVING_SETJMP
+   if (current_function_calls_setjmp)
+     {
+       int i;
+ 
+       for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ 	if (!call_used_regs[i] && !call_fixed_regs[i])
+ 	  regs_ever_live[i] = 1;
+     }
+ #endif
  
    /* Record beginning of the symbol-block that's the entire function.  */
  
    if (write_symbols == GDB_DEBUG)
      {
        pending_blocks[block_depth++] = next_block_index;
        fprintf (file, "\t.gdbbeg %d\n", next_block_index++);
      }
  
    /* Initial line number is supposed to be output
       before the function's prologue and label
       so that the function's address will not appear to be
       in the last statement of the preceding function.  */
    if (NOTE_LINE_NUMBER (first) != NOTE_INSN_DELETED)
      {
-- 
Jyrki Kuoppala    Helsinki University of Technology, Finland.
Internet :        jkp@cs.hut.fi           [130.233.251.253]
X400 :            /C=fi/A=fumail/P=inet/O=hut/OU=cs/S=Kuoppala/G=Jyrki
BITNET :          jkp@fingate.bitnet      Gravity is a myth, the Earth sucks!

HBO043%DJUKFA11.BITNET@cunyvm.cuny.edu (Christoph van Wuellen) (03/11/91)

The problem is not a 'faulty' compiler, but setjmp/longjmp is a real mess.

Declare everthing 'volatile' in functions fiddling with setjmp/longjmp.
This might perhaps cure the problem.

C.v.W.

ladd@cbnewsc.att.com (david.ladd) (03/13/91)

In article <1991Mar10.223859.380@santra.uucp> jkp@cs.HUT.FI (Jyrki Kuoppala) writes:
>As distributed, Minix (1.5.10 and I think also 1.3) setjmp library
>routine doesn't save the registers which are not allowed to be
>clobbered by the called function.  Generally, Unix saves all registers
>in setjmp.  In my opinion, both of these methods seem to be about as
>good, also there are some points to support the Minix style.
>
>However, the compiler must be aware of when setjmp is called and must
>generate code to save the registers which are not allowed to be

I quote from the gcc.info file:

`-traditional'
     Attempt to support some aspects of traditional C compilers.                          Specifically:

	....

        * All automatic variables not declared `register' are
          preserved by `longjmp'.  Ordinarily, GNU C follows ANSI C:
          automatic variables not declared `volatile' may be clobbered.              
To me, this suggests you either make everything volatile or use
gcc -traditional.  What's the big deal?

jkp@cs.HUT.FI (Jyrki Kuoppala) (03/14/91)

>To me, this suggests you either make everything volatile or use
>gcc -traditional.  What's the big deal?

Aaaaarrrgggghhh.  I would suggest that people read an article before
they reply to it like this.  I thought I was very verbose enough about
explaining the problem now, learned on an earlier forum that people
don't appear to think before they jump into conclusions.  Seems that
explaining things doesn't help.  Ah well.

Local variables are not the issue here, and -traditional does not
cure the problem I was having.

Someone also suggested that I should not blame a compiler when setjmp
is faulty.  I don't think I did blame a compiler, and to me it seems
neither one (gcc or setjmp) is faulty, they just don't act well
together.  Minix setjmp takes a different approach than setjmp on Unix
systems traditionally, and gcc as is doesn't have the functionality to
handle this.  I assume ACK and other Minix compiler do have the
functionality to take care no registers are presumed valid when
longjmp returns, so on Minix setjmp should work just fine along the
ANSI spec.  Haven't checked with ACK or other Minix compilers, though,
'cause I don't have them.

But to make this flame perhaps useful for someone, the patch I sent
doesn't cure all the problems - it only makes sure the function
calling setjmp doesn't clobber the parent function's variables.  To
make local variables safe (I hear ANSI says that they are guaranteed
to remain valid if they are not modified between setjmp and longjmp)
you'll need to put local variables in stack in functions calling
setjmp on OS's like Minix where setjmp doesn't save registers.

There might still be a problem with gcc storing something (like a
function address) in call-saved registers and assuming it to be there
when setjmp returns.  But this problem would appear also on machines
where longjump restores registers from stack instead of the jump
buffer, and there seems to be code in flow.c to take care of this.

Here's a diff for that which should fix local variables handling,
although I haven't tested it it should be simple enought so that no
bugs have crept in.

*** c-decl.c.orig	Tue Mar 12 01:22:03 1991
--- c-decl.c	Tue Mar 12 01:25:59 1991
***************
*** 4757,4768 ****
  
    /* Must mark the RESULT_DECL as being in this function.  */
  
    DECL_CONTEXT (DECL_RESULT (fndecl)) = fndecl;
  
!   /* Obey `register' declarations if `setjmp' is called in this fn.  */
!   if (flag_traditional && current_function_calls_setjmp)
      {
        setjmp_protect (DECL_INITIAL (fndecl));
        setjmp_protect_args ();
      }
  
--- 4757,4774 ----
  
    /* Must mark the RESULT_DECL as being in this function.  */
  
    DECL_CONTEXT (DECL_RESULT (fndecl)) = fndecl;
  
!   /* Obey `register' declarations if `setjmp' is called in this fn.
!   if (
! #ifndef NON_REG_SAVING_SETJMP
!   /* If setjmp doesn't save registers, we'll have to put all local
!      variables in the stack whether -traditional or not */
!     flag_traditional &&
! #endif
!       current_function_calls_setjmp)
      {
        setjmp_protect (DECL_INITIAL (fndecl));
        setjmp_protect_args ();
      }
  
*** function.c.orig	Tue Mar 12 01:31:54 1991
--- function.c	Tue Mar 12 01:34:59 1991
***************
*** 2877,2887 ****
    for (decl = BLOCK_VARS (block); decl; decl = TREE_CHAIN (decl))
      if ((TREE_CODE (decl) == VAR_DECL
  	 || TREE_CODE (decl) == PARM_DECL)
  	&& DECL_RTL (decl) != 0
  	&& GET_CODE (DECL_RTL (decl)) == REG
! 	&& ! TREE_REGDECL (decl))
        put_var_into_stack (decl);
    for (sub = BLOCK_SUBBLOCKS (block); sub; sub = TREE_CHAIN (sub))
      setjmp_protect (sub);
  }
  
--- 2877,2893 ----
    for (decl = BLOCK_VARS (block); decl; decl = TREE_CHAIN (decl))
      if ((TREE_CODE (decl) == VAR_DECL
  	 || TREE_CODE (decl) == PARM_DECL)
  	&& DECL_RTL (decl) != 0
  	&& GET_CODE (DECL_RTL (decl)) == REG
! #ifndef NON_REG_SAVING_SETJMP
! 	/* To make variables not clobbered on machines where setjmp
! 	   doesn't save registers, we'll have to put also those declared
! 	   as `register' in the stack. */
! 	&& ! TREE_REGDECL (decl)
! #endif
! 	)
        put_var_into_stack (decl);
    for (sub = BLOCK_SUBBLOCKS (block); sub; sub = TREE_CHAIN (sub))
      setjmp_protect (sub);
  }
  
***************
*** 2895,2905 ****
         decl; decl = TREE_CHAIN (decl))
      if ((TREE_CODE (decl) == VAR_DECL
  	 || TREE_CODE (decl) == PARM_DECL)
  	&& DECL_RTL (decl) != 0
  	&& GET_CODE (DECL_RTL (decl)) == REG
! 	&& ! TREE_REGDECL (decl))
        put_var_into_stack (decl);
  }
  
  /* Return the context-pointer register corresponding to DECL,
     or 0 if it does not need one.  */
--- 2901,2914 ----
         decl; decl = TREE_CHAIN (decl))
      if ((TREE_CODE (decl) == VAR_DECL
  	 || TREE_CODE (decl) == PARM_DECL)
  	&& DECL_RTL (decl) != 0
  	&& GET_CODE (DECL_RTL (decl)) == REG
! #ifndef NON_REG_SAVING_SETJMP
! 	&& ! TREE_REGDECL (decl)
! #endif
! 	)
        put_var_into_stack (decl);
  }
  
  /* Return the context-pointer register corresponding to DECL,
     or 0 if it does not need one.  */



//Jyrki

richard@aiai.ed.ac.uk (Richard Tobin) (03/14/91)

In article <1991Mar12.225635.29426@cbnewsc.att.com> ladd@cbnewsc.att.com (david.ladd) writes:
>        * All automatic variables not declared `register' are
>          preserved by `longjmp'.  Ordinarily, GNU C follows ANSI C:
>          automatic variables not declared `volatile' may be clobbered.

This has nothing to do with the problem.  If "-traditional" is set, in
a function that calls setjmp(), gcc only puts into registers those
variables declared "register".  Normally, it ignores register
declarations and puts whatever variables it feels like in registers.

The real problem is that the Minix setjmp() only saves those registers
that the Minix compiler needs saved.  If you use gcc, you need a
setjmp() that saves the registers that gcc needs saved.  The simplest
solution is to save all the registers.  This means that setjmp() is
good for all compilers (and for assembler too).

-- Richard
-- 
Richard Tobin,                       JANET: R.Tobin@uk.ac.ed             
AI Applications Institute,           ARPA:  R.Tobin%uk.ac.ed@nsfnet-relay.ac.uk
Edinburgh University.                UUCP:  ...!ukc!ed.ac.uk!R.Tobin