[cfe-dev] Erasures and bloat vs. ABI stability

David Krauss potswa at gmail.com
Thu May 21 02:30:27 PDT 2015

Hi guys,

We want to share implementation details between std::function and its new cousins which we are designing. A program which targets a class foo by std::function<void()> shouldn’t pay the same price a second time when it initializes a std::unique/movable_function<void()> with a foo as well. However, we also don’t want to use exactly the same erasure class for both wrappers, because then unique_function would instantiate copy constructors it cannot use, representing a different kind of bloat.

The solution is to share some functions between similar templates. This might violate libc++’s ABI standards, or it might not. Internal functions, including erasure methods, are declared using this macro:

#define _LIBCPP_INLINE_VISIBILITY __attribute__ ((__always_inline__))

The idea is that it’s dangerous for a function to be exported from a DLL, then vanish in a subsequent version at the whim of the inliner. A function which is always inlined will never be visible in the first place. This stabilizes not only libc++.dylib, but also user libraries with respect to the standard library.

The danger, but not the solution, also applies to vtables. If libc++ (or another library) uses a polymorphic class, then it’s committed to exporting it forever. The erasures of std::function are implemented using vtables, but this isn’t the only possible approach. libstdc++ uses switch statements instead.

Introducing new-but-similar specializations to the the vtable-polymorphic erasure class is making me confused.

Is it OK at all that shared libraries tend to export standard erasures? Or is this all a giant bug requiring a ground-up rewrite? (I’ve not dug into the history.)
If OK, should shared functions dominated by the vtables of several specializations be marked __noinline__? (Such a function is unreachable except through a vtable, which is already exported.) Is there some macro or protocol for this?
I’m spending this effort optimizing a case without first analyzing it. Has libc++ already decided whether to care about std::function overhead, or should I just make fully-independent specializations and be through with it? Is there a known, quantified margin of headroom?

Regarding #2, why does the virtual function std::__function::__base::~__base have inline visibility? Yes, it’s called directly by the derived class destructor, but that is only accessible by a vtable. Is there some potential ABI error if it omitted the visibility spec, or is it just erring on the side of caution? (All the other virtual functions omit the spec.)

	- Thanks much,
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20150521/9103fa5d/attachment.html>

More information about the cfe-dev mailing list