This is the mail archive of the gcc-bugs@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Re: __builtin_longjmp problem


> From: Artem Alimarine <aalimari@best.ms.philips.com>
> Date: Wed, 25 Aug 1999 13:18:45 +0200

> I use gcc-2.95 on Cygwin for i960-unknown-coff. The exception
> handling does not work. As far as I could find it is due to
> __builtin_setjmp/longjmp. The following code hangs on VxWorks 5.1:

> int jbuf[1024];

> if(__builtin_setjmp(jbuf) == 0) {
>      __builtin_longjmp(jbuf, 1);
> } else {
>      printf("jumped\n");
> }

> In the assembler I can see that the code for longjmp is wrong:

> code generated for __bulitin_setjmp (seems OK for me):

>     ldconst _jbuf,g4
>     st fp,(g4)
>     ldconst L17,g5
>     st g5,4(g4)
>     st sp,8(g4)
>     mov 0,g4

Yeah, looks fine to me too.

> code generated for __buitin_longjmp(seems wrong):

>     ldconst _jbuf,g4
>     flushreg
>     mov 1,fp
>     lda -64(fp),pfp
>     ld (g4),g5
>     st g5,-56(fp)
>     ld 8(g4),g4
>     st g4,-60(fp)
>     ret

Yeah, I get:

_mainm__Fv:
        lda     48(sp),sp
        #Prologue stats:
        #  Total Frame Size: 48 bytes
        #  Local Variable Size: 48 bytes
        #End Prologue#
        st      g14,64(fp)
        ldconst _jbuf,g4
        st      fp,(g4)
        ldconst L2,g5
        st      g5,4(g4)
        st      sp,8(g4)
        st      g14,80(fp)
        b       L3
        .align 3
L2:
        ld      64(fp),g4
        mov     g4,g14
        st      r3,96(fp)
        ld      96(fp),g7
        callx   (g7)
        mov     1,g7
        st      g7,80(fp)
L3:
        ld      80(fp),g7
        cmpi    g7,0
        bne L1
        ldconst _jbuf,g6
        ld      (g6),g4
        ldconst ___dummy,g5
        ld      4(g6),g6
        flushreg
        st      g6,8(g4)
        st      g5,12(g4)
        mov     g4,pfp
        ret
        .align 3
        b       L4
        .align 3
L1:
        ldconst LC0,g0
        callx   _printf
L4:
        ret

from T2 (a tested product where EH works on the i960).  I have yet to
test out EH on newer compilers on i960, so I can't say much about my
internal tree.  Ok, I just checked, I get the same bad code as you.
:-(

Below I will attempt to show you what is in the T2 compiler...  I
thought I got all the i960 changes into gcc.

> Is that possible to make compiler to generate calls to a  setjmp/longjmp
> from the library for exception handling?

Bad idea, don't do this.

Here are the changelog entries I had...  The can probably find
relevant hits in gcc-patches and gcc (never mind, I reproduced them
below).

23-Apr-1998

	* expr.c (expand_builtin_setjmp): Add support to use the
        hard_frame_pointer_rtx instead of virtual_stack_vars_rtx for
        machines that require it.
        * i960.h (SETUP_FRAME_ADDRESSES): Define.
        (NEED_HARD_FRAME_FOR_NONLOCAL_GOTO): Define.
        * i960.md (ret): Define support routine for nonlocal_goto.
        (nonlocal_goto): Define.  Doesn't yet support nested function
        nonlocal gotos.
        (flush_register_windows): Define.

from i960.h:

+ /* Generate RTL to flush the register windows so as to make arbitrary frames
+    available.  */
+ #define SETUP_FRAME_ADDRESSES()               \
+   emit_insn (gen_flush_register_windows ())
+ 
+ #define NEED_HARD_FRAME_FOR_NONLOCAL_GOTO 1
+ 

from i960.md:

+ ;; A return instruction.  Used only by nonlocal_goto to change the
+ ;; stack pointer, frame pointer, previous frame pointer and the return
+ ;; instruction pointer.
+ (define_insn "ret"
+   [(use (reg:SI 16))
+    (unspec_volatile [(const_int 0)] 3)]
+   ""
+   "ret"
+   [(set_attr "type" "branch")
+    (set_attr "length" "1")])
+ 
+ (define_expand "nonlocal_goto"
+   [(match_operand:SI 0 "" "")
+    (match_operand:SI 1 "general_operand" "")
+    (match_operand:SI 2 "general_operand" "")
+    (match_operand:SI 3 "general_operand" "")]
+   ""
+   "
+ {
+   rtx fp = operands[0];
+   rtx chain = operands[1];
+   rtx stack = operands[2];
+   rtx lab = operands[3];
+ 
+   /* This code isn't sufficient to make nonlocal_gotos for nested
+      functions to work fully.  Here we assume that the passed frame
+      pointer is a real hard frame pointer, not a
+      virtual_stack_vars_rtx type of frame.  */
+ 
+   /* We must restore the stack pointer, frame pointer, previous frame
+      pointer and the return instruction pointer.  Since the ret
+      instruction does all this for us with one instruction, we arrange
+      everything so that ret will do everything we need done.  */
+ 
+   if (GET_CODE (fp) != REG)
+     fp = force_reg (Pmode, fp);
+   if (GET_CODE (lab) != REG)
+     lab = force_reg (Pmode, lab);
+   if (GET_CODE (chain) != REG)
+     chain = force_reg (Pmode, chain);
+ 
+ 
+   /* First, we must flush the register windows, so that we can modify
+      the saved local registers on the stack directly and because we
+      are going to change the previous frame pointer.  */
+ 
+   emit_insn (gen_flush_register_windows ());
+ 
+   /* Next, we put the address that we want to transfer to, into the
+      saved $rip value on the stack.  Once we ret below, that value
+      will be loaded into the pc (IP).  */
+ 
+   emit_move_insn (gen_rtx (MEM, SImode,
+ 			   plus_constant (fp, 8)),
+ 		  chain);
+ 
+   /* Next, we put the address of __dummy into the static chain
+      register's save area on the stack.  After the ret below, this
+      will be loaded into r3 (the static chain).  */
+      
+   emit_move_insn (gen_rtx (MEM, SImode,
+ 			   plus_constant (fp, 12)),
+ 		  lab);
+ 
+   /* We now load pfp (the previous frame pointer) with the value that
+      we want fp to be.  */
+ 
+   emit_move_insn (gen_rtx (REG, SImode, 16), fp);
+ 
+   /* And finally, we can now just ret to get all the values saved
+      above into all the right registers, and also, all the local
+      register that were in use in the function, are restored from
+      their saved values (from the call instruction) on the stack
+      because we are very careful to ret from the exact save area in
+      use during the original call.  */
+ 
+   emit_insn (gen_ret ());
+   emit_barrier ();
+   DONE;
+ }")
+ 
+ ;; Special insn to flush register windows.
+ (define_insn "flush_register_windows"
+   [(unspec_volatile [(const_int 0)] 1)]
+   ""
+   "flushreg"
+   [(set_attr "type" "misc")
+    (set_attr "length" "1")])
+ 

*** expr.c.before	Mon Jan 12 18:49:06 1998
--- expr.c	Thu Apr 23 15:38:22 1998
*************** expand_builtin_setjmp (buf_addr, target)
*** 7760,7770 ****
  
    emit_queue ();
  
!   /* We store the frame pointer and the address of lab1 in the buffer
!      and use the rest of it for the stack save area, which is
!      machine-dependent.  */
!   emit_move_insn (gen_rtx (MEM, Pmode, buf_addr),
! 		  virtual_stack_vars_rtx);
    emit_move_insn
      (validize_mem (gen_rtx (MEM, Pmode,
  			    plus_constant (buf_addr,
--- 7760,7780 ----
  
    emit_queue ();
  
! #if defined (HAVE_nonlocal_goto) && defined (NEED_HARD_FRAME_FOR_NONLOCAL_GOTO)
!   if (HAVE_nonlocal_goto && NEED_HARD_FRAME_FOR_NONLOCAL_GOTO)
!     /* We store the frame pointer and the address of lab1 in the buffer
!        and use the rest of it for the stack save area, which is
!        machine-dependent.  */
!     emit_move_insn (gen_rtx (MEM, Pmode, buf_addr),
! 		    hard_frame_pointer_rtx);
!   else
! #endif
!     /* We store the frame pointer and the address of lab1 in the buffer
!        and use the rest of it for the stack save area, which is
!        machine-dependent.  */
!     emit_move_insn (gen_rtx (MEM, Pmode, buf_addr),
! 		    virtual_stack_vars_rtx);
! 
    emit_move_insn
      (validize_mem (gen_rtx (MEM, Pmode,
  			    plus_constant (buf_addr,


I checked my logs, and it went it.  I first sent in:

1998-04-23  Mike Stump  <mrs@wrs.com>

	* expr.c (expand_builtin_setjmp): Add support to use the
	hard_frame_pointer_rtx instead of virtual_stack_vars_rtx for
	machines that require it.
	* i960.h (SETUP_FRAME_ADDRESSES): Define.
	(NEED_HARD_FRAME_FOR_NONLOCAL_GOTO): Define.
	* i960.md (ret): Define support routine for nonlocal_goto.
	(nonlocal_goto): Define.  Doesn't yet support nested function
	nonlocal gotos.
	(flush_register_windows): Define.

Doing diffs in expr.c.~1~:
*** expr.c.~1~	Fri Apr 17 16:14:36 1998
--- expr.c	Thu Apr 23 15:21:24 1998
*************** expand_builtin_setjmp (buf_addr, target,
*** 7689,7699 ****
  
    emit_queue ();
  
!   /* We store the frame pointer and the address of lab1 in the buffer
!      and use the rest of it for the stack save area, which is
!      machine-dependent.  */
!   emit_move_insn (gen_rtx_MEM (Pmode, buf_addr),
! 		  virtual_stack_vars_rtx);
    emit_move_insn (validize_mem
  		  (gen_rtx_MEM (Pmode,
  				plus_constant (buf_addr,
--- 7689,7709 ----
  
    emit_queue ();
  
! #if defined (HAVE_nonlocal_goto) && defined (NEED_HARD_FRAME_FOR_NONLOCAL_GOTO)
!   if (HAVE_nonlocal_goto && NEED_HARD_FRAME_FOR_NONLOCAL_GOTO)
!     /* We store the frame pointer and the address of lab1 in the buffer
!        and use the rest of it for the stack save area, which is
!        machine-dependent.  */
!     emit_move_insn (gen_rtx_MEM (Pmode, buf_addr),
! 		    hard_frame_pointer_rtx);
!   else
! #endif
!     /* We store the frame pointer and the address of lab1 in the buffer
!        and use the rest of it for the stack save area, which is
!        machine-dependent.  */
!     emit_move_insn (gen_rtx_MEM (Pmode, buf_addr),
! 		    virtual_stack_vars_rtx);
! 
    emit_move_insn (validize_mem
  		  (gen_rtx_MEM (Pmode,
  				plus_constant (buf_addr,
--------------
Doing diffs in config/i960/i960.h.~1~:
*** config/i960/i960.h.~1~	Wed Apr  1 10:58:27 1998
--- config/i960/i960.h	Thu Apr 23 15:16:34 1998
*************** extern struct rtx_def *gen_compare_reg (
*** 1496,1501 ****
--- 1496,1508 ----
  		  CXT);							\
  }
  
+ /* Generate RTL to flush the register windows so as to make arbitrary frames
+    available.  */
+ #define SETUP_FRAME_ADDRESSES()		\
+   emit_insn (gen_flush_register_windows ())
+ 
+ #define NEED_HARD_FRAME_FOR_NONLOCAL_GOTO 1
+ 
  #if 0
  /* Promote char and short arguments to ints, when want compatibility with
     the iC960 compilers.  */
--------------
Doing diffs in config/i960/i960.md.~1~:
*** config/i960/i960.md.~1~	Fri Apr 10 14:05:52 1998
--- config/i960/i960.md	Thu Apr 23 15:16:34 1998
***************
*** 2287,2292 ****
--- 2287,2381 ----
    "* return i960_output_ret_insn (insn);"
    [(set_attr "type" "branch")])
  
+ ;; A return instruction.  Used only by nonlocal_goto to change the
+ ;; stack pointer, frame pointer, previous frame pointer and the return
+ ;; instruction pointer.
+ (define_insn "ret"
+   [(use (reg:SI 16))
+    (unspec_volatile [(const_int 0)] 3)]
+   ""
+   "ret"
+   [(set_attr "type" "branch")
+    (set_attr "length" "1")])
+ 
+ (define_expand "nonlocal_goto"
+   [(match_operand:SI 0 "" "")
+    (match_operand:SI 1 "general_operand" "")
+    (match_operand:SI 2 "general_operand" "")
+    (match_operand:SI 3 "general_operand" "")]
+   ""
+   "
+ {
+   rtx fp = operands[1];
+   rtx new_pc = operands[3];
+   rtx stack = operands[2];
+   rtx val = operands[0];
+ 
+   /* This code isn't sufficient to make nonlocal_gotos for nested
+      functions to work fully.  Here we assume that the passed frame
+      pointer is a real hard frame pointer, not a
+      virtual_stack_vars_rtx type of frame.  */
+ 
+   /* We must restore the stack pointer, frame pointer, previous frame
+      pointer and the return instruction pointer.  Since the ret
+      instruction does all this for us with one instruction, we arrange
+      everything so that ret will do everything we need done.  */
+ 
+   if (GET_CODE (fp) != REG)
+     fp = force_reg (Pmode, fp);
+   if (GET_CODE (val) != REG)
+     val = force_reg (Pmode, val);
+   if (GET_CODE (new_pc) != REG)
+     new_pc = force_reg (Pmode, new_pc);
+ 
+ 
+   /* First, we must flush the register windows, so that we can modify
+      the saved local registers on the stack directly and because we
+      are going to change the previous frame pointer.  */
+ 
+   emit_insn (gen_flush_register_windows ());
+ 
+   /* Next, we put the address that we want to transfer to, into the
+      saved $rip value on the stack.  Once we ret below, that value
+      will be loaded into the pc (IP).  */
+ 
+   emit_move_insn (gen_rtx (MEM, SImode,
+ 			   plus_constant (fp, 8)),
+ 		  new_pc);
+ 
+   /* Next, we put the value into the static chain register's save
+      area on the stack.  After the ret below, this will be loaded into
+      r3 (the static chain).  */
+      
+   emit_move_insn (gen_rtx (MEM, SImode,
+ 			   plus_constant (fp, 12)),
+ 		  val);
+ 
+   /* We now load pfp (the previous frame pointer) with the value that
+      we want fp to be.  */
+ 
+   emit_move_insn (gen_rtx (REG, SImode, 16), fp);
+ 
+   /* And finally, we can now just ret to get all the values saved
+      above into all the right registers, and also, all the local
+      register that were in use in the function, are restored from
+      their saved values (from the call instruction) on the stack
+      because we are very careful to ret from the exact save area in
+      use during the original call.  */
+ 
+   emit_insn (gen_ret ());
+   emit_barrier ();
+   DONE;
+ }")
+ 
+ ;; Special insn to flush register windows.
+ (define_insn "flush_register_windows"
+   [(unspec_volatile [(const_int 0)] 1)]
+   ""
+   "flushreg"
+   [(set_attr "type" "misc")
+    (set_attr "length" "1")])
+ 
  (define_insn "nop"
    [(const_int 0)]
    ""
--------------

and then I addressed some comments and sent in :

Subject: Re: improved i960 builtin setjmp/longjmp handling (PATCH)

Ok, all outstanding suggestions taken in consideration.  Below is the
version kenner likes (updated to fit into egcs)...  Can this go in?


>From kenner@vlsi1.ultra.nyu.edu Sat May 23 19:19 PDT 1998
Date: Sat, 23 May 98 22:21:25 EDT
From: kenner@vlsi1.ultra.nyu.edu (Richard Kenner)
To: mrs@wrs.com
Subject: Re:  improved i960 builtin setjmp/longjmp handling

Thanks.

I took rth's suggestion and modified this slightly, as enclosed below.
Please doublecheck I didn't break anything.

1998-04-23  Mike Stump  <mrs@wrs.com>

	* expr.c (expand_builtin_setjmp): Handle BUILTIN_SETJMP_FRAME_VALUE.
	* i960.h (SETUP_FRAME_ADDRESSES, BUILTIN_SETJMP_FRAME_VALUE): Define.
	* i960.md (ret, flush_register_windows): Define.
	(nonlocal_goto): Likewise.  Nested function nonlocal gotos don't
	work yet.

Doing diffs in gcc:
*** ./config/i960/i960.h.~1~	Fri May 15 17:45:59 1998
--- ./config/i960/i960.h	Tue May 26 16:31:55 1998
*************** extern struct rtx_def *gen_compare_reg (
*** 1495,1500 ****
--- 1495,1507 ----
  		  CXT);							\
  }
  
+ /* Generate RTL to flush the register windows so as to make arbitrary frames
+    available.  */
+ #define SETUP_FRAME_ADDRESSES()		\
+   emit_insn (gen_flush_register_windows ())
+ 
+ #define BUILTIN_SETJMP_FRAME_VALUE hard_frame_pointer_rtx
+ 
  #if 0
  /* Promote char and short arguments to ints, when want compatibility with
     the iC960 compilers.  */
*** ./config/i960/i960.md.~1~	Thu May  7 11:52:35 1998
--- ./config/i960/i960.md	Tue May 26 16:31:55 1998
***************
*** 2287,2292 ****
--- 2287,2381 ----
    "* return i960_output_ret_insn (insn);"
    [(set_attr "type" "branch")])
  
+ ;; A return instruction.  Used only by nonlocal_goto to change the
+ ;; stack pointer, frame pointer, previous frame pointer and the return
+ ;; instruction pointer.
+ (define_insn "ret"
+   [(use (reg:SI 16))
+    (unspec_volatile [(const_int 0)] 3)]
+   ""
+   "ret"
+   [(set_attr "type" "branch")
+    (set_attr "length" "1")])
+ 
+ (define_expand "nonlocal_goto"
+   [(match_operand:SI 0 "" "")
+    (match_operand:SI 1 "general_operand" "")
+    (match_operand:SI 2 "general_operand" "")
+    (match_operand:SI 3 "general_operand" "")]
+   ""
+   "
+ {
+   rtx fp = operands[1];
+   rtx new_pc = operands[3];
+   rtx stack = operands[2];
+   rtx val = operands[0];
+ 
+   /* This code isn't sufficient to make nonlocal_gotos for nested
+      functions to work fully.  Here we assume that the passed frame
+      pointer is a real hard frame pointer, not a
+      virtual_stack_vars_rtx type of frame.  */
+ 
+   /* We must restore the stack pointer, frame pointer, previous frame
+      pointer and the return instruction pointer.  Since the ret
+      instruction does all this for us with one instruction, we arrange
+      everything so that ret will do everything we need done.  */
+ 
+   if (GET_CODE (fp) != REG)
+     fp = force_reg (Pmode, fp);
+   if (GET_CODE (val) != REG)
+     val = force_reg (Pmode, val);
+   if (GET_CODE (new_pc) != REG)
+     new_pc = force_reg (Pmode, new_pc);
+ 
+ 
+   /* First, we must flush the register windows, so that we can modify
+      the saved local registers on the stack directly and because we
+      are going to change the previous frame pointer.  */
+ 
+   emit_insn (gen_flush_register_windows ());
+ 
+   /* Next, we put the address that we want to transfer to, into the
+      saved $rip value on the stack.  Once we ret below, that value
+      will be loaded into the pc (IP).  */
+ 
+   emit_move_insn (gen_rtx (MEM, SImode,
+ 			   plus_constant (fp, 8)),
+ 		  new_pc);
+ 
+   /* Next, we put the value into the static chain register's save
+      area on the stack.  After the ret below, this will be loaded into
+      r3 (the static chain).  */
+      
+   emit_move_insn (gen_rtx (MEM, SImode,
+ 			   plus_constant (fp, 12)),
+ 		  val);
+ 
+   /* We now load pfp (the previous frame pointer) with the value that
+      we want fp to be.  */
+ 
+   emit_move_insn (gen_rtx (REG, SImode, 16), fp);
+ 
+   /* And finally, we can now just ret to get all the values saved
+      above into all the right registers, and also, all the local
+      register that were in use in the function, are restored from
+      their saved values (from the call instruction) on the stack
+      because we are very careful to ret from the exact save area in
+      use during the original call.  */
+ 
+   emit_insn (gen_ret ());
+   emit_barrier ();
+   DONE;
+ }")
+ 
+ ;; Special insn to flush register windows.
+ (define_insn "flush_register_windows"
+   [(unspec_volatile [(const_int 0)] 1)]
+   ""
+   "flushreg"
+   [(set_attr "type" "misc")
+    (set_attr "length" "1")])
+ 
  (define_insn "nop"
    [(const_int 0)]
    ""
*** ./tm.texi.~1~	Tue May 26 13:08:53 1998
--- ./tm.texi	Tue May 26 16:31:54 1998
*************** of @var{frameaddr}---that is, the stack 
*** 2188,2199 ****
  address of the stack word that points to the previous frame.
  
  @findex SETUP_FRAME_ADDRESSES
! @item SETUP_FRAME_ADDRESSES ()
  If defined, a C expression that produces the machine-specific code to
  setup the stack so that arbitrary frames can be accessed.  For example,
  on the Sparc, we must flush all of the register windows to the stack
! before we can access arbitrary stack frames.
! This macro will seldom need to be defined.
  
  @findex RETURN_ADDR_RTX
  @item RETURN_ADDR_RTX (@var{count}, @var{frameaddr})
--- 2188,2207 ----
  address of the stack word that points to the previous frame.
  
  @findex SETUP_FRAME_ADDRESSES
! @item SETUP_FRAME_ADDRESSES
  If defined, a C expression that produces the machine-specific code to
  setup the stack so that arbitrary frames can be accessed.  For example,
  on the Sparc, we must flush all of the register windows to the stack
! before we can access arbitrary stack frames.  You will seldom need to
! define this macro.
! 
! @findex BUILTIN_SETJMP_FRAME_VALUE
! @item BUILTIN_SETJMP_FRAME_VALUE
! If defined, a C expression that contains an rtx that is used to store
! the address of the current frame into the built in @code{setjmp} buffer.
! The default value, @code{virtual_stack_vars_rtx}, is correct for most
! machines.  One reason you may need to define this macro is if
! @code{hard_frame_pointer_rtx} is the appropriate value on your machine.
  
  @findex RETURN_ADDR_RTX
  @item RETURN_ADDR_RTX (@var{count}, @var{frameaddr})
*** ./expr.c.~1~	Tue May 26 13:08:34 1998
--- ./expr.c	Tue May 26 16:35:12 1998
*************** expand_builtin_setjmp (buf_addr, target,
*** 7725,7735 ****
  
    emit_queue ();
  
    /* We store the frame pointer and the address of lab1 in the buffer
       and use the rest of it for the stack save area, which is
       machine-dependent.  */
    emit_move_insn (gen_rtx_MEM (Pmode, buf_addr),
! 		  virtual_stack_vars_rtx);
    emit_move_insn (validize_mem
  		  (gen_rtx_MEM (Pmode,
  				plus_constant (buf_addr,
--- 7725,7739 ----
  
    emit_queue ();
  
+ #ifndef BUILTIN_SETJMP_FRAME_VALUE
+ #define BUILTIN_SETJMP_FRAME_VALUE virtual_stack_vars_rtx
+ #endif
+ 
    /* We store the frame pointer and the address of lab1 in the buffer
       and use the rest of it for the stack save area, which is
       machine-dependent.  */
    emit_move_insn (gen_rtx_MEM (Pmode, buf_addr),
! 		  BUILTIN_SETJMP_FRAME_VALUE);
    emit_move_insn (validize_mem
  		  (gen_rtx_MEM (Pmode,
  				plus_constant (buf_addr,
--------------

Maybe something got broken by someone else, maybe the first patches
was better...  Anyway, I'll probably look at in in the next few
months, if you want to wait.  If you don't want to, play with the raw
bits, and see if you can track down why it broke.

Try a dated cvs tree from around that time, just after my work went in
and see what type of code was generated at that time (as compared to
now).

We be appreciative if you had the time to track this down and help
us fix it.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]