On 7/19/25 2:22 PM, Andrew Pinski wrote:
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.
For better or worse __builtin_unreachable doesn't halt the program, it just lets it keep going in an undefined state. A NOP seems like the choice that would preserve behavior here.

Is there a reason you went with trapping/infinite loop? I think that's a better solution all-around for __builtin_unreachable, but I'm pretty sure I'm in the minority on that opinion.

jeff

Reply via email to