This is an RFC based on the design that was presented at the monthly libc meeting. For notes on our discussion, see the meeting notes: Monthly LLVM libc meeting - #11 by michaelrj-google
Context
Objective
The LLVM-libc and libc++ projects should be capable of sharing implementations to improve performance and reduce maintenance overhead.
Background
In functionality, libc and libc++ are very similar with slightly different interfaces. They both provide utilities for string manipulation, math, and general algorithms. Their interfaces are slightly different and incompatible, leading libc++ to be forced to either create an awkward translation layer (like in constexpr_c_functions.h), or to reimplement logic that already exists in libc (like in to_charās floating point support). This isnāt bad design, just a result of the constraints theyāre working with.
Additionally, LLVM-libc would like to support libc++, but some of the existing awkward translation layer uses obscure functions that LLVM-libc doesnāt yet support. (Like their use of strtoll_l with the C locale specified in locale)
Design
Overview
The core piece of this effort will be moving functionality shared by LLVM-libc and libc++ to a shared directory within the libc directory. This will allow LLVM-libc and libc++ to share code with a common interface, providing multiple benefits.
The shared code will be the same for both implementations, avoiding possible implementation mismatches. Small functions can be inlined into both libraries, and larger functions can be deduplicated with link time optimization when statically linking libc++ and libc. Implementation effort can be shared, reducing duplicated effort and improving developer velocity.
Detailed design
- Start of implementation is creating an interface in libc/shared
- The code in libc/shared will depend on libc/src/__support, which may only depend on libc/include/llvm-libc-macros and libc/include/llvm-libc-types
-
These folders should be header only, this is already mostly true. There are some cases of non-header files in support that will need to be cleaned up.
-
The interface will be designed to fit libc++ needs, but otherwise mostly be a thin wrapper over libcās existing support functions.
-
Create libc++ dependency into libc/shared
- This must be done so that none of the libc headers are even transitively included in the public libc++ headers
- If the libc shared functions are leaked to the public headers, then the header only structure would cause it to pull in large parts of the libc support machinery.
- We should have a header guard to ensure that this doesnāt happen, similar to what is currently done for headers in /usr/include/bits/
- This may mean transforming an existing public libc call (such as the call to strtoll_l) into a call to a libc++ internal function, defined in a .cpp file, that calls the libc/shared function.
- This may affect users who expect a given file to be header only, which files does that matter for?
- If the libc shared functions are leaked to the public headers, then the header only structure would cause it to pull in large parts of the libc support machinery.
- This will be a build system break for anyone not on cmake
- Might need to have a fallback implementation in libc++
- Might also need a libcxx/shared directory with a readme explaining what the libc/shared directory is
- The libc++ code must not include the libc shared interface from their public headers, since that would leak the libc internal code to userspace
- This must be done so that none of the libc headers are even transitively included in the public libc++ headers
-
Modify libc++ to remove translation layers to libc where possible (e.g. calling islower_l(char, c locale) becomes internal::islower(char))
-
Figure out what to do with __support/CPP since it contains standalone of implementations of libc++ functions
- At the start, just leave it. This is a complex issue that will require input from the libcxx contributors.
- Eventually we want to be able to share standalone code both ways between libc and libcxx
- In future, create a design that doesnāt require building libcxx before building libc, possibly by using header-only libraries.
- This may end up in a shared utility library, which has the same issues described below.
- Notably, once the build system issues are resolved they wonāt be an issue again.
- It could be argued that not creating this shared library is taking on technical debt, and this would be paying that down.
Alternatives considered
Project management
Work estimates
This is a new interface that will require continuous support, but after the initial lift it should be fairly simple to keep up.
Initial cleanup/build will take 2-4 weeks, depending on how long gathering approvals from stakeholders and finalizing the design takes. If the design is approved as-is, then the first function (from_chars using string to float) will likely take 1-2 weeks, with an additional 1 week for writing new documentation. Each further group of functions (e.g. ctype) would likely take an additional week, though these can be parallelized.
Ongoing support will mostly focus on bug fixes that take no additional time (since theyāll be necessary for libc anyways). If libcxx only uses the smallest subset of libc code necessary, then ongoing support will likely take 1-2 weeks per year. If libcxx integrates more deeply with the libc internals, then 4-5 weeks per year may be necessary, though some of that should be handled by libcxx developers.
Documentation plan
Downstream vendors of libc++ with their own build systems (e.g. Fuchsia) will need to be provided documentation on which pieces of libc are necessary. This should be created alongside the initial patch and distributed directly to users who live at head, then distributed alongside any future releases.
Libc and libc++ developers should document their internal interface during the design process, and make documentation available on LLVM developer pages after the libc/shared directory is created.