https://gcc.gnu.org/g:028b8b62979f7e6326cb15ec8e519b1600e86a36
commit r17-4922-g028b8b62979f7e6326cb15ec8e519b1600e86a36 Author: Kyrylo Tkachov <[email protected]> Date: Fri Sep 11 16:31:01 2026 +0200 cfgcleanup: keep tail merging out of an unfactored computed goto [PR127246] bbcb0c056be (PR15242) stopped try_crossjump_bb from sharing the tails of the predecessors of a computed jump unless optimizing for size. pass_duplicate_computed_gotos copies the jump back into each predecessor and a shared tail defeats that. efd8f7507b3 replaced !optimize_size with optimize_bb_for_size_p (bb), which inverted the test: tails are now shared in code optimized for speed and kept apart in code optimized for size. Test the condition of the gate of pass_duplicate_computed_gotos instead, !optimize_function_for_size_p (cfun) && flag_expensive_optimizations. PR42621 fixed the bb-reorder half of the same change in that gate. As in bbcb0c056be, the guard then applies to the whole function, also to a block that is too large to be unfactored. try_head_merge_bb has a copy of the inverted test that cannot change code, because every successor edge of a computed jump is abnormal. It stays as it is. On the PR127246 testcase with identical handler tails and -fno-tree-slp-vectorize, jump2 no longer crossjumps the handlers at -O2 only for compgotos to copy the tails back. With BB SLP the handlers jump to a block of vector copies in front of the dispatch, which is a separate problem of the PR. At -Os the handler tails are shared again, which makes the aarch64 code of the PR testcase 54% smaller. With -fno-expensive-optimizations, which disables the duplication, the tails are shared as well. Bootstrapped and regression tested on aarch64-unknown-linux-gnu and x86_64-pc-linux-gnu. gcc/ChangeLog: PR rtl-optimization/127246 * cfgcleanup.cc (try_crossjump_bb): Keep the predecessors of a computed jump apart under the condition of the gate of pass_duplicate_computed_gotos. gcc/testsuite/ChangeLog: PR rtl-optimization/127246 * gcc.dg/pr127246-1.c: New test. * gcc.dg/pr127246-2.c: New test. * gcc.dg/pr127246-3.c: New test. Signed-off-by: Kyrylo Tkachov <[email protected]> Diff: --- gcc/cfgcleanup.cc | 8 ++++--- gcc/testsuite/gcc.dg/pr127246-1.c | 48 +++++++++++++++++++++++++++++++++++++++ gcc/testsuite/gcc.dg/pr127246-2.c | 12 ++++++++++ gcc/testsuite/gcc.dg/pr127246-3.c | 12 ++++++++++ 4 files changed, 77 insertions(+), 3 deletions(-) diff --git a/gcc/cfgcleanup.cc b/gcc/cfgcleanup.cc index 1d9ec908dab0..f9178a92c392 100644 --- a/gcc/cfgcleanup.cc +++ b/gcc/cfgcleanup.cc @@ -2180,9 +2180,11 @@ try_crossjump_bb (int mode, basic_block bb) if (EDGE_COUNT (bb->preds) < 2) return false; - /* Don't crossjump if this block ends in a computed jump, - unless we are optimizing for size. */ - if (optimize_bb_for_size_p (bb) + /* Don't crossjump the predecessors of a computed jump when computed + gotos are duplicated (see pass_duplicate_computed_gotos::gate), as + that copies the jump back into each predecessor. */ + if (!optimize_function_for_size_p (cfun) + && flag_expensive_optimizations && bb != EXIT_BLOCK_PTR_FOR_FN (cfun) && computed_jump_p (BB_END (bb))) return false; diff --git a/gcc/testsuite/gcc.dg/pr127246-1.c b/gcc/testsuite/gcc.dg/pr127246-1.c new file mode 100644 index 000000000000..cfea56d631df --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr127246-1.c @@ -0,0 +1,48 @@ +/* PR rtl-optimization/127246 */ +/* { dg-do compile } */ +/* { dg-require-effective-target label_values } */ +/* { dg-require-effective-target indirect_jumps } */ +/* { dg-options "-O2 -fno-tree-slp-vectorize -fdump-rtl-jump2-details" } */ + +/* pass_duplicate_computed_gotos copies the dispatch back into every handler, + so tail merging must not share the identical handler tails in front of + it. */ + +struct frame +{ + const unsigned short *instr_ptr; + long *stackpointer; +}; + +#define OPS(X) \ + X (0) X (1) X (2) X (3) X (4) X (5) X (6) X (7) X (8) X (9) X (10) \ + X (11) X (12) X (13) X (14) X (15) X (16) X (17) X (18) X (19) + +extern long op (long); + +#define DISPATCH() goto *targets[*next_instr & 0xff] + +#define HANDLER(N) \ + L_##N: \ + frame->instr_ptr = next_instr; \ + next_instr += 1; \ + frame->stackpointer = stack_pointer; \ + stack_pointer[-1] = op (N); \ + stack_pointer -= 1; \ + DISPATCH (); + +#define ENTRY(N) &&L_##N, + +void +interp (struct frame *frame) +{ + static const void *const targets[] = { OPS (ENTRY) }; + const unsigned short *next_instr = frame->instr_ptr; + long *stack_pointer = frame->stackpointer; + + DISPATCH (); + + OPS (HANDLER) +} + +/* { dg-final { scan-rtl-dump-not "Cross jumping" "jump2" } } */ diff --git a/gcc/testsuite/gcc.dg/pr127246-2.c b/gcc/testsuite/gcc.dg/pr127246-2.c new file mode 100644 index 000000000000..02328f04eab1 --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr127246-2.c @@ -0,0 +1,12 @@ +/* PR rtl-optimization/127246 */ +/* { dg-do compile } */ +/* { dg-require-effective-target label_values } */ +/* { dg-require-effective-target indirect_jumps } */ +/* { dg-options "-Os -fdump-rtl-jump2-details" } */ + +/* When optimizing for size the computed goto is not unfactored, so tail + merging the handler tails is what we want. */ + +#include "pr127246-1.c" + +/* { dg-final { scan-rtl-dump "Cross jumping" "jump2" } } */ diff --git a/gcc/testsuite/gcc.dg/pr127246-3.c b/gcc/testsuite/gcc.dg/pr127246-3.c new file mode 100644 index 000000000000..a4da38da167c --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr127246-3.c @@ -0,0 +1,12 @@ +/* PR rtl-optimization/127246 */ +/* { dg-do compile } */ +/* { dg-require-effective-target label_values } */ +/* { dg-require-effective-target indirect_jumps } */ +/* { dg-options "-O2 -fno-expensive-optimizations -fno-tree-slp-vectorize -fdump-rtl-jump2-details" } */ + +/* Without -fexpensive-optimizations computed gotos are not duplicated, so + tail merging the handler tails is what we want. */ + +#include "pr127246-1.c" + +/* { dg-final { scan-rtl-dump "Cross jumping" "jump2" } } */
