Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

has_function: Improve builtin detection #3675

Closed
wants to merge 5 commits into from

Conversation

nirbheek
Copy link
Member

@nirbheek nirbheek commented Jun 3, 2018

  • has_function: Improve built-in detection with headers

    • __has_builtin(foo) is more correct and more reliable than __has_builtin(__builtin_foo)
    • If detection of built-ins with headers fails in the first check because we can't take the address of a built-in, we can do an ordinary check.

We can't always do this ordinary check because clang on macOS ld removes the symbol when using -Wl,-no_weak_imports, which is needed for being able to target the correct platform with XCode 8 and newer.

Closes #3672

  • has_function: Fix checking for __builtin_foo functions

Some functions are meant to be used only as compiler builtins, such as __builtin_smul_overflow and friends.

Detect this case and don't look for __builtin_foo because such a check will never include a header.

Fixes #3676

@nirbheek nirbheek force-pushed the nirbheek/mingw-has_function-quirks branch from fa2a067 to 5bebeaa Compare June 3, 2018 11:40
@textshell
Copy link
Contributor

I think this wrongly attributes the problem to MinGW when this just reveals less than universally true a assumptions in meson. The current code also fails on linux where gcc is one of the two native compilers in #3676.

Meson should not assume a fixed relationship between foo and __builtin_foo. If there is a relationship is has to come from a system header.

This of course is breaking for projects where the build file failed to specify the needed header. We have to solve that in some way before we can do the proper fix.

@nirbheek
Copy link
Member Author

nirbheek commented Jun 3, 2018

I think this wrongly attributes the problem to MinGW

The current commits do not do this.

The current code also fails on linux where gcc is one of the two native compilers in #3676.

Now contains a fix for that issue + a test with a note about gcc being broken.

This of course is breaking for projects where the build file failed to specify the needed header. We have to solve that in some way before we can do the proper fix.

Do tell us if you find a way to fix this. We have no choice but to do this for now.

@textshell
Copy link
Contributor

textshell commented Jun 3, 2018

I think for compatibility we need to deprecate this implicit assumption. Ideally meson would print a warning, but work as previously for a few releases. One way to do this would be to collect a list of affected functions and their needed includes and just implicitly add that include for the deprecation period.

From a grep of my include directory it seems like these functions might be affected.
(Using egrep -r '#.*__builtin_' /usr/include/ on a debian stretch, but with newlib and diet libc headers also installed)

fpclassify isfinite isnormal isgreater isgreaterequal isless islessequal islessgreater isunordered alloca isnan va_start va_end va_arg offsetof strncpy strncat strcspn strspn strpbrk

It also reveals that the naming relation is often a bit different so the old code already does not work reliable, e.g.:

#define    __bswap16(_x)   __builtin_bswap16(_x)
#define isinf(__x) (__builtin_isinf_sign (__x))
#define __unreachable() __builtin_unreachable()

That list seems manageable although it is a bit long.

@@ -678,7 +678,8 @@ def has_function(self, funcname, prefix, env, extra_args=None, dependencies=None
head, main = self._no_prototype_templ()
templ = head + stubs_fail + main

if self.links(templ.format(**fargs), env, extra_args, dependencies):
if not funcname.startswith('__builtin_') and \
Copy link
Contributor

@textshell textshell Jun 3, 2018

Choose a reason for hiding this comment

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

I do not think meson should have this assumption, why is this needed? Optimization?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, that check will always fail with builtins because it tries to take the address of the function.

Copy link
Contributor

Choose a reason for hiding this comment

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

Which is fine, it just fails and meson goes on to the builtin test.
But what happens if there is a function that matches that pattern that is not a builtin? I think hardcoding that naming convention is just assuming things that might not be true. So i would always let that test fail. And if in some odd environment a function with such an name is an actual external function it will not break just because of naming.

t = '''{prefix}
int main() {{
#ifdef __has_builtin
#if !__has_builtin(__builtin_{func})
#if !__has_builtin({func})
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

But does this work without also adding implicit includes for the cases where users currently got away with not giving the includes?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, the alloca test checks for this already (on clang).

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, it seems to work with alloca, but with isgreaterequal it does not. Now it might be my clang install (it's just for compiling qtcreator). But a simple manual build test with clang seems to work. But even has_function('isgreaterequal', prefix: '#include <math.h>') does not work for me at the moment. It works with a random older meson.

__builtin_{func};
#elif defined(__stub_{func}) || defined(_stub__{func})
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need a comment similar to the one near stubs_fail to make it easier to understand.
Do we need to make sure that limits.h is included? Because the comment above implies it is needed for this to work.

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed.

prefix : '#include <alloca.h>\n#undef alloca',
args : unit_test_args),
'built-in alloca must not be found with #include and #undef')
'built-in alloca must be found with #include and #undef')
Copy link
Contributor

Choose a reason for hiding this comment

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

How does this work? It seems like it should not?

Copy link
Member Author

@nirbheek nirbheek Jun 3, 2018

Choose a reason for hiding this comment

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

The test was added to ensure that the __builtin_foo codepath is not hit when there is a #include in prefix because there was a #error in the other path. That other path is gone now, and we have a fallback check for foo there now.

alloca may not be a #define now, but it is still available as a prototype. See: alloca.h.

if ['gcc', 'clang'].contains(cc.get_id())
assert(cc.has_function('__builtin_smul_overflow'), '__builtin_smul_overflow must be found')
# FIXME: this fails on gcc, and we can't fix it without breaking builtin
# detection without headers for things like alloca()
Copy link
Contributor

Choose a reason for hiding this comment

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

I think with the implicit include workaround we can enable this test for gcc as well.

@@ -704,10 +706,12 @@ def has_function(self, funcname, prefix, env, extra_args=None, dependencies=None
* We would always check for this, but we get false positives on
* MSYS2 if we do. Their toolchain is broken, but we can at least
* give them a workaround. */
#if {no_includes:d}
#if {no_includes:d} && !{func_is_builtin:d}
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should try if the idea with implicit includes allows us to avoid this.

Copy link
Member Author

Choose a reason for hiding this comment

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

I think we should do that in a separate PR since it has a large potential for a yak-shave.

Copy link
Contributor

Choose a reason for hiding this comment

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

If this is the only place where we would need that, i'm ok with splitting that part of into a separate PR.

* __has_builtin(foo) is more correct and more reliable than
  __has_builtin(__builtin_foo)
* If detection of built-ins with headers fails in the first check
  because we can't take the address of a built-in, we can do an
  ordinary check.

We can't always do this ordinary check because clang on macOS ld
removes the symbol when using -Wl,-no_weak_imports, which is needed
for being able to target the correct platform with XCode 8 and newer.

Closes #3672
Some functions are meant to be used only as compiler builtins, such as
__builtin_smul_overflow and friends.

Detect this case and don't look for __builtin_foo because such a check
will never include a header.

Fixes #3676
@nirbheek nirbheek force-pushed the nirbheek/mingw-has_function-quirks branch from 5bebeaa to ce3121b Compare June 3, 2018 13:10
@nirbheek nirbheek changed the title has_function: Don't use builtin detection at all on MinGW has_function: Improve builtin detection Jun 3, 2018
Who knows maybe people are dumb and use __builtin_ as a prefix to
their own functions
This allows us to also accept functions that are simple pre-processor
defines in the header pointing to an inline definition, built-in
implementation, etc.

Also add a test for all the builtins listed by @textshell here:

#3675 (comment)
@nirbheek nirbheek self-assigned this Jun 3, 2018
@textshell
Copy link
Contributor

Sorry, not yet gotten to review the current version.

But from discussing with @ePirat and from @ePirat's testing i stumbled over this "nice" loophole with builtins:

https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html says:

With the exception of built-ins that have library equivalents such as the standard C library functions discussed below, or that expand to library calls, GCC built-in functions are always expanded inline and thus do not have corresponding entry points and their address cannot be obtained. Attempting to use them in an expression other than a function call results in a compile-time error.

GCC includes built-in versions of many of the functions in the standard C library. These functions come in two forms: one whose names start with the _builtin prefix, and the other without. Both forms have the same type (including prototype), the same address (when their address is taken), and the same meaning as the C library functions even if you specify the -fno-builtin option see C Dialect Options). Many of these functions are only optimized in certain cases; if they are not optimized in a particular case, a call to the library function is emitted.

And then goes on to list a ton of functions that are special in this way. So all functions from the list are builtins and still need their implementation to be available for linking. So this basically makes this a lot harder to do right.

But i think passing -fno-builtin to the test compilation would help. Btw, that option name is a bit misleading, as it's documented to only disable builtins that do not start with __builtin. But i would expect it to also not disable the "mandatory" builtins that don't have a non builtin version, but that would need checking.

Testing by @ePirat suggests that this would for nstrdup as well as the cases covered by the existing tests.

@nirbheek
Copy link
Member Author

nirbheek commented Jul 2, 2018

I didn't get enough time to look into this, and it's too dangerous to change this now because it might regress existing users. Will work on this after the 0.47 release which will happen today.

@nirbheek nirbheek modified the milestones: meson-next, 0.48.0 Jul 7, 2018
@jpakkane
Copy link
Member

⚔️ conflicts.

@nirbheek nirbheek added the WIP label Aug 29, 2018
@nirbheek
Copy link
Member Author

This is WIP, not ready to merge yet. I have to add the 'loophole' that @textshell pointed out above.

@ePirat
Copy link
Contributor

ePirat commented Sep 16, 2018

#3902 is somewhat related

@nirbheek nirbheek modified the milestones: 0.48.0, 0.49.0 Sep 20, 2018
@nirbheek
Copy link
Member Author

Fixed by #7116.

@nirbheek nirbheek closed this Jul 12, 2020
@nirbheek nirbheek deleted the nirbheek/mingw-has_function-quirks branch July 12, 2020 08:53
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.

has_function misdetects smul_overflow as available. Meson misdetects some functions with mingw-w64
4 participants