[cfe-dev] Smart Pointer Lifetime Optimizations
Zoe Carver via cfe-dev
cfe-dev at lists.llvm.org
Fri Jun 5 11:45:42 PDT 2020
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.
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
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.
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).
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.
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
With optimizations (branchless): https://pastebin.com/raw/mQ2r6pru
-------------- next part --------------
An HTML attachment was scrubbed...
More information about the cfe-dev