[cfe-dev] Smart Pointer Lifetime Optimizations

John McCall via cfe-dev cfe-dev at lists.llvm.org
Fri Jun 5 13:09:29 PDT 2020

On 5 Jun 2020, at 14:45, Zoe Carver via cfe-dev wrote:

> Hello all,
> I'm planning to do some work to add lifetime optimization passes for 
> smart
> pointers and reference-counted objects. I'll use this email as a sort 
> of
> proposal for what I hope to do.
> *Scope*
> As I'm developing the pass, I'm trying to keep it general and create
> utilities that could work across multiple smart pointers. But, right 
> now,
> I'm focussing on unique_ptr and applying specific ownership 
> optimizations to
> unique_ptr only.
> *unique_ptr Optimzations*
> The pass I'm currently developing adds a single, simple, optimization:
> constant fold the destructor based on ownership information. 
> unique_ptr has
> a lot of ownership information communicated with reference semantics. 
> When a
> unique_ptr is moved into another function, that function takes over
> ownership of the unique_ptr, and subsequent destructors can be 
> eliminated
> (because they will be no-ops). Otherwise, branchless functions are 
> often
> complicated after inlining unique_ptr's destructor so, this 
> optimization
> should be fairly beneficial.
> unique_ptr's reset and release methods both complicate this 
> optimization a
> bit. Because they are also able to transfer and remove ownership, all
> unknown instructions must be ignored. However, in the future, 
> knowledge of
> those methods might be able to make the pass more robust.
> With unique_ptr, it's difficult to prove liveness. So, it is hard to
> constant fold the destructor call to always be there. Maybe in the 
> future,
> this would be possible, though (with sufficient analysis).
> Last, an optimization that I hope to do is lowering the unique_ptr to 
> a raw
> pointer if all lifetime paths are known. I think removing this layer 
> of
> abstraction would make it easier for other optimization passes to be
> successful. Eventually, we may even be able to specialize functions 
> that
> used to take a unique_ptr to now take a raw pointer, if the argument's
> lifetime was also able to be fully analyzed.
> *Lifetime Annotations*
> Right now, the pass relies on (pre-inlined) function calls to generate
> ownership information. Another approach would be to add ownership
> annotations, such as the lifetime intrinsics (i.e. 
> llvm.lifetime.start).
> *ARC Optimizations*
> There are a huge number of large and small ARC optimizations already 
> in
> LLVM. For unique_ptr specifically, I'm not sure these are of any 
> benefit
> because unique_ptr doesn't actually do any reference counting. But, 
> later
> on, when I start working on generalizing this pass to support more 
> smart
> pointers (specifically shared_ptr) I think the ARC optimization pass, 
> and
> especially the utilities it contains, could be very beneficial. If 
> anyone
> has experience with ARC optimizations, I'd love to hear your thoughts 
> on
> extending them to other reference counted objects.
> *trivial_abi and Hidden References*
> Arthur O'Dwyer made a good point, which is that a lot of these
> optimizations can be applied when with the trivial_abi attribute. 
> However,
> given that's not a standard attribute and these optimizations only 
> *happen*
> to work with trivial_abi (i.e., in a more complicated program, they 
> may not
> continue to work). I think lifetime utilities and specific lifetime
> optimization passes are still beneficial (especially if they can be 
> applied
> to other smart pointers in the future).
> Because all smart pointers have non-trivial destructors, they are 
> always
> passed by hidden references. With unique_ptr, this is as simple as
> bit-casting the pointer member to unique_ptr, which would allow for it 
> to
> be lowered to a single raw pointer instead of a stack-allocated 
> object.
> Even without the trival_abi attribute, I think this is an optimization 
> that
> could be done.
> *Results*
> Here's the unique_ptr pass I've been talking about: ⚙ D81288 Opt 
> Smart
> pointer lifetime optimizations pass. <https://reviews.llvm.org/D81288>
> For reference, here are the before and after results:
> Clang trunk (four branches): Compiler Explorer
> <https://godbolt.org/z/bsJFty>
> With optimizations (branchless): https://pastebin.com/raw/mQ2r6pru

Unfortunately, these are not legal optimizations for your test case:

- `guaranteed` is permitted to escape a reference (or pointer) to the
   object it was passed.  Tat references and pointers remain valid
   until the object goes out of scope.

- The object can be mutated through that reference because the 
   object is not `const`.  Being passed a `const` reference is not a
   semantic contract in C++.

- Through a combination of the above, the call to `owner` may change
   the value of `p`, and so the caller may not rely on it still being
   in a trivially-destructible state after that call.

- `owner` may leave the value of its parameter object in a
   non-trivially-destructible state, and under the Itanium C++ ABI, 
   up that object is the caller’s responsibility.  I agree that this 
is a
   bad rule for optimization purposes, but it’s the rule.  This can 
only be
   optimized with a more global, interprocedural optimization that 
   responsibility to `owner` to destroy its argument.

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20200605/5bbee70e/attachment.html>

More information about the cfe-dev mailing list