In this code: __attribute__((__warning__("argh"))) static void foo(void) { __asm__ ( ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;" ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;" ); } void bar0(int x) { if (__builtin_constant_p(x)) foo(); } void bar1(int x) { if (__builtin_constant_p(x)) foo(); } No calls to foo are emitted in the final output, and GCC knows this, since no warnings are emitted. However, GCC will still emit a function body for foo, unless either the __asm__ statement is removed or both conditions are changed to integer constant expressions, so that the function is eliminated by one of the inlining passes. (I decided to demonstrate it this way instead of using __attribute__((__noinline__)), just to make sure this is not related to __attribute__((__noinline__)) somehow implying __attribute__((__used__)).)
Confirmed. ___builtin_constant_p is only resolved after IPA optimization and at that point unreachable functions can no longer be removed reliably (foo is output before bar0 __builtin_constant_p is resolved). We could add another "IPA" sync point before RTL expansion but not sure if we really want to. We could also see there's no callers to bar0 and bar1 and resolve __builtin_constant_p earlier during IPA.
Related to PR 47205, PR 89139