Re: [PATCH] Fix cfg_layout_merge_blocks (PR rtl-optimization/52139)
On Tue, 7 Feb 2012, Jakub Jelinek wrote: Hi! On the following testcase we hit two bugs during cfglayout merge_blocks. One is that b-il.rtl-header has some jumptable in it, followed by barrier. We call emit_insn_after_noloc to insert the whole b's header after BB_END (a) and then delete_insn_chain it, with the intention that only non-deletable insns like deleted label notes are kept around. Unfortunately delete_insn/remove_insn it uses isn't prepared to handle BARRIERs as part of a bb (i.e. if BB_END is equal to some barrier because of the emit_insn_after_noloc call, delete_insn_chain won't update BB_END properly). As barriers aren't part of a BB, instead of adjusting remove_insn this patch adjusts BB_END not to point to a barrier before calling delete_insn_chain. The second bug is that remove_insn ICEs if deleting an insn with NEXT_INSN NULL, unless that insn is part of a current sequence (or some sequence in the sequence stack). In the first version of the patch I've tried to avoid calling delete_insn on insns that have NEXT_INSN NULL, but given that having NULL NEXT_INSN is a pretty common situation when in cfglayout mode if the insn is at BB_END, I think it is better to allow that in remove_insn. Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? 2012-02-07 Jakub Jelinek ja...@redhat.com PR rtl-optimization/52139 * emit-rtl.c (remove_insn): Allow removing insns with NEXT_INSN NULL, if they are at BB_END. * cfgrtl.c (cfg_layout_merge_blocks): If BB_END is a BARRIER after emit_insn_after_noloc, move BB_END to the last non-BARRIER insn before it. Cleanup. * gcc.dg/pr52139.c: New test. --- gcc/emit-rtl.c.jj 2012-02-07 16:05:47.913534092 +0100 +++ gcc/emit-rtl.c2012-02-07 16:14:32.529734964 +0100 @@ -1,7 +1,7 @@ /* Emit RTL for the GCC expander. Copyright (C) 1987, 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, - 2010, 2011 + 2010, 2011, 2012 Free Software Foundation, Inc. This file is part of GCC. @@ -3957,7 +3957,19 @@ remove_insn (rtx insn) break; } - gcc_assert (stack); + if (stack == NULL) + { + /* In cfglayout mode allow remove_insn of + insns at the end of bb. */ + if (BARRIER_P (insn)) + { + gcc_assert (prev); + bb = BLOCK_FOR_INSN (prev); + } + else + bb = BLOCK_FOR_INSN (insn); + gcc_assert (bb BB_END (bb) == insn); + } Isn't it cheaper to do that before we scan all the sequences? Thus, I'd expect BLOCK_FOR_INSN to be NULL if the insn is in a sequence? Like simply Index: emit-rtl.c === --- emit-rtl.c (revision 183971) +++ emit-rtl.c (working copy) @@ -3946,7 +3946,7 @@ remove_insn (rtx insn) } else if (get_last_insn () == insn) set_last_insn (prev); - else + else if (!BLOCK_FOR_INSN (insn)) { struct sequence_stack *stack = seq_stack; /* Scan all pending sequences too. */ @@ -3957,7 +3957,7 @@ remove_insn (rtx insn) break; } - gcc_assert (stack); + gcc_assert (BARRIER_P (insn) || stack); } if (!BARRIER_P (insn) (bb = BLOCK_FOR_INSN (insn))) ? I realize that doesn't do the extra assertions, but this is a pretty core routine and checking of invariants would more belong to RTL checking. Richard. } if (!BARRIER_P (insn) (bb = BLOCK_FOR_INSN (insn))) --- gcc/cfgrtl.c.jj 2012-02-07 16:05:47.977533716 +0100 +++ gcc/cfgrtl.c 2012-02-07 17:03:52.925956927 +0100 @@ -2818,6 +2818,7 @@ static void cfg_layout_merge_blocks (basic_block a, basic_block b) { bool forwarder_p = (b-flags BB_FORWARDER_BLOCK) != 0; + rtx insn; gcc_checking_assert (cfg_layout_can_merge_blocks_p (a, b)); @@ -2871,6 +2872,11 @@ cfg_layout_merge_blocks (basic_block a, rtx first = BB_END (a), last; last = emit_insn_after_noloc (b-il.rtl-header, BB_END (a), a); + /* The above might add a BARRIER as BB_END, but as barriers + aren't valid parts of a bb, remove_insn doesn't update + BB_END if it is a barrier. So adjust BB_END here. */ + while (BB_END (a) != first BARRIER_P (BB_END (a))) + BB_END (a) = PREV_INSN (BB_END (a)); delete_insn_chain (NEXT_INSN (first), last, false); b-il.rtl-header = NULL; } @@ -2878,40 +2884,28 @@ cfg_layout_merge_blocks (basic_block a, /* In the case basic blocks are not adjacent, move them around. */ if (NEXT_INSN (BB_END (a)) != BB_HEAD (b)) { - rtx first = unlink_insn_chain (BB_HEAD (b), BB_END (b)); + insn = unlink_insn_chain (BB_HEAD (b), BB_END (b)); - emit_insn_after_noloc (first, BB_END (a), a); - /* Skip possible DELETED_LABEL insn. */ - if
Re: [PATCH] Fix cfg_layout_merge_blocks (PR rtl-optimization/52139)
On Wed, Feb 08, 2012 at 10:27:42AM +0100, Richard Guenther wrote: Isn't it cheaper to do that before we scan all the sequences? Thus, I'd expect BLOCK_FOR_INSN to be NULL if the insn is in a sequence? Like simply Actually, on a third look, the emit-rtl.c changes aren't needed, apparently NEXT_INSN even in cfglayout mode is NULL for the in bb insns only if it is equal to get_last_insn (), unless in the header/footer sequences, but for those one shouldn't call remove_insn. To fix this bug the + /* The above might add a BARRIER as BB_END, but as barriers +aren't valid parts of a bb, remove_insn doesn't update +BB_END if it is a barrier. So adjust BB_END here. */ + while (BB_END (a) != first BARRIER_P (BB_END (a))) + BB_END (a) = PREV_INSN (BB_END (a)); hunk is all that is needed (and the remaining cfgrtl.c hunks just a nice to have cleanup, could be postponed for 4.8). Without this BB_END (a) is a barrier, but deleted one, so get_last_insn () was actually moved to some insn before it and therefore when we emit_insn_after_noloc the b bb unchained sequence, it doesn't match get_last_insn () when it should. I'll bootstrap/regtest now just that single hunk, is that ok for trunk/4.6/4.5? Jakub
Re: [PATCH] Fix cfg_layout_merge_blocks (PR rtl-optimization/52139)
On Wed, 8 Feb 2012, Jakub Jelinek wrote: On Wed, Feb 08, 2012 at 10:27:42AM +0100, Richard Guenther wrote: Isn't it cheaper to do that before we scan all the sequences? Thus, I'd expect BLOCK_FOR_INSN to be NULL if the insn is in a sequence? Like simply Actually, on a third look, the emit-rtl.c changes aren't needed, apparently NEXT_INSN even in cfglayout mode is NULL for the in bb insns only if it is equal to get_last_insn (), unless in the header/footer sequences, but for those one shouldn't call remove_insn. To fix this bug the + /* The above might add a BARRIER as BB_END, but as barriers +aren't valid parts of a bb, remove_insn doesn't update +BB_END if it is a barrier. So adjust BB_END here. */ + while (BB_END (a) != first BARRIER_P (BB_END (a))) + BB_END (a) = PREV_INSN (BB_END (a)); hunk is all that is needed (and the remaining cfgrtl.c hunks just a nice to have cleanup, could be postponed for 4.8). Without this BB_END (a) is a barrier, but deleted one, so get_last_insn () was actually moved to some insn before it and therefore when we emit_insn_after_noloc the b bb unchained sequence, it doesn't match get_last_insn () when it should. I'll bootstrap/regtest now just that single hunk, is that ok for trunk/4.6/4.5? Yes. Thanks, Richard.
Re: [PATCH] Fix cfg_layout_merge_blocks (PR rtl-optimization/52139)
On Wed, Feb 08, 2012 at 11:55:52AM +0100, Richard Guenther wrote: I'll bootstrap/regtest now just that single hunk, is that ok for trunk/4.6/4.5? Yes. Thanks, this is what I've committed after bootstrap/regtest on x86_64-linux and i686-linux: 2012-02-08 Jakub Jelinek ja...@redhat.com PR rtl-optimization/52139 * cfgrtl.c (cfg_layout_merge_blocks): If BB_END is a BARRIER after emit_insn_after_noloc, move BB_END to the last non-BARRIER insn before it. * gcc.dg/pr52139.c: New test. --- gcc/cfgrtl.c.jj 2012-02-07 16:05:47.977533716 +0100 +++ gcc/cfgrtl.c2012-02-07 17:03:52.925956927 +0100 @@ -2871,6 +2871,11 @@ cfg_layout_merge_blocks (basic_block a, rtx first = BB_END (a), last; last = emit_insn_after_noloc (b-il.rtl-header, BB_END (a), a); + /* The above might add a BARRIER as BB_END, but as barriers +aren't valid parts of a bb, remove_insn doesn't update +BB_END if it is a barrier. So adjust BB_END here. */ + while (BB_END (a) != first BARRIER_P (BB_END (a))) + BB_END (a) = PREV_INSN (BB_END (a)); delete_insn_chain (NEXT_INSN (first), last, false); b-il.rtl-header = NULL; } --- gcc/testsuite/gcc.dg/pr52139.c.jj 2012-02-07 16:14:32.537734917 +0100 +++ gcc/testsuite/gcc.dg/pr52139.c 2012-02-07 16:14:32.537734917 +0100 @@ -0,0 +1,49 @@ +/* PR rtl-optimization/52139 */ +/* { dg-do compile } */ +/* { dg-options -O -fno-tree-dominator-opts -fno-tree-fre } */ +/* { dg-additional-options -fpic { target fpic } } */ + +void *p; + +void +foo (int a) +{ + switch (a) +{ +case 0: +a0: +case 1: +a1: + p = a1; +case 2: +a2: + p = a2; +case 3: +a3: + p = a3; +case 4: +a4: + p = a4; +case 5: +a5: + p = a5; +case 6: +a6: + p = a6; +case 7: +a7: + p = a7; +case 8: +a8: + p = a8; +case 9: +a9: + p = a9; +case 10: +a10: + p = a10; +default: + p = a0; +} + goto *p; +} Jakub
[PATCH] Fix cfg_layout_merge_blocks (PR rtl-optimization/52139)
Hi! On the following testcase we hit two bugs during cfglayout merge_blocks. One is that b-il.rtl-header has some jumptable in it, followed by barrier. We call emit_insn_after_noloc to insert the whole b's header after BB_END (a) and then delete_insn_chain it, with the intention that only non-deletable insns like deleted label notes are kept around. Unfortunately delete_insn/remove_insn it uses isn't prepared to handle BARRIERs as part of a bb (i.e. if BB_END is equal to some barrier because of the emit_insn_after_noloc call, delete_insn_chain won't update BB_END properly). As barriers aren't part of a BB, instead of adjusting remove_insn this patch adjusts BB_END not to point to a barrier before calling delete_insn_chain. The second bug is that remove_insn ICEs if deleting an insn with NEXT_INSN NULL, unless that insn is part of a current sequence (or some sequence in the sequence stack). In the first version of the patch I've tried to avoid calling delete_insn on insns that have NEXT_INSN NULL, but given that having NULL NEXT_INSN is a pretty common situation when in cfglayout mode if the insn is at BB_END, I think it is better to allow that in remove_insn. Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? 2012-02-07 Jakub Jelinek ja...@redhat.com PR rtl-optimization/52139 * emit-rtl.c (remove_insn): Allow removing insns with NEXT_INSN NULL, if they are at BB_END. * cfgrtl.c (cfg_layout_merge_blocks): If BB_END is a BARRIER after emit_insn_after_noloc, move BB_END to the last non-BARRIER insn before it. Cleanup. * gcc.dg/pr52139.c: New test. --- gcc/emit-rtl.c.jj 2012-02-07 16:05:47.913534092 +0100 +++ gcc/emit-rtl.c 2012-02-07 16:14:32.529734964 +0100 @@ -1,7 +1,7 @@ /* Emit RTL for the GCC expander. Copyright (C) 1987, 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, - 2010, 2011 + 2010, 2011, 2012 Free Software Foundation, Inc. This file is part of GCC. @@ -3957,7 +3957,19 @@ remove_insn (rtx insn) break; } - gcc_assert (stack); + if (stack == NULL) + { + /* In cfglayout mode allow remove_insn of +insns at the end of bb. */ + if (BARRIER_P (insn)) + { + gcc_assert (prev); + bb = BLOCK_FOR_INSN (prev); + } + else + bb = BLOCK_FOR_INSN (insn); + gcc_assert (bb BB_END (bb) == insn); + } } if (!BARRIER_P (insn) (bb = BLOCK_FOR_INSN (insn))) --- gcc/cfgrtl.c.jj 2012-02-07 16:05:47.977533716 +0100 +++ gcc/cfgrtl.c2012-02-07 17:03:52.925956927 +0100 @@ -2818,6 +2818,7 @@ static void cfg_layout_merge_blocks (basic_block a, basic_block b) { bool forwarder_p = (b-flags BB_FORWARDER_BLOCK) != 0; + rtx insn; gcc_checking_assert (cfg_layout_can_merge_blocks_p (a, b)); @@ -2871,6 +2872,11 @@ cfg_layout_merge_blocks (basic_block a, rtx first = BB_END (a), last; last = emit_insn_after_noloc (b-il.rtl-header, BB_END (a), a); + /* The above might add a BARRIER as BB_END, but as barriers +aren't valid parts of a bb, remove_insn doesn't update +BB_END if it is a barrier. So adjust BB_END here. */ + while (BB_END (a) != first BARRIER_P (BB_END (a))) + BB_END (a) = PREV_INSN (BB_END (a)); delete_insn_chain (NEXT_INSN (first), last, false); b-il.rtl-header = NULL; } @@ -2878,40 +2884,28 @@ cfg_layout_merge_blocks (basic_block a, /* In the case basic blocks are not adjacent, move them around. */ if (NEXT_INSN (BB_END (a)) != BB_HEAD (b)) { - rtx first = unlink_insn_chain (BB_HEAD (b), BB_END (b)); + insn = unlink_insn_chain (BB_HEAD (b), BB_END (b)); - emit_insn_after_noloc (first, BB_END (a), a); - /* Skip possible DELETED_LABEL insn. */ - if (!NOTE_INSN_BASIC_BLOCK_P (first)) - first = NEXT_INSN (first); - gcc_assert (NOTE_INSN_BASIC_BLOCK_P (first)); - BB_HEAD (b) = NULL; - - /* emit_insn_after_noloc doesn't call df_insn_change_bb. - We need to explicitly call. */ - update_bb_for_insn_chain (NEXT_INSN (first), - BB_END (b), - a); - - delete_insn (first); + emit_insn_after_noloc (insn, BB_END (a), a); } /* Otherwise just re-associate the instructions. */ else { - rtx insn; - - update_bb_for_insn_chain (BB_HEAD (b), BB_END (b), a); - insn = BB_HEAD (b); - /* Skip possible DELETED_LABEL insn. */ - if (!NOTE_INSN_BASIC_BLOCK_P (insn)) - insn = NEXT_INSN (insn); - gcc_assert (NOTE_INSN_BASIC_BLOCK_P (insn)); - BB_HEAD (b) = NULL; BB_END (a) = BB_END (b); - delete_insn (insn); } + /* emit_insn_after_noloc doesn't call df_insn_change_bb. + We need to explicitly call. */ +