logoalt Hacker News

fluoridationtoday at 1:18 PM3 repliesview on HN

N/RVO works by (at the machine language level, of course) rewriting the function signature to return void and take an extra pointer parameter, which is written to before returning. If you're returning a newly-constructed object, the compiler can rewrite that into calling the constructor on the pointer, but if you're returning a named object, the class may have a non-trivial destructor that needs to run after the move, such that it's not possible to rewrite uses of the local object into uses of the pointer.

I'm not too confident on that last part, because such an implementation would mess with semantics in case of an exception, so anyone feel free to correct me on that.


Replies

jcranmertoday at 2:11 PM

I'm sorry, this comment is completely wrong.

NRVO does not affect the ABI of the function. It cannot affect the ABI, for whether or not it kicks in depends on the body of the function, and affecting the ABI would make it impossible to use it if only the declaration appears in a header.

The correct explanation is this:

In C++, classes with nontrivial destructors or copy/move constructors are considered nontrivial for the purposes of calls and are passed via pointers rather than via value. By passing via pointer, the class has a stable address and thus 'this' pointer. Returning such a class means the caller allocates the storage for the class on the stack before calling the function, and passes the pointer to that storage to the function as an extra parameter. This is based solely on the definition of the class itself; this happens whether or not NRVO kicks in.

Usually, when you declare a variable, the abstract machine of C++ requires you to construct a new object and call the copy/move constructors or assignment operators and the destructors at various times as appropriate. With nontrivial versions of these special functions, it is possible to observe whether or not they were called (these things still happen with trivial classes, but it's not so easy to observe). Returning a value requires constructing the storage space for that object--with all the attendant abstract machinery that involves.

What NRVO does is to say that, under certain conditions, rather than constructing storage space for a given variable that is normally required, the storage space that is allocated for the return value by the ABI is used instead. In essence, you are promoting a given variable to the return value hence the name 'Named Return Value Optimization'. What makes this annoying to implement is that you have to track at the AST level, before doing any code generation at all, whether or not a given variable is eligible for NRVO, and then use that information to control the code generation for allocating storage space.

Despite its name, NRVO is not actually an 'optimization' in the compiler. The optimizer plays no role in it, since the optimizer is fulfilling the requirements of the abstract machine. Instead, it is a set of conditions that allows the frontend to omit calls to copy constructors, etc. under specific circumstances.

meindnochtoday at 3:40 PM

>rewriting the function signature to return void and take an extra pointer parameter, which is written to before returning

This is completely unrelated to RVO. Every non-trivial class is returned via pointers to caller-allocated storage under the Itanium ABI. Period.

dataflowtoday at 1:21 PM

> N/RVO works by (at the machine language level, of course) rewriting the function signature to return void and take an extra pointer parameter

This sounds wrong, are you sure? Would you mind demonstrating with an example on godbolt? Whether NRVO applies or not, the ABI should be the same, AFAIK.

show 2 replies