This is the mail archive of the gcc-patches@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]
Other format: [Raw text]

Re: More SJLJ exception breakage


Richard Guenther wrote:
> On Thu, Apr 16, 2009 at 8:24 PM, Ulrich Weigand <uweigand@de.ibm.com> wrote:
> > It would appear the call to _Unwind_SjLj_Unregister is simply inserted
> > too late; it needs to take place *before* the result register is set up
> > (insn 33), not afterwards.
> >
> >
> > Any idea why this might happen or suggestions what else to check?
> 
> Maybe fixed after http://gcc.gnu.org/ml/gcc-cvs/2009-04/msg00805.html?
> Or needs a similar fix in the place wherever we handle SJLJ exceptions
> in the middle-end?

Well, the _Unwind_SjLj_Unregister call is introduced only at the RTL
level; I'm not sure how gimple changes can affect this?

In any case, I now understand the problem a bit better.  Fundamentally,
this is about the assumption in sjlj_emit_function_exit that the place
where to insert the unregister call -that was announced (from function.c)
via the sjlj_emit_function_exit_after routine- is located within the
last basic block, i.e. the one with a fall-through edge to EXIT.

In GCC 4.4, this is true; sjlj_emit_function_exit_after installs the
code_label 32 as place to call the unregister function, and at the
time sjlj_emit_function_exit is called, the RTL looks like:

(code_label/s 32 31 41 7 1 "" [1 uses])

(note 41 32 33 7 [bb 7] NOTE_INSN_BASIC_BLOCK)

(insn 33 41 39 7 new_op.ii:23 (set (reg/i:SI 3 $3)
        (reg:SI 138 [ <result> ])) -1 (nil))

(insn 39 33 0 7 new_op.ii:23 (use (reg/i:SI 3 $3)) -1 (nil))


In mainline GCC, however, sjlj_emit_function_exit_after also installs
code_label 32, but the RTL on entry to sjlj_emit_function_exit is:

(code_label 32 37 41 8 1 "" [1 uses])

(note 41 32 33 8 [bb 8] NOTE_INSN_BASIC_BLOCK)

(insn 33 41 38 8 new_op.ii:23 (set (reg/i:SI 3 $3)
        (reg:SI 138 [ <result> ])) -1 (nil))

(code_label 38 33 42 9 4 "" [1 uses])

(note 42 38 39 9 [bb 9] NOTE_INSN_BASIC_BLOCK)

(insn 39 42 0 9 new_op.ii:23 (use (reg/i:SI 3 $3)) -1 (nil))

So the last basic block is the one starting at code_label 38.


Now, the immediate cause for the difference is the following
patch by Honza:
http://gcc.gnu.org/ml/gcc-patches/2009-04/msg00375.html

With GCC 4.4, expand in fact generates the extra block at
code_label 38 as well, however, by the time sjlj_emit_function_exit
is called, the CFG has been cleaned up to remove it.  This cleanup
code was removed by the patch above.

In fact, the code generated by expand_function_end looks the same
for mainline and GCC 4.4:

;; Start of basic block () -> 7
(note 40 31 34 7 [bb 7] NOTE_INSN_BASIC_BLOCK)

(insn 34 40 35 7 new_op.ii:23 (clobber (reg/i:SI 3 $3)) -1 (nil))

(insn 35 34 36 7 new_op.ii:23 (clobber (reg:SI 138 [ <result> ])) -1 (nil))

(jump_insn 36 35 37 7 new_op.ii:23 (set (pc)
        (label_ref 38)) -1 (nil))
;; End of basic block 7 -> ( 9)

;; Succ edge  9 [100.0%]

(barrier 37 36 32)

;; Start of basic block ( 5) -> 8
;; Pred edge  5 [100.0%]
(code_label 32 37 41 8 1 "" [1 uses])

(note 41 32 33 8 [bb 8] NOTE_INSN_BASIC_BLOCK)

(insn 33 41 38 8 new_op.ii:23 (set (reg/i:SI 3 $3)
        (reg:SI 138 [ <result> ])) -1 (nil))
;; End of basic block 8 -> ( 9)

;; Succ edge  9 [100.0%]  (fallthru)

;; Start of basic block ( 7 8) -> 9
;; Pred edge  7 [100.0%]
;; Pred edge  8 [100.0%]  (fallthru)
(code_label 38 33 42 9 4 "" [1 uses])

(note 42 38 39 9 [bb 9] NOTE_INSN_BASIC_BLOCK)

(insn 39 42 0 9 new_op.ii:23 (use (reg/i:SI 3 $3)) -1 (nil))
;; End of basic block 9 -> ( 1)

;; Succ edge  EXIT [100.0%]  (fallthru)


In GCC 4.4, the delete_unreachable_block call in rest_of_handle_jump
then removes basic block 7; and the cleanup_cfg call in rest_of_handle_eh
then merges basic block 9 into 8.

The following patch, reverting those pieces of Honza's patch, fixes
the problem for me:

Index: except.c
===================================================================
*** except.c    (revision 146246)
--- except.c    (working copy)
*************** gate_handle_eh (void)
*** 4267,4272 ****
--- 4267,4273 ----
  static unsigned int
  rest_of_handle_eh (void)
  {
+   cleanup_cfg (CLEANUP_NO_INSN_DEL);
    finish_eh_generation ();
    cleanup_cfg (CLEANUP_NO_INSN_DEL);
    return 0;
Index: cfgcleanup.c
===================================================================
*** cfgcleanup.c        (revision 146246)
--- cfgcleanup.c        (working copy)
*************** cleanup_cfg (int mode)
*** 2198,2203 ****
--- 2198,2205 ----
  static unsigned int
  rest_of_handle_jump (void)
  {
+   delete_unreachable_blocks ();
+
    if (crtl->tail_call_emit)
      fixup_tail_calls ();
    return 0;


However, I'm wondering why expand_function_end deliberately generates an
unreachable basic block, which (in 4.4) was subsequently *always* deleted
again in the immediately following pass ...

This is done by the following piece of code:

  /* Emit the actual code to clobber return register.  */
  {
    rtx seq;

    start_sequence ();
    clobber_return_register ();
    expand_naked_return ();
    seq = get_insns ();
    end_sequence ();

    emit_insn_after (seq, clobber_after);
  }

  /* Output the label for the naked return from the function.  */
  emit_label (naked_return_label);

I'm not quite sure whether this is really still necessary (in particular
with the new DF and IRA infrastructure) ...


Any suggestions how to best fix this?

Thanks,
Ulrich

-- 
  Dr. Ulrich Weigand
  GNU Toolchain for Linux on System z and Cell BE
  Ulrich.Weigand@de.ibm.com


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