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

Reply via email to