This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Re: More SJLJ exception breakage
- From: "Ulrich Weigand" <uweigand at de dot ibm dot com>
- To: richard dot guenther at gmail dot com (Richard Guenther)
- Cc: gcc-patches at gcc dot gnu dot org, hubicka at ucw dot cz
- Date: Fri, 17 Apr 2009 15:08:22 +0200 (CEST)
- Subject: 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