[libc++] Porting libc++ to embedded targets/compilers

Hello,

I am a developer in the compiler division (CodeGen) at Texas Instruments. Our compiler targets a variety of embedded targets from DSPs like the C6000 to microcontrollers like the C2000 and MSP430. All of our compilers use a common, modified EDG C++ front end accepting a majority of the C++14 standard in addition to many extensions allowed by both GCC and Clang, such as __has_feature.

We’re investigating using libc++ in addition to our home grown and augmented C (and C++ implementation-specific behavior) library to fill out our C++ support. Having worked on this for a week or so, I’ve cultivated a list of issues whose answers/solutions will help guide me in completing and trying to upstream some/all of this work.

  • Atomic builtins (__sync functions) are assumed to exist, and other atomic issues

Our first drop of C++14 will not have support for threads or atomics, since we’re still talking with other teams which support the low-level operations and APIs needed.

The CMakelists.txt file checks for atomic support, warning “Host compiler must support std::atomic”. Is this a major problem that could stop us from being able to use libc++ immediately?

For the __sync functions, we could define template functions to overload and perform the elemental operation without any guarantee of atomicity (I believe all uses are currently ‘int’ operations). Is this alright, assuming we will not be supporting multiple theads?

  • Is there any documentation on what exceptions.cpp needs to be doing?

From the looks of things, we need an implementation that defines everything in exception_fallback.ipp. If it’s as simple as that, great!

  • The CMake system seems to assume a Clang/GCC-like or a Microsoft command line.

For example, there are assumptions made about what options exist and look like. A solid example is the pre-inclusion option, which is ‘-include’ for Clang/GCC and ‘/FI’ for Microsoft. Our compiler expects the option to be ‘–preinclude’.

There are two solutions I’ve discussed internally that I’d like some input on:

The first is creating a wrapper script that will make our compiler act like GCC/Clang by translating options to what our compiler expects. This is not work that can be upstreamed, and would be more difficult for us to maintain in case of changes to libc++ since we’ll have to update our scripts whenever a new option is assumed by cmake. However, this avoids potentially bloating the CMake files with yet another compiler option set.

The second is to provide new attributes in the CMakelists.txt file that we can leverage to tell the build system what the option looks like. For example, for the ‘-include’ and ‘/FI’ switch, I could add LIBCXX_PREINCLUDE_OPTION, which defaults to either -include or /FI depending on COMPILER_ID. This makes things easier on our end, but will add extra work in CMakeLists.txt for each new option added to libc++ from here on out.

  • The proper way to handle ‘fallbacks’, which seem to exist in $LIBCXX_ROOT/include/support

Our library doesn’t support the POSIX locales (e.g.: locale_t), nor does it support the ‘__…’ predefined macros.

I modeled the predefined macros after the limit_defs.h found in win32/ibm. I’m assuming that’s the correct process, but I just want to make sure.

For locales, however, I couldn’t find a perfect analog, though the original fallback implementation in the git history came from Newlib, so I modeled after that header. It seems like the fallbacks completely negate the need for locale_t and _l variants of functions (and, thus, POSIX support), so that seemed like the right thing to do for our targets.

Fun fact: include/utility uses CHAR_BIT but doesn’t include limits as far as I could see. Before I added CHAR_BIT to our compiler, we were expanding the size_t parameter in __murmur2_or_cityhash to 0. Does Microsoft/IBM, who also define CHAR_BIT in an include/support header, have this happen?

  • The implementation of std::random_device does not support utilizing a random number engine in the absence of implementation-specific non-deterministic RNGs

Two third-party library solutions, arc4 and Sodium, and two /platform-specific solutions, Unix-like /dev/random and Microsoft srand_s are supported.

Could a fallback macro be used to implement random_device via another engine found in the random header? I think std::mt19937 is the simplest random engine that supports the full range of values of unsigned int.

If you’d like, I can split these questions out into separate threads so we can get into things in more detail.

We appreciate any opinions or comments on this project outside of the questions posed here, as well. I’m pretty new to not only the libc++ source, but also to llvm, clang, and even cmake. I’m slowly learning my way around, but I don’t purport to be fluent quite yet!

Thanks for your time,

J.B. Nagurne

Code Generation

Texas Instruments

There’s a CMake option LIBCXX_ENABLE_THREADS you can use to turn off multi-threaded support… but you might still have trouble with atomics. See . -Eli

Hi,

Hello,

I am a developer in the compiler division (CodeGen) at Texas Instruments. Our compiler targets a variety of embedded targets from DSPs like the C6000 to microcontrollers like the C2000 and MSP430. All of our compilers use a common, modified EDG C++ front end accepting a majority of the C++14 standard in addition to many extensions allowed by both GCC and Clang, such as __has_feature.

We’re investigating using libc++ in addition to our home grown and augmented C (and C++ implementation-specific behavior) library to fill out our C++ support. Having worked on this for a week or so, I’ve cultivated a list of issues whose answers/solutions will help guide me in completing and trying to upstream some/all of this work.

- Atomic builtins (__sync functions) are assumed to exist, and other atomic issues
Our first drop of C++14 will not have support for threads or atomics, since we’re still talking with other teams which support the low-level operations and APIs needed.
The CMakelists.txt file checks for atomic support, warning “Host compiler must support std::atomic”. Is this a major problem that could stop us from being able to use libc++ immediately?
For the __sync functions, we could define template functions to overload and perform the elemental operation without any guarantee of atomicity (I believe all uses are currently ‘int’ operations). Is this alright, assuming we will not be supporting multiple theads?

- Is there any documentation on what exceptions.cpp needs to be doing?
From the looks of things, we need an implementation that defines everything in exception_fallback.ipp. If it’s as simple as that, great!

- The CMake system seems to assume a Clang/GCC-like or a Microsoft command line.
For example, there are assumptions made about what options exist and look like. A solid example is the pre-inclusion option, which is ‘-include’ for Clang/GCC and ‘/FI’ for Microsoft. Our compiler expects the option to be ‘--preinclude’.

There are two solutions I’ve discussed internally that I’d like some input on:
The first is creating a wrapper script that will make our compiler act like GCC/Clang by translating options to what our compiler expects. This is not work that can be upstreamed, and would be more difficult for us to maintain in case of changes to libc++ since we’ll have to update our scripts whenever a new option is assumed by cmake. However, this avoids potentially bloating the CMake files with yet another compiler option set.

The second is to provide new attributes in the CMakelists.txt file that we can leverage to tell the build system what the option looks like. For example, for the ‘-include’ and ‘/FI’ switch, I could add LIBCXX_PREINCLUDE_OPTION, which defaults to either -include or /FI depending on COMPILER_ID. This makes things easier on our end, but will add extra work in CMakeLists.txt for each new option added to libc++ from here on out.

I just want to point out that in order to make the second option practical, the cmake logic for TI compilers would need to be tested in some continuous integration setup, so that committers would be aware when the TI build breaks.

- The proper way to handle ‘fallbacks’, which seem to exist in $LIBCXX_ROOT/include/support
Our library doesn’t support the POSIX locales (e.g.: locale_t), nor does it support the ‘__<FPType>...’ predefined macros.
I modeled the predefined macros after the limit_defs.h found in win32/ibm. I’m assuming that’s the correct process, but I just want to make sure.
For locales, however, I couldn’t find a perfect analog, though the original fallback implementation in the git history came from Newlib, so I modeled after that header. It seems like the fallbacks completely negate the need for locale_t and _l variants of functions (and, thus, POSIX support), so that seemed like the right thing to do for our targets.

Fun fact: include/utility uses __CHAR_BIT__ but doesn’t include limits as far as I could see. Before I added __CHAR_BIT__ to our compiler, we were expanding the size_t parameter in __murmur2_or_cityhash to 0. Does Microsoft/IBM, who also define __CHAR_BIT__ in an include/support header, have this happen?

- The implementation of std::random_device does not support utilizing a random number engine in the absence of implementation-specific non-deterministic RNGs
Two third-party library solutions, arc4 and Sodium, and two /platform-specific solutions, Unix-like /dev/random and Microsoft srand_s are supported.
Could a fallback macro be used to implement random_device via another engine found in the random header? I think std::mt19937 is the simplest random engine that supports the full range of values of unsigned int.

If you’d like, I can split these questions out into separate threads so we can get into things in more detail.
We appreciate any opinions or comments on this project outside of the questions posed here, as well. I’m pretty new to not only the libc++ source, but also to llvm, clang,

Welcome!

vedant

We're certainly prepared to set up CI to test any changes that are cleared for the upstream, but our short-term focus is to ensure the feasibility of the project.

Right now, most of the cmake work we've done is in a toolchain file so as to avoid disturbing much of the original logic. The -include problem, however, necessitated something a little more invasive.

Regards,
JB

We're certainly prepared to set up CI to test any changes that are cleared for the upstream, but our short-term focus is to ensure the feasibility of the project.

Right now, most of the cmake work we've done is in a toolchain file so as to avoid disturbing much of the original logic. The -include problem, however, necessitated something a little more invasive.

I'm not aware of any reasons we'd rule out such a change. I've CC'd Marshall, hopefully he can give you a more definitive answer.

vedant

Thanks, Eli.
This commit will actually do precisely what I was suggesting with regards to sync functions. Glad to see I want the only one wondering about this :slight_smile:

I'll patch this into my branch next chance I get a give it a spin

JB

Hello,

I am a developer in the compiler division (CodeGen) at Texas Instruments. Our compiler targets a variety of embedded targets from DSPs like the C6000 to microcontrollers like the C2000 and MSP430. All of our compilers use a common, modified EDG C++ front end accepting a majority of the C++14 standard in addition to many extensions allowed by both GCC and Clang, such as __has_feature.

We’re investigating using libc++ in addition to our home grown and augmented C (and C++ implementation-specific behavior) library to fill out our C++ support. Having worked on this for a week or so, I’ve cultivated a list of issues whose answers/solutions will help guide me in completing and trying to upstream some/all of this work.

- Atomic builtins (__sync functions) are assumed to exist, and other atomic issues
Our first drop of C++14 will not have support for threads or atomics, since we’re still talking with other teams which support the low-level operations and APIs needed.
The CMakelists.txt file checks for atomic support, warning “Host compiler must support std::atomic”. Is this a major problem that could stop us from being able to use libc++ immediately?
For the __sync functions, we could define template functions to overload and perform the elemental operation without any guarantee of atomicity (I believe all uses are currently ‘int’ operations). Is this alright, assuming we will not be supporting multiple theads?

I believe that it’s a good idea to support std::atomic anyway for compatibility, but in a deployment environment where there will never be threads, an implementation of std::atomic can simply use non-atomic arithmetic (though if you need interrupt safety for the atomics then you might need a slightly more complex compare and exchange operation).

- Is there any documentation on what exceptions.cpp needs to be doing?
From the looks of things, we need an implementation that defines everything in exception_fallback.ipp. If it’s as simple as that, great!

If you’re not throwing exceptions, exception_fallback should work for you. For embedded targets, if you are throwing exceptions then you probably want the libcxxrt version (libcxxrt is a much smaller binary than libc++abi).

- The proper way to handle ‘fallbacks’, which seem to exist in $LIBCXX_ROOT/include/support
Our library doesn’t support the POSIX locales (e.g.: locale_t), nor does it support the ‘__<FPType>...’ predefined macros.
I modeled the predefined macros after the limit_defs.h found in win32/ibm. I’m assuming that’s the correct process, but I just want to make sure.
For locales, however, I couldn’t find a perfect analog, though the original fallback implementation in the git history came from Newlib, so I modeled after that header. It seems like the fallbacks completely negate the need for locale_t and _l variants of functions (and, thus, POSIX support), so that seemed like the right thing to do for our targets.

The C++11 locale support is designed to be a thin wrapper around POSIX2008 locales. These are pretty heavy and likely not to be useful in an embedded target, where you’d only want to support the C locale (std::locale::classic). In this case, all of these would be compiled away. I don’t think we have such a fallback implementation, but a trivial one should be easy to write.

David

  • The implementation of std::random_device does not support utilizing a random number engine in the absence of implementation-specific non-deterministic RNGs

Two third-party library solutions, arc4 and Sodium, and two /platform-specific solutions, Unix-like /dev/random and Microsoft srand_s are supported.

Could a fallback macro be used to implement random_device via another engine found in the random header? I think std::mt19937 is the simplest random engine that supports the full range of values of unsigned int.

The intent of the standard and the expectation of users is that std::random_device provide true random (and not pseudo-random) numbers. In my opinion, it is better to omit std::random_device than it would be to make it provide pseudo-random numbers. If a user only needs pseudo-random numbers, then they can directly use the pseudo-random facilities.

The C++11 locale support is designed to be a thin wrapper around POSIX2008 locales. These are pretty heavy and likely not to be useful in an embedded target, where you’d only want to support the C locale (std::locale::classic). In this case, all of these would be compiled away. I don’t think we have such a fallback implementation, but a trivial one should be easy to write.

Assuming we're talking about the same thing, there's one in: `include/support/xlocale/__posix_l_fallback.h`. That's included, for example, by: `include/support/newlib/xlocale.h`

Jon

That provides implementations that ignore the std::locale, which is half of what you want. The other half is to provide a std::locale implementation where all of the method bodies are almost empty and where `locale(const char*)` and `locale(const string&)` always throw, so that the compiler can optimise away any uses of `std::locale` entirely (you don’t want to be carrying around a big structure that’s never used on an embedded target).

David

I would agree that the original intent of random_device is to provide truly random numbers. However there is an explicit clause (§26.5.6.2 in the C++14 standard) that blesses the use of a random number engine for implementations where non-deterministic random numbers are not supported.

JB