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

1

17

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.

Chloric answered 26/6, 2016 at 19:43 Comment(7)
I can imagine an ABI (if a ridiculous one) that would be broken by this change. Would that be sufficient, or are you looking for actual instances of an ABI that is actually used by an actual non-toy compiler that breaks?Ashelyashen
How can you break something which does not exists?Savoirfaire
"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." I know of no such circumstances where that would be possible.Orvalorvan
the proposal is great. Always auto would be nice to haveBahadur
I thought I had an example of what compilers do now that wouldn't be allowed under the new rules, but I actually suspect it's not allowed under the current rules either, as it manages to avoid copying and thereby copy elision altogether. I asked it as a new question, but you may want to consider if small tweaks to it to introduce copy elision can get you an answer: #38043788Khelat
@Yakk I was looking for one or more actual instances of an ABI used by some non-toy compiler that breaks.Chloric
Interesting news from Oulu meeting (which is BTW a neighbor town across the border from Luleå). I have argued for a long time that nothing less than a guarantee from the standard is good enough to be able to use copy elision returns in performance-critical/hard real-time code. Note however the phrase 'This paper addresses only the first case. While we believe that reliable NRVO ("named return value optimization", the second bullet) is an important feature to allow reasoning about performance, the cases where NRVO is possible are subtle and a simple guarantee is difficult to give.'Mudslinger
O
12

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.

Orvalorvan answered 26/6, 2016 at 20:19 Comment(5)
The question then boils down to whether there are any extant compilers that implement the return value memory in some stupid way that would screw with either the callee writing the result directly into it and or the caller using the result directly from it.Chloric
@Novelocrat: The ABI defines how values mechanically are passed across function calls. The only way it could gets screwed up is if a compiler wasn't following the ABI. Which is a much larger problem than failing to implement guaranteed elision correctly.Orvalorvan
@Nicol: What if rather than allowing the caller to specify an arbitrary location where the return value will be placed, the ABI specifies it relative to something else (parameter locations, return address, etc). Then the caller may be unable to avoid a subsequent transfer from the ABI-defined location (which may be overwritten by additional function calls before the object's lifetime ends) to a longer-lived location.Relieve
@BenVoigt: I'm not expert on ABI's; I just have the general notion how it works. So I kinda defer to the standards committee's judgement on this. If nobody there had a problem with this, then there probably are no ABIs that do something like that.Orvalorvan
Ben's example case exactly illustrates what I thought could be going on.Chloric

© 2022 - 2024 — McMap. All rights reserved.