When we have an empty function, things can go wrong with cfi_startproc/cfi_endproc and a few other things like exceptions. So if the only thing the function does is a call to __builtin_unreachable, let's replace that with a __builtin_trap instead if the target has a trap instruction. For targets without a trap instruction defined, replace it with an infinite loop; this allows not to need for the abort call to happen but still get the correct behavior of not having two functions at the same location.
The QOI idea for basic block reorder is recorded as PR 120004. Changes since v1: * v2: Move to final gimple cfg cleanup instead of expand and use BUILT_IN_UNREACHABLE_TRAP. * v3: For targets without a trap defined, create an infinite loop. Bootstrapped and tested on x86_64-linux-gnu. PR middle-end/109267 gcc/ChangeLog: * tree-cfgcleanup.cc (execute_cleanup_cfg_post_optimizing): If the first non debug statement in the first (and only) basic block is a call to __builtin_unreachable change it to a call to __builtin_trap or an infinite loop. gcc/testsuite/ChangeLog: * lib/target-supports.exp (check_effective_target_trap): New proc. * g++.dg/missing-return.C: Update testcase for the !trap case. * gcc.dg/pr109267-1.c: New test. * gcc.dg/pr109267-2.c: New test. Signed-off-by: Andrew Pinski <quic_apin...@quicinc.com> --- gcc/testsuite/g++.dg/missing-return.C | 4 +++- gcc/testsuite/gcc.dg/pr109267-1.c | 15 +++++++++++++++ gcc/testsuite/gcc.dg/pr109267-2.c | 14 ++++++++++++++ gcc/testsuite/lib/target-supports.exp | 12 ++++++++++++ gcc/tree-cfgcleanup.cc | 24 ++++++++++++++++++++++++ 5 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 gcc/testsuite/gcc.dg/pr109267-1.c create mode 100644 gcc/testsuite/gcc.dg/pr109267-2.c diff --git a/gcc/testsuite/g++.dg/missing-return.C b/gcc/testsuite/g++.dg/missing-return.C index 5f8e2ccb100..f6934b05707 100644 --- a/gcc/testsuite/g++.dg/missing-return.C +++ b/gcc/testsuite/g++.dg/missing-return.C @@ -5,4 +5,6 @@ int foo(int a) { } /* { dg-warning "no return statement" } */ -/* { dg-final { scan-tree-dump "__builtin_unreachable" "optimized" } } */ +/* For targets without traps, it will be an infinite loop */ +/* { dg-final { scan-tree-dump "__builtin_unreachable" "optimized" { target trap } } } */ +/* { dg-final { scan-tree-dump "goto <" "optimized" { target { ! trap } } } } */ diff --git a/gcc/testsuite/gcc.dg/pr109267-1.c b/gcc/testsuite/gcc.dg/pr109267-1.c new file mode 100644 index 00000000000..e762e59c3f8 --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr109267-1.c @@ -0,0 +1,15 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-optimized" } */ + +/* PR middle-end/109267 */ + +int f(void) +{ + __builtin_unreachable(); +} + +/* This unreachable should be changed to be a trap. */ + +/* { dg-final { scan-tree-dump-times "__builtin_unreachable trap \\\(" 1 "optimized" { target trap } } } */ +/* { dg-final { scan-tree-dump-times "goto <" 1 "optimized" { target { ! trap } } } } */ +/* { dg-final { scan-tree-dump-not "__builtin_unreachable \\\(" "optimized"} } */ diff --git a/gcc/testsuite/gcc.dg/pr109267-2.c b/gcc/testsuite/gcc.dg/pr109267-2.c new file mode 100644 index 00000000000..6cd1419a1e3 --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr109267-2.c @@ -0,0 +1,14 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-optimized" } */ + +/* PR middle-end/109267 */ +void g(void); +int f(int *t) +{ + g(); + __builtin_unreachable(); +} + +/* The unreachable should stay a unreachable. */ +/* { dg-final { scan-tree-dump-not "__builtin_unreachable trap \\\(" "optimized"} } */ +/* { dg-final { scan-tree-dump-times "__builtin_unreachable \\\(" 1 "optimized"} } */ diff --git a/gcc/testsuite/lib/target-supports.exp b/gcc/testsuite/lib/target-supports.exp index 65d2e67a85b..30d12a97574 100644 --- a/gcc/testsuite/lib/target-supports.exp +++ b/gcc/testsuite/lib/target-supports.exp @@ -1017,6 +1017,18 @@ proc check_effective_target_label_values {} { return 1 } + +# Return 1 if builtin_trap expands not into a call but an instruction, +# 0 otherwise. +proc check_effective_target_trap { } { + return [check_no_messages_and_pattern trap "!\\(call" rtl-expand { + void foo () + { + return __builtin_trap (); + } + } "" ] +} + # Return 1 if builtin_return_address and builtin_frame_address are # supported, 0 otherwise. diff --git a/gcc/tree-cfgcleanup.cc b/gcc/tree-cfgcleanup.cc index a34a51ec186..5aaa18df0c5 100644 --- a/gcc/tree-cfgcleanup.cc +++ b/gcc/tree-cfgcleanup.cc @@ -47,6 +47,7 @@ along with GCC; see the file COPYING3. If not see #include "tree-into-ssa.h" #include "tree-cfgcleanup.h" #include "gimple-pretty-print.h" +#include "target.h" /* The set of blocks in that at least one of the following changes happened: @@ -1569,6 +1570,29 @@ execute_cleanup_cfg_post_optimizing (void) cleanup_dead_labels (); if (group_case_labels ()) todo |= TODO_cleanup_cfg; + + basic_block bb = single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun)); + gimple_stmt_iterator gsi = gsi_start_nondebug_after_labels_bb (bb); + /* If the first (and only) bb and the only non debug + statement is __builtin_unreachable call, then replace it with a trap + so the function is at least one instruction in size. */ + if (!gsi_end_p (gsi) + && gimple_call_builtin_p (gsi_stmt (gsi), BUILT_IN_UNREACHABLE)) + { + if (targetm.have_trap ()) + { + gimple_call_set_fndecl (gsi_stmt (gsi), builtin_decl_implicit (BUILT_IN_UNREACHABLE_TRAP)); + update_stmt (gsi_stmt (gsi)); + } + /* If the target does not have a trap, convert it into an infinite loop. */ + else + { + gsi_remove (&gsi, true); + make_single_succ_edge (bb, bb, EDGE_FALLTHRU); + fix_loop_structure (NULL); + } + } + if ((flag_compare_debug_opt || flag_compare_debug) && flag_dump_final_insns) { -- 2.43.0