Hi atomic fans 

(and non-fans I guess),
C++17 adds support for hardware destructive / constructive interference size constexpr values.
- cppreference: https://en.cppreference.com/w/cpp/thread/hardware_destructive_interference_size
- Paper with motivation: http://wg21.link/P0154
I volunteer to implement the necessary bits to support this in clang and libc++, and to give them proper values for current ARM and x86 processors. Iâve discussed this plan with other libc++ folks as well as libstdc++ / GCC folks, and we plan to implement the same builtins in both toolchains as well as adopt the same constexpr values wherever possible to keep ABIs compatible.
Under this plan, ARM and x86 will properly expose the new values in libc++, and other targets will automagically expose these values in C++ when theyâre updated with target-specific values in their target tablegen file. After a while targets that havenât settled on values will fail that one libc++ C++17 conformance test (for now the test will only check targets which expose the builtin).
FWIW MSVC already exposes this, but since they support fewer targets they decided on what everyone knows the right value is to expose: 64B. Weâre not so fortunate, so bear with me as I propose a plan:
1. Standard library support
Add the following code to header :
#if (__cplusplus >= 201703L) && __has_builtin(__builtin_hardware_destructive_interference_size) && __has_builtin(__builtin_hardware_constructive_interference_size)
inline constexpr std::size_t hardware_destructive_interference_size = __builtin_hardware_destructive_interference_size();
inline constexpr std::size_t hardware_constructive_interference_size = __builtin_hardware_constructive_interference_size();
#endif
Add corresponding tests which ensure that both values are at least alignof(std::max_align_t), and are constexpr. Conditionalize these tests on the same __has_builtin test for now. File a bug and leave a FIXME to move the test to just #if __cplusplus >= 201703L once targets have adopted this. libc++ will keep the __has_builtin test so that itâll compile just fine even if the builtin insât defined, it just wonât expose the values (so user code will only fail if they try to use these values).
2. Compiler support
- Teach the target infrastructure that hardware interference size is something they can specify (in tablegen files somewhere).
- Allow overriding the value in sub-targets using -march or -mcpu (the sub-target defines the numeric value, and the user gets the overriden one by using -march or -mcpu).
- Allow overriding the value (or defining, if the target doesnât already) on the command line using flags -mhardware-destructive-interference-size and -mhardware-constructive-interference-size. Initially I thought weâd go with -mattr, but those donât really allow values being passed.
- In clang, if these properties are set, expose the builtin. Donât expose a builtin if the value is not set by the target or on the command-line, such that the STL wonât expose a random value. Iâll expose them even if weâre in pre-C++17 mode, because theyâre builtins and libc++ only exposes the constexpr value if weâre in C++17 or later.
- For generic le32 / be32 ARM targets expose constructive / destructive as 64B.
- For generic le64 / be64 ARM targets expose constructive as 64B and destructive as 128B.
- For generic x86 expose constructive / destructive as 64B.
- Honor existing sub-target preferences (AFAICT x86 doesnât have any, ARM has some in AArch64Subtarget::initializeProperties). These override the generic ones above.
- Leave other targets as-is for now, since I canât test them and I donât know what the appropriate values would be. Hopefully this RFC will elicit feedback as to what the appropriate values are.
What do yâall think?
Thanks,
JF
We canât change the value based on -mcpu. We generally allow mixing code built with different values of -mcpu. And any code which is linked together must use the same value for hardware_destructive_interference_size, or else we violate ODR. -Eli
What does âas isâ mean? I think that we should not define values at all until someone with knowledge of the target sets values. These become part of the target ABI, and I donât think that we want to accidentally make an ABI choice for a target. -Hal
We canât change the value based on -mcpu. We generally allow mixing code built with different values of -mcpu. And any code which is linked together must use the same value for hardware_destructive_interference_size, or else we violate ODR.
Interesting point. The case Iâd like to cover is one where the developer wants to get the exact right value for their particular CPU, instead of a conservative answer with extra padding. How do you think we should meet this use case?
What does âas isâ mean? I think that we should not define values at all until someone with knowledge of the target sets values. These become part of the target ABI, and I donât think that we want to accidentally make an ABI choice for a target.
Targets other than ARM and x86 would, for now, not define the builtin at all. I think that meets exactly your point, or at least my intent was do do exactly as you want.
Go back to the standards committee and ask for a function that isnât constexpr? I canât think of any other reasonable solution. -Eli
Unfortunately, to define structure layouts they need to be constant. The best solution Iâve thought of is to extend the abi_tag support to force the mangling of interfaces depending on values of these constructs to be different. -Hal
Hi atomic fans 

(and non-fans I guess),
C++17 adds support for hardware destructive / constructive interference
size constexpr values.
- cppreference: https://en.cppreference.com/w/cpp/thread/
hardware_destructive_interference_size
<https://en.cppreference.com/w/cpp/thread/hardware_destructive_interference_size>
- Paper with motivation: P0154R1 constexpr std::hardware_{constructive,destructive}_interference_size
I volunteer to implement the necessary bits to support this in clang and
libc++, and to give them proper values for current ARM and x86 processors.
Iâve discussed this plan with other libc++ folks as well as libstdc++ / GCC
folks, and we plan to implement the same builtins in both toolchains as
well as adopt the same constexpr values wherever possible to keep ABIs
compatible.
Under this plan, ARM and x86 will properly expose the new values in
libc++, and other targets will automagically expose these values in C++
when theyâre updated with target-specific values in their target tablegen
file. After a while targets that havenât settled on values will fail that
one libc++ C++17 conformance test (for now the test will only check targets
which expose the builtin).
FWIW MSVC already exposes this, but since they support fewer targets they
decided on what everyone knows the right value is to expose: 64B. Weâre not
so fortunate, so bear with me as I propose a plan:
*1. Standard library support*
Add the following code to header <new>:
#if (__cplusplus >= 201703L) && __has_builtin(__builtin_
hardware_destructive_interference_size) && __has_builtin(__builtin_
hardware_constructive_interference_size)
inline constexpr std::size_t hardware_destructive_interference_size =
__builtin_hardware_destructive_interference_size();
inline constexpr std::size_t hardware_constructive_interference_size =
__builtin_hardware_constructive_interference_size();
#endif
Add corresponding tests which ensure that both values are at least
alignof(std::max_align_t), and are constexpr. Conditionalize these tests
on the same __has_builtin test for now. File a bug and leave a FIXME to
move the test to just #if __cplusplus >= 201703L once targets have
adopted this. libc++ will keep the __has_builtin test so that itâll compile
just fine even if the builtin insât defined, it just wonât expose the
values (so user code will only fail if they try to use these values).
Why do you propose modeling these as builtin functions rather than as
preprocessor defines? The latter is how we model every other similar
property.
*2. Compiler support*
1. Teach the target infrastructure that hardware interference size is
something they can specify (in tablegen files somewhere).
2. Allow overriding the value in sub-targets using -march or -mcpu
(the sub-target defines the numeric value, and the user gets the overriden
one by using -march or -mcpu).
We can't change the value based on -mcpu. We generally allow mixing code
built with different values of -mcpu. And any code which is linked
together must use the same value for hardware_destructive_interference_size,
or else we violate ODR.
Interesting point. The case Iâd like to cover is one where the developer
wants to get the exact right value for their particular CPU, instead of a
conservative answer with extra padding. How do you think we should meet
this use case?
Go back to the standards committee and ask for a function that isn't
constexpr? I can't think of any other reasonable solution.
Unfortunately, to define structure layouts they need to be constant.
The best solution I've thought of is to extend the abi_tag support to
force the mangling of interfaces depending on values of these constructs to
be different.
abi_tag is not an effective way of maintaining ABI, because it needs to be
"viral" / transitive, and can't be (at least, not without huge developer
effort).
Perhaps we could add an attribute
to hardware_{con,de}structive_interference_size that produces a warning if
they are used outside the main source file? We'd also need to make them
non-inline, which is an observable conformance break, but seems unlikely to
be important compared to the other issues.
We discussed both, nobody really cared either way, so builtins won. Macro is fine as well.
Interesting. I had thought that abi_tag was transitive. It occurs to me that Transitive ABI Infection Mechanism (TAIM) has a reasonable acronym.
- I suspect thatâs what we need in this case. I thought about suggesting this, but didnât, because I suspect that many/most uses will be in header files, just project-internal header files (because theyâll be defining structure layouts, padding arrays, etc.). I think that such a warning will be pretty noisy, unfortunately. Good point. Do you think that we should file a DR about this? I imagine that most everyone is going to be in the same boat in this regard. -Hal
Interesting. I had thought that abi_tag was transitive. It occurs to me that Transitive ABI Infection Mechanism (TAIM) has a reasonable acronym.
- I suspect thatâs what we need in this case. I thought about suggesting this, but didnât, because I suspect that many/most uses will be in header files, just project-internal header files (because theyâll be defining structure layouts, padding arrays, etc.). I think that such a warning will be pretty noisy, unfortunately. Good point. Do you think that we should file a DR about this? I imagine that most everyone is going to be in the same boat in this regard.
MSVC only ever sets it to 64, so they donât have this issue.
That was the original idea, but halfway through implementing it, they realized that forward declarations are a thing.
Thus it is unfortunately not possible to infer the ABI-name of a struct from its contents.
1. Teach the target infrastructure that hardware interference size is
something they can specify (in tablegen files somewhere).
2. Allow overriding the value in sub-targets using -march or -mcpu
(the sub-target defines the numeric value, and the user gets the overriden
one by using -march or -mcpu).
We can't change the value based on -mcpu. We generally allow mixing code
built with different values of -mcpu. And any code which is linked
together must use the same value for hardware_destructive_interference_size,
or else we violate ODR.
Interesting point. The case Iâd like to cover is one where the developer
wants to get the exact right value for their particular CPU, instead of a
conservative answer with extra padding. How do you think we should meet
this use case?
Go back to the standards committee and ask for a function that isn't
constexpr? I can't think of any other reasonable solution.
Unfortunately, to define structure layouts they need to be constant.
The best solution I've thought of is to extend the abi_tag support to
force the mangling of interfaces depending on values of these constructs to
be different.
abi_tag is not an effective way of maintaining ABI, because it needs to be
"viral" / transitive, and can't be (at least, not without huge developer
effort).
Interesting. I had thought that abi_tag was transitive.
It occurs to me that Transitive ABI Infection Mechanism (TAIM) has a
reasonable acronym.
- I suspect that's what we need in this case.
That's not possible, because classes can be forward-declared, and you need
to know what fields and base classes they have to transitively propagate
the taint.
GCC tries make it possible to transitively propagate the taint manually:
they have a warning for a type that uses a tainted type and isn't itself
declared with the abi_tag attribute. But in practice (at least for the
"C++11 ABI" abi_tag) the taint ends up affecting sufficiently many classes
as to make using the attribute that way infeasible to all but the most
dedicated.
Perhaps we could add an attribute to
hardware_{con,de}structive_interference_size
that produces a warning if they are used outside the main source file?
I thought about suggesting this, but didn't, because I suspect that
many/most uses will be in header files, just project-internal header files
(because they'll be defining structure layouts, padding arrays, etc.). I
think that such a warning will be pretty noisy, unfortunately.
We'd also need to make them non-inline, which is an observable conformance
break, but seems unlikely to be important compared to the other issues.
Good point. Do you think that we should file a DR about this? I imagine
that most everyone is going to be in the same boat in this regard.
Yes, we probably should. Making them non-inline would presumably mean that
we can tell the user it's their fault if the value differs between
translation units and they use it in an ODR-relevant context. That doesn't
solve the problem, but it does make it not our problem to solve, to a
certain extent. We're still left with this being a dangerous feature, but I
think that's really unavoidable if we want these things to be compile-time
constants.
Itâd certainly be unreasonable for a C++ stdlib to promise that the CPUâs cache layout will never change. And, IMO, weâd be better off not implementing these functions, rather than trying to work around the issue with various warnings/restrictions.
ISTM that user requirements may actually be best served by a non-constexpr function which can safely return the correct value on any platform. The situations where itâs not feasible to use dynamically-allocated memory for such an object seem pretty slim. And if youâre dynamically allocating memory, passing a runtime-constant alignment to the allocator is fine.
I disagree, we need a constexpr value. The original paper has an example, from your own employer, which youâll find copy / pasted all over the various forks of base/. Plenty of other projects just hard-code 64.
For both values on all platforms? -Hal