[cfe-dev] [libc++] A proposal for getting rid of __always_inline__ in _LIBCPP_INLINE_VISIBILITY
Louis Dionne via cfe-dev
cfe-dev at lists.llvm.org
Mon Jul 9 09:33:55 PDT 2018
There's been a lot of discussion about the use of __always_inline__ in libc++, both recently but also going back a long time. I would really like to solve this problem once and for all, so I’ve put together what I hope is a good summary of the situation, along with a proposal which crystallizes prior discussions on this list.
If I understood correctly, the use cases we are trying to support are:
1. An executable can link against DSOs that were built with different libc++ headers than those the executable is built with. This requires something to appear at the ABI boundary of libc++ only if it is ABI stable across versions (symbols, calling conventions, structure sizes, semantics, etc).
2. Two object files that were built using different libc++ headers can be linked into the same final linked image (executable/DSO). This requires anything on the ABI boundary of libc++ to be ABI stable (like (1)), but in addition requires the ABI unstable parts to be private to each object file (to avoid the linker de-duplicating incompatible functions that happen to have the same symbol).
The current solution is to:
- mark anything on the ABI boundary as being externally visible from the dylib (using one of the visibility macros like `_LIBCPP_TYPE_VIS` and `_LIBCPP_FUNC_VIS`), and
- mark most internal functions with `_LIBCPP_INLINE_VISIBILITY`, which forces inlining the function and gives the symbol hidden visibility for dylib visibility purposes, and
- mark some methods of extern templates with `_LIBCPP_EXTERN_TEMPLATE_INLINE_VISIBILITY`, which forces inlining the method, but still includes a copy of the method in the dylib and makes that symbol visible for dylib visibility purposes. This is required for methods that used to be exported in the dylib, but are now defined in the headers — those are still exported from the dylib for backwards compatibility purposes, but they are marked as __always_inline__ so they’re inlined into the caller if the caller is using recent libc++ headers. When the method can’t be inlined, the one from the dylib is used instead.
The problem here is that we're relying on inlining, which leads to debuggability and (potentially) code size problems. I believe we should continue to provide solutions for both (1) and (2), but make (2) opt-out for users that don't care about it.
Concretely, here's a proposal that (I think) achieves all of these goals:
1. Everything that is ABI stable is marked with a visibility macro like today (no change here).
2. Everything that is marked with `_LIBCPP_INLINE_VISIBILITY` today is marked with a new `_LIBCPP_HIDE_FROM_ABI` macro instead. This macro can be defined to `__attribute__((__visibility__("hidden")))` or to `__attribute__((__visibility__("hidden"),internal_linkage))` depending on whether we want to support only use case (1), or both use cases (1) and (2), respectively. This would look something like:
# define _LIBCPP_HIDE_FROM_ABI __attribute__((__visibility__("hidden")))
# define _LIBCPP_HIDE_FROM_ABI __attribute__((__visibility__("hidden"),internal_linkage))
In the future, we can decide which default behavior we want, but for now, I suggest we stick to what we have right now, which is support for both (1) and (2). It would be fine to change this in the future if we make that decision.
3. For the time being, we do NOT touch `_LIBCPP_EXTERN_TEMPLATE_INLINE_VISIBILITY`. This macro only decorates a few (40) small functions, so I don't think that leaving the `__always_inline__` there is a big deal. I’ve considered many potential alternatives to this based on what has been discussed on this list previously, but I think this should be tackled as part of a follow-up proposal because this problem has more subtleties to it. I will followup with a proposal in the future.
I’ve implemented the proposal outlined here and successfully ran all of libc++’s tests on my machine. I've also built libc++.dylib before and after applying my patch, and I verified that the symbols exported by the dylib are the same. For reference, I `diff`ed the output of the following command:
nm -g libc++.dylib | c++filt | cut -c 18-
Please let me know what you think about this and whether I’ve missed anything important. If we agree on doing this, I’ll put a patch up for review soon.
P.S.: For reference, here's all the prior discussions I could find on this topic (so you don't have to search like I did):
May/June 2018 (proposal to allow disabling _LIBCPP_INLINE_VISIBILITY):
April 2017 (follow-up on Duncan's plan):
July 2016 (pretty concrete plan by Duncan):
July 2016 (discussion about internal_linkage, taking addresses and deduplicating functions with internal linkage):
October 2015 (initial proposal to add the `internal_linkage` attribute to Clang):
More information about the cfe-dev