Variadic builtin strangeness (and an odd vfprintf!)

Hello everyone,

I'm trying to sort out https://llvm.org/bugs/show_bug.cgi?id=20958 --
but struggling.

For the particular case of __builtin_isnan (the C99 math macros in
this section are strange too), it is defined as,

BUILTIN(__builtin_isnan, "i.", "nc")

That's how it should look. Unfortunately, ASTContext::GetBuiltinType
treats an argument list with "..." as its sole member specially,

  if (ArgTypes.empty() && Variadic) return
    getFunctionNoProtoType(ResType, EI);

Which essentially generates a declaration like,

int __builtin_isnan();

That works for double and long double (but not float (default
promotions...)) in C, and in C++
it's a very strange declaration for this builtin!

Once you fix this and make Clang generate the correct (magic)
prototype of

int __builtin_isnan(...);

which Sema::SemaBuiltinFPClassification expects it to take, the bug in
PR20958 goes away, we can declare,

int __builtin_isnan(T);

for T in (double, long double, float).

Unfortunately, we get some interesting test errors:

CodeGen/builtin-ms-noop.cpp

/work/llvm-gitsvn/tools/clang/test/CodeGen/builtin-ms-noop.cpp:11:17:
error: cannot pass object of non-POD type 'A' through variadic
function; call will abort at runtime
  return __noop(A());

The documentation for __noop says that the argument list should be
parsed but no code should be generated. This is where my knowledge of
cfe breaks down -- it seems like the implementation of this intrinsic
as as a variadic builtin is wrong, but I would appreciate pointers on
how to fix that, or indeed if I'm barking up the wrong tree.

The other test errors uncovered a strange implementation of vfprintf,

LIBBUILTIN(vfprintf, "i.", "fP:1:", "stdio.h", ALL_LANGUAGES)

That should of course be,

LIBBUILTIN(vfprintf, "iP*cC*a", "fP:0:", "stdio.h", ALL_LANGUAGES)

but the reason for doing it this way is because of autoconf weirdness,

http://llvm.org/viewvc/llvm-project?view=revision&revision=72760

I'm not sure about the rationale for the above commit. Can anyone
with context help me understand it?

I note that in GCC's own builtins.def file (you'd think they would
get autoconf right!), the type of vfprintf is int ()(FILE*, const
char *, va_list) as I expect. Maybe GCC has workarounds elsewhere.

Clues? Thanks for reading!

- Charlie.

I have a patch attached for this issue that allows you do declare

Apologies for the previous message, I hit send by accident.

I have a patch attached now that allows you to declare,

int __builtin_isnan(T);

for T in (double, long double, float).

In C++ mode, but not in C. Where as before this patch you could declare

int __builtin_isnan(T);

for T in (double, long double), but not for T=float in C before, now
you can declare any of these prototypes.

So I suppose I have just moved the problem from C++ to C. I have
cleaned up the strange behavior of builtins taking only "..." to be
declared as taking "()".

The root problem here is that there does not appear to be a way to
declare type generic builtins that work across C and C++.

The most rigorous way to define the __builtin_isnan function would be
to add the "t" attribute in Builtins.def and then write a custom
argument checking function in SemaChecking that makes sure the
arguments are "real-floating", as defined in C99.

That seems pretty unpleasant, so before I attempt such work, I would
like to ask for advice as to how best to fix this problem.

Thank you for your time,
Charlie.

0001-PR20958-Allow-__builtin_isnan-to-be-declared.patch (4.46 KB)