Back in 2007 glibc gained some logic to implement "error" and
"error_at_line" by splitting into zero and non-zero cases, with the
nonzero case calling a "noreturn" function [1].
This doesn't seem to work. I tested back to 4.8.1 with Compiler
Explorer [2], which seems to be the earliest GCC that supports -fdump-
tree-original=stderr.
What happens is that glibc's
__extern_always_inline void
error (int __status, int __errnum, const char *__format, ...)
{
if (__builtin_constant_p (__status) && __status != 0)
__error_noreturn (__status, __errnum, __format, __va_arg_pack ());
else
__error_alias (__status, __errnum, __format, __va_arg_pack ());
}
comes out of GCC's C frontend as:
{
if (0)
{
__error_noreturn (__status, __errnum, __format, __builtin_va_arg_pack ());
}
else
{
__error_alias (__status, __errnum, __format, __builtin_va_arg_pack ());
}
}
since __status is not a builtin constant, and this rapidly gets
optimized down to just:
__error_alias (__status, __errnum, __format, __builtin_va_arg_pack ());
and so by the time GCC gets to try inlining calls to
error (EXIT_FAILURE, ...etc
we just have a call to __error_alias, and any "noreturn" logic is lost.
glib'c attempt to specialize the cases is causing false positives from
GCC's -fanalyzer, which is how I spotted this [3]. It's probably
trivial to workaround the issue within -fanalyzer, but I wonder to what
extent glibc has further such specializations being thwarted by gcc
optimizing away the __builtin_constant_p, and if gcc should be doing
this (e.g. keep it around for __always_inline__ functions???), hence I
thought it worth sharing this to both mailing lists.
Thoughts?
Dave
[1] ("Specializations of error functions"
https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=fae56ce808e36e8112d15189bf4337b3a39ee683
[2] https://godbolt.org/z/WsvqxP6hY
[3] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=115724