Skip to content

Check for conditionally-inlined compiler builtins - #10674

Draft
lb90 wants to merge 2 commits into
mesonbuild:masterfrom
lb90:check-linking-of-builtin-functions
Draft

lb90 wants to merge 2 commits into
mesonbuild:masterfrom
lb90:check-linking-of-builtin-functions

Conversation

@lb90

@lb90 lb90 commented Aug 10, 2022

Copy link
Copy Markdown
Contributor

A builtin function may resolve to a function call, but it's not mandatory
for a libc implementation to provide such function. When testing for the
presence of a builtin function, also check if linking succeeds or not.

Fixes #10641

@lb90
lb90 requested a review from dcbaker as a code owner August 10, 2022 12:21
@codecov

codecov Bot commented Aug 10, 2022 •

Copy link
Copy Markdown

Codecov Report

Merging #10674 (fc439b0) into master (14b8381) will increase coverage by 8.23%.
The diff coverage is n/a.

❗ Current head fc439b0 differs from pull request most recent head 378d220. Consider uploading reports for the commit 378d220 to get more accurate results

@@            Coverage Diff             @@
##           master   #10674      +/-   ##
==========================================
+ Coverage   58.31%   66.54%   +8.23%     
==========================================
  Files         203      203              
  Lines       44048    44098      +50     
  Branches     9770     9781      +11     
==========================================
+ Hits        25685    29344    +3659     
+ Misses      16071    12420    -3651     
- Partials     2292     2334      +42     
Impacted Files Coverage Δ
mesonlib/__init__.py 55.55% <0.00%> (ø)
cmake/interpreter.py 85.09% <0.00%> (+0.11%) ⬆️
interpreterbase/interpreterbase.py 77.70% <0.00%> (+0.45%) ⬆️
modules/qt.py 74.00% <0.00%> (+0.72%) ⬆️
mparser.py 92.44% <0.00%> (+0.83%) ⬆️
scripts/symbolextractor.py 30.08% <0.00%> (+0.84%) ⬆️
interpreter/interpreterobjects.py 90.66% <0.00%> (+0.94%) ⬆️
dependencies/configtool.py 83.00% <0.00%> (+1.00%) ⬆️
dependencies/dev.py 57.03% <0.00%> (+1.30%) ⬆️
cmake/tracetargets.py 74.66% <0.00%> (+1.33%) ⬆️
... and 76 more

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

@eli-schwartz eli-schwartz left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In addition to the CI failures that indicate this logic is flawed, there are a couple of typos.

Comment thread mesonbuild/compilers/mixins/clike.py Resolved
Comment thread mesonbuild/compilers/mixins/clike.py Resolved
@lb90

lb90 commented Aug 10, 2022 •

Copy link
Copy Markdown
Contributor Author

This is tricky to get right. I made a test using autotools:

configure.ac

AC_INIT([testcheck], [1.0])
AM_INIT_AUTOMAKE([foreign])

AC_CHECK_LIB(m, sincos)
AC_CHECK_FUNC(alloca)

AC_PROG_CC
AC_CONFIG_FILES([Makefile])
AC_OUTPUT

Makefile.am

bin_PROGRAMS=testcheck

main.c

int main()
{
  return 0;
}

Running

autoreconf -fiv
./configure

Yelds the following output:

checking for sincos in -lm... yes
checking for alloca... no

Here's the relevant log:

configure:4209: result: gcc3
configure:4225: checking for sincos in -lm
configure:4248: gcc -o conftest -g -O2   conftest.c -lm   >&5
conftest.c:15:6: warning: conflicting types for built-in function 'sincos'; expected 'void(double,  double *, double *)' [-Wbuiltin-declaration-mismatch]
   15 | char sincos ();
      |      ^~~~~~
conftest.c:1:1: note: 'sincos' is declared in header '<math.h>'
    1 | /* confdefs.h */
configure:4248: $? = 0
configure:4258: result: yes
configure:4269: checking for alloca
configure:4269: gcc -o conftest -g -O2   conftest.c -lm  >&5
conftest.c:28:6: warning: conflicting types for built-in function 'alloca'; expected 'void *(long unsigned int)' [-Wbuiltin-declaration-mismatch]
   28 | char alloca ();
      |      ^~~~~~
/usr/bin/ld: /tmp/cck1SkYn.o: in function `main':
/home/luca/test-autoconf/conftest.c:39: undefined reference to `alloca'
collect2: error: ld returned 1 exit status
configure:4269: $? = 1
configure: failed program was:
| /* confdefs.h */
| #define PACKAGE_NAME "testcheck"
| #define PACKAGE_TARNAME "testcheck"
| #define PACKAGE_VERSION "1.0"
| #define PACKAGE_STRING "testcheck 1.0"
| #define PACKAGE_BUGREPORT ""
| #define PACKAGE_URL ""
| #define PACKAGE "testcheck"
| #define VERSION "1.0"
| #define HAVE_LIBM 1
| /* end confdefs.h.  */
| /* Define alloca to an innocuous variant, in case <limits.h> declares alloca.
|    For example, HP-UX 11i <limits.h> declares gettimeofday.  */
| #define alloca innocuous_alloca
| 
| /* System header to define __stub macros and hopefully few prototypes,
|    which can conflict with char alloca (); below.  */
| 
| #include <limits.h>
| #undef alloca
| 
| /* Override any GCC internal prototype to avoid an error.
|    Use char because int might match the return type of a GCC
|    builtin and then its argument prototype would still apply.  */
| #ifdef __cplusplus
| extern "C"
| #endif
| char alloca ();
| /* The GNU C library defines this for functions which it implements
|     to always fail with ENOSYS.  Some functions are actually named
|     something starting with __ and the normal name is an alias.  */
| #if defined __stub_alloca || defined __stub___alloca
| choke me
| #endif
| 
| int
| main (void)
| {
| return alloca ();
|   ;
|   return 0;
| }
configure:4269: result: no

@lb90

lb90 commented Aug 10, 2022 •

Copy link
Copy Markdown
Contributor Author

To know if a built-in may not be inlined, I guess we can test for

__has_builtin (func) && __has_builtin (__builtin_func)

Then, to know if func is provided by the target libc, we may use the -u symbol command-line option:

Pretend the symbol symbol is undefined, to force linking of library modules to define it.

@lb90
lb90 force-pushed the check-linking-of-builtin-functions branch from dc4514c to ffbb1a4 Compare August 12, 2022 12:54
@lb90
lb90 marked this pull request as draft August 12, 2022 12:55
@lb90 lb90 changed the title Also test linking when checking for a builtin function Check for conditionally-inlined compiler builtins Aug 12, 2022
@lb90
lb90 force-pushed the check-linking-of-builtin-functions branch from ffbb1a4 to 378d220 Compare August 12, 2022 13:50
@matthiasclasen

Copy link
Copy Markdown

All this seems much to complicated to me. What we want to know is:

  1. Will a program that calls sincos(x) link ?
  2. If not, will it link if we add -lm to LIBS ?

I don't see why there is any need to fiddle with builtins here.

@lb90

lb90 commented Aug 19, 2022

Copy link
Copy Markdown
Contributor Author

Yeah, see also:

Now that compilers have __has_builtin there's even less need to do checks from the build system

@lb90

lb90 commented Aug 19, 2022

Copy link
Copy Markdown
Contributor Author

@lazka could you let us know more about the non-functioning builtins in mingw GCC?

@lazka

lazka commented Aug 19, 2022 •

Copy link
Copy Markdown
Contributor

@lazka could you let us know more about the non-functioning builtins in mingw GCC?

that's all I know: https://github.com/mesonbuild/meson/pull/7116/files#r424014885

edit: I'm happy to forward any specific questions though

@lb90

lb90 commented Aug 19, 2022 •

Copy link
Copy Markdown
Contributor Author

Anyway consider compiling a math library using sincos(), sqrt() etc. on netBSD. If the architecture has machine instruction for sincos, we don't actually care that the netBSD libc doesn't implement sincos(), because GCC uses the machine instruction anyway. So yes, I'd say the function should be reported as available. Avoiding that could hurt performance considerably.

Anyway, the fix is for compilers to implement a __is_builtin_always_inlined() macro, reporting if the builtin is guaranteed to be substituted inline for the given compiler invocation (or change __has_builtin to only report always-inlined builtins)

@matthiasclasen

Copy link
Copy Markdown

Anyway consider compiling a math library using sincos(), sqrt() etc. on netBSD. If the architecture has machine instruction for sincos, we don't actually care that the netBSD libc doesn't implement sincos(), because GCC uses the machine instruction anyway. So yes, I'd say the function should be reported as available. Avoiding that could hurt performance considerably.

But if the compiler doesn't use a library function, then compiling a trivial test program using sincos(x) will succeed, no? Why do we need to fiddle with builtins?

@lb90

lb90 commented Aug 19, 2022 •

Copy link
Copy Markdown
Contributor Author

then compiling a trivial test program using sincos(x) will succeed, no?

Unfortunately no, it will fail with GCC (not so sure about CLang)

A build system writes a simple test program to check for availability of a function. It doesn't know how it should call the function though, because it doesn't have any knowledge of the argument list (signature). Thus the function is prototyped in a generic way: char func();. This is how a test program looks like:

/* generic prototype */
char func();

int main()
{
  return func();
}

If that compiles and links, the function is available.

Now consider what happens when func is a builtin. The compiler encounters an incompatible prototype of one of its builtin functions. What should it do? That's not standardized, because builtins are not part of the C or C++ language specs. GCC, probably for compatibility with existing codebases, will consider func a normal function, not a builtin. Thus it will try to resolve func at link time. In the case of sincos on netBSD, that will fail.

It can also be tested on Linux:

  • This compiles with gcc test-sincos.c
    void sincos(double x, double *s, double *c);
    
    int main()
    {
      double s, c;
      sincos(3.14 / 4, &s, &c);
    
      return (int)((s*s + c*c)*1000);
    }
  • This requires linking with the math library: gcc test-sincos.c -lm
    char sincos();
    
    int main()
    {
      return sincos();
    }

Another example is:

/* remove me to compile */
char __builtin_trap();

int main()
{
  __builtin_trap();
  return 0;
}

Finally, another issue that might get in the way is explained in https://gcc.gnu.org/pipermail/gcc/2022-August/239262.html

That is quite complicated and I'm not an expert in compilers (at all!), so if anybody has some idea to make that work I'd be very happy :)

@matthiasclasen

Copy link
Copy Markdown

I would be quite ok if the check determines that sincos is not available on netbsd.

@matthiasclasen

matthiasclasen commented Aug 19, 2022 •

Copy link
Copy Markdown

I would say the 'generic prototype' is part of the problem here.

Maybe something like this would work better:

#define _GNU_SOURCE

#include <math.h>

typedef char (* func_ptr) (void);

int main ()
{
  func_ptr f;

  f = (func_ptr) sincos;

  return 0;
}

@lb90

lb90 commented Aug 21, 2022 •

Copy link
Copy Markdown
Contributor Author

Here's something very intersting: in clang, __has_builtin () evaluates to 1 only for builtins that are always inlined. Consider the following sample:

#if __has_builtin (sincos)
#error "sincos is a builtin"
#else
#error "sincos is NOT a builtin"
#endif

int main ()
{
  return 0;
}

Here's the outcome of compilation:

[luca@ArchLinux ~]$ gcc test-has-builtin.c 
test-has-builtin.c:2:2: error: #error "sincos is a builtin"
    2 | #error "sincos is a builtin"
      |  ^~~~~

[luca@ArchLinux ~]$ clang test-has-builtin.c 
test-has-builtin.c:4:2: error: "sincos is NOT a builtin"
#error "sincos is NOT a builtin"
 ^
1 error generated.

The same happens for __builtin_sincos.

@lb90

lb90 commented Aug 21, 2022 •

Copy link
Copy Markdown
Contributor Author

IMO the behavior of clang makes more sense. Also, __has_builtin was introduced in gcc as a consequence of its addition in clang, so it'd be good to stay compatible

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66970

@lb90

lb90 commented Aug 21, 2022

Copy link
Copy Markdown
Contributor Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

sincos, sincosf detection as used by gtk misdetects sincos, sincosf

4 participants