[cfe-dev] Smart Pointer Lifetime Optimizations

Richard Smith via cfe-dev cfe-dev at lists.llvm.org
Wed Jun 10 15:30:21 PDT 2020


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

> On 10 Jun 2020, at 15:55, Richard Smith wrote:
> > 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.
>
> Not relative with each other, but I think it has to be understood
> as allowing differences relative to temporaries created by the calling
> full-expression.
>

Oh, definitely. Prior to C++17, that's a problem (as described in
http://itanium-cxx-abi.github.io/cxx-abi/argument-destruction.pdf). In
C++17 and later, though, there is never a case where we elide a copy of a
temporary into a parameter, so we never have a function parameter that
might need to outlive its call due to the "longer of the two lifetimes"
rule. The new model is that a prvalue of class type simply initializes its
target in-place, without even notionally creating and copying from a
temporary.

>> 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.
>
> Argh, sorry, I meant “callee” here.
>
> >> 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).
>
> Yeah.
>

Reflecting on that some more: suppose

* we define two entry points for a non-member 'operator$=' function where
one parameter is callee-cleanup and the other parameter has non-trivial
destruction (incredibly rare already)
* for functions with callee-cleanup parameters, we destroy the first
callee-cleanup parameters *and all subsequent parameters* right-to-left in
the callee
  - except that for the second entry point for 'operator$=' we instead
destroy the last callee-cleanup parameter and all prior parameters
left-to-right in the callee

Then we can get destruction order correct in all cases, and the only cost
is that the incredibly rare case of a non-member operator$= taking two
parameters of class type (with some additional conditions) gets an extra
symbol.

This (especially the second bullet) would be an ABI change for existing
users of [[trivial_abi]]. (Alternative simpler rule: if any parameter is
callee-cleanup then all parameter destruction happens in the callee. I'm
not sure if that's better or worse, but it seems simpler.)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20200610/798b882e/attachment-0001.html>


More information about the cfe-dev mailing list