This change is based on work from @Quuxplusone in D67524 and D61761. All credit to them, all blame to me for any errors or problems, as I may have accidentally made it worse. :)
Previous change: D114732
The benefit here is that it can make trivially relocatable types -- by which, for now, we mean "types which are trivial for calls" -- easy to optimize. I have measured performance improvements of up to 0.25% on some large server macrobenchmarks.
For microbenchmarks, with my copy of clang, this change makes std::vector<std::unique_ptr<T>>::push_back ~38% faster when trivial_abi is enabled on unique_ptr.
This also also to unlocks further optimizations: it is, after this change, even more profitable to convert an existing type to trivial_abi, e.g. to enable it on even more types in the standard library than just the smart pointers. For example: it could become more worthwhile, after this change, to try to mark std::string as [[trivial_abi]].
Changes from D61761 and D67524:
- This change does not include support for std::swap (*relatively* low value, and very difficult to do correctly), just vector operations. It also does not include the relocate_at family of functions.
- (minor) Use __is_default_allocator instead of a new __is_exactly_std_allocator. At least to my eye, these seemed identical.
- bugfix to erase: it would memove N *bytes*, instead of N *items*.
- The tests now use std::conditional to enable trivial relocatability, as [[clang::trivial_abi]] doesn't take a bool parameter.
- Due to a (new?) warning that breaks the test, I had to add a layer of indirection to the countdown in insert_exception_safety_pass.
- in is_trivially_relocatable.pass.cpp:
- I renamed the classes to suit my personal taste (e.g. NonEmpty->Polymorphic, as it's a nonempty polymorphic class).
- I moved the static_asserts directly into main, so that I could see which one failed. This drops the test that is_trivially_relocatable and is_trivially_relocatable_v are identical, but this can be verified by inspection.
- The test asserted that const MoveOnly2 was trivially relocatable, even though it was not trivially move-constructible due to the const. I reversed the test, but I'm not 100% sure whether that was the right fix.
- I renamed the classes to suit my personal taste (e.g. NonEmpty->Polymorphic, as it's a nonempty polymorphic class).
- Finally, of course, some things in the source tree have changed, and I had to figure out where to put things. Not sure if I did it right!
The most important bit I want feedback on: are there any changes to the compiler needed to actually make it defined behavior to memcpy a trivially-relocatable type? I believe the answer is no, but want to double check. I chatted a bit with @rsmith, and the takeaway that I got was that Clang defines the behavior (or at least, doesn't exploit undefined behavior) for all non-polymorphic types. Since trivially relocatable types cannot be polymorphic, this should be a documentation-only change: we explicitly extend implicit-lifetime types to also include trivially-relocatable types (which are, at least for now, all trivial-abi types.)
It is possibly worthwhile to note that vector *already*, before this change, used memcpyor memmove on types which have nontrivial destructors, and are therefore not implicit-lifetime. So it already had in mind some extension to implicit-lifetime types, though not one which it describes explicitly. This change further extends it to also include all __is_trivially_relocatable types.
Pulling this out into somewhere we can thread the discussion:
As of D114732 / D119017, Clang now supports a built-in type trait __is_trivially_relocatable(T) which is currently true for types that are trivial for purposes of ABI and false for everyone else (including e.g. std::string and std::unique_ptr-with-the-default-ABI). The intent is that this state of affairs is not permanent, and eventually we'd like to extend the built-in type trait __is_trivially_relocatable(T) to encompass a greater set of types, perhaps corresponding to p1144, but we're not committing to exactly p1144 right now. But the people in charge of the [[clang::trivial_abi]] attribute are comfortable promising that those types will always be trivially relocatable in every possible sense of the word.
Clang does not currently support any way to "opt in" arbitrary types to trivial relocatability. For now, the only way to "become" trivially relocatable is to become also [[clang::trivial_abi]] at the same time.
This PR adds optimizations to std::vector<T> when __is_trivially_relocatable<T> (i.e., part of D67524), and also adds enough boilerplate in <type_traits> to support that optimization (i.e., part of D61761). It does not "opt in" any STL types — e.g., std::vector<int> itself will not be considered trivially relocatable even after this PR — because, as I said above, that's physically impossible in Clang so far.
This PR, if adopted, would shrink the diff between my trivially-relocatable branch and main, i.e., it's "friendly" in that sense.
I'm not saying it's a good idea, but I'm definitely not saying it's a bad idea. :)