Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Will 'Guaranteed Copy Elision' (P0135, C++1z) potentially require ABI breakage?

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0135r0.html

The above proposal for 'Guaranteed Copy Elision' was voted into the C++ working paper in the June 2016 meeting in Oulu, Finland, which was then voted for publication as a committee draft. Hopefully that leads to publication as the C++17 standard next year.

The proposal clarifies various value categories involving temporary objects, to enforce the absence of copy constructor calls in certain use cases.

My question is "might this new requirement might break ABI compatibility for compilers that might previously not have done copy elision in these circumstances, or have implemented it in a way that won't be compatible with the new requirements?"

I'm thinking of things like initializations that elide copies when the creation of an object can be inlined, but not when crossing compilation unit boundaries.

like image 494
Phil Miller Avatar asked Jun 26 '16 19:06

Phil Miller


People also ask

Is copy elision guaranteed?

You can see this behavior in compiler versions Visual Studio 2017 15.6, Clang 4, GCC 7, and above. Despite the name of the paper and what you might read on the Internet, the new rules do not guarantee copy elision. Instead, the new value category rules are defined such that no copy exists in the first place.

What is a guaranteed copy?

Guaranteed copy elision redefines a number of C++ concepts, such that certain circumstances where copies/moves could be elided don't actually provoke a copy/move at all. The compiler isn't eliding a copy; the standard says that no such copying could ever happen. Consider this function: T Func() {return T();}


1 Answers

When a function gets called, the function has to return a value. That value needs memory to live in, but the return value needs to out-live the function itself. The ABI defines how this all works. Generally speaking, this happens by the caller providing a piece of memory of the size/alignment for the return value to the function.

So if a function computes a value and returns it, it has to (in theory) copy that computed value into the return value memory. And when the caller retrieves it, it has to (in theory) copy that return value memory into some other stack object for later use.

Non-guaranteed copy elision says that neither of these copies are necessary. On the returning-function side, the compiler is permitted to simply use the return value memory internally when generating that value, so the return statement doesn't have to copy anything. And on the receiving side, if the memory is going to be used to initialize a stack object, then it doesn't have to copy into that memory.

Guaranteed copy elision says that if the receiving side is initializing an object of the same type, then the receiver will not consider whether the object has a copy/move constructor. Thus, the code calling a function like auto t = Func(); will not treat it as a potential copy operation into t. The compiler processing that code will call Func with return value memory that is in the stack space for t.

And on the callee side, if you return a prvalue directly, then it is not necessary that a copy/move constructor exist. The callee will construct the prvalue directly in the return value memory.

Here's the thing: ABIs don't care about any of that. All the ABI cares about is low-level memory. That is, so long as the caller is passing return value memory of the appropriate size and alignment, and the callee is initializing that memory with an object of the appropriate type... the ABI doesn't care.

If the caller wants to use that return value memory for later operations, that's fine to the ABI. If the callee wants to initialize data directly into the return value memory instead of copying it, the ABI won't notice.

The ABI defines the interface; what you do with that interface is up to you.

As an example, consider the Itanium ABI on return values. It allows class types to be stored in registers, but only if they have trivial copy/move constructors. Otherwise, regardless of their contents, they must be constructed in memory provided by the calling function. If the class is trivially copyable, then you can't tell the difference between elision and non-elision.

The only way an ABI could pose a problem for this feature is if the ABI arbitrarily decided where the return value (and presumably the parameters) are stored, relative to each other. That is, the ABI forces the caller to place the object on the stack in a specific location relative to the parameters.

Could such an ABI exist? I have no particular knowledge to say that it cannot. Does it? I rather doubt it, as such an ABI would make elision quite difficult in general.

like image 186
Nicol Bolas Avatar answered Oct 27 '22 00:10

Nicol Bolas