[cfe-dev] Smart Pointer Lifetime Optimizations

Richard Smith via cfe-dev cfe-dev at lists.llvm.org
Wed Jun 10 12:55:59 PDT 2020


On Wed, 10 Jun 2020 at 12:18, John McCall via cfe-dev <
cfe-dev at lists.llvm.org> wrote:

> On 10 Jun 2020, at 14:32, Richard Smith wrote:
> > On Mon, 8 Jun 2020 at 19:52, John McCall via cfe-dev
> > <cfe-dev at lists.llvm.org>
> > wrote:
> >> It definitely changes observable semantics, but it’s not
> >> *obviously*
> >> non-conforming; [expr.call]p7 gives us a lot of flexibility here:
> >>
> >> It is implementation-defined whether the lifetime of a parameter
> >> ends when the function in which it is defined returns or at the
> >> end of the enclosing full-expression.
> >>
> > This is the non-conformance I'm referring to:
> > https://godbolt.org/z/cgf5_2
> >
> > Even given [expr.call]p7, we are still required to destroy
> > automatic-storage-duration objects in reverse construction order by
> > [stmt.jump]p2:
> >
> > "On exit from a scope (however accomplished), objects with automatic
> > storage duration (6.7.5.3) that have been constructed in that scope
> > are
> > destroyed in the reverse order of their construction."
>
> Don’t temporaries not have automatic storage duration formally?
>

The intent is that they don't; we have a longstanding open issue to
introduce a notion of "full-expression storage duration" to describe
temporary objects. But in the absence of such a language change, it's
unclear which utterances about "automatic storage duration" apply to
temporaries.

But in any case, I think that's immaterial here, because function
parameters are local variables, not temporary objects, and do have
automatic storage duration even in the hypothetical world where there's a
different storage duration for temporaries.

That’s why [class.temporary]p7 has to spell out the interordering
> of destruction of lifetime-extended temporaries.
>
> [expr.call]p7 is the most specific statement about the destruction
> of parameters.   Under normal principles of interpretation, it should
> take priority.
>

Well, p7 says nothing about the relative ordering of parameter
destructions, only the points where such destruction may occur. The only
place we constrain the relative ordering, as far as I know, is
[stmt.jump]p2, and that intends to cover the destruction of local variables
(including parameters) as part of a return statement. The [expr.call]p7
change intended to permit implementations to choose between caller and
callee cleanup, not to override the "destruction order is reverse
construction order" rule for automatic storage duration variables.

Doing the parameters in reverse order seems like a more serious problem,
> but one that we can address very specifically.  On targets where we
> normally emit left-to-right (everywhere except MS, right?), we can
> just destroy arguments right-to-left in the caller. I think we

probably do this already, because we probably push cleanups
> left-to-right.


We can't destroy [[trivial_abi]] parameters in the caller. That's the heart
of the problem: [[trivial_abi]] forces destruction into the callee, because
the caller doesn't have access to the parameter after the call.

The one exception about argument order is with
> assignment operators, where the standard forces us to emit the RHS
> first.  Simple assignment operators can only be declared as non-static
> member functions with one parameter, so there can only be one by-value
> parameter in the first place.  Compound assignment operators could in
> theory be overloaded with two by-value parameters, but of course
> they’ll usually have a reference on the LHS instead.  If we really
> feel strongly about this case, we could destroy left-to-right and
> thus make this only a problem when someone takes the address of an
> oddly-defined overloaded compound assignment operator.  Or we could
> call it a standard bug, yeah.
>

I'm arguing on the core reflector right now that this case is a standard
bug :) I think it would be great if we only got this wrong for an
address-taken non-member operator$= function (that's the only case where
right-to-left argument evaluation order is mandated and observable).


> >> Now, it’s possible that the copy-elision rules have an unfortunate
> >> impact here. IIRC an object initialized with an elided copy is
> >> supposed
> >> to take on the longer of the two natural lifetimes. Does that mean
> >> that
> >> if you have a parameter initialized by an elided copy from a
> >> temporary,
> >> the parameter needs to live until the end of the calling
> >> full-expression
> >> like the temporary would have? If so, you either wouldn’t be able
> >> to
> >> use a callee-destroy ABI or you wouldn’t be allowed to elide copies
> >> into parameters, and the latter seems unacceptable.
> >>
> > That case can no longer arise when initializing a function parameter
> > under
> > the new guaranteed copy-elision rules, so this problem is gone at
> > least in
> > C++17 onwards. But yes, historically I believe it was that case that
> > an
> > implementation that did callee cleanup was effectively not permitted
> > to
> > perform copy elision for function parameters (though of course
> > implementations did perform copy elision anyway).
>
> And rightly so. :)
>
> John.
> _______________________________________________
> cfe-dev mailing list
> cfe-dev at lists.llvm.org
> https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20200610/fd7ffbe1/attachment-0001.html>


More information about the cfe-dev mailing list