This now supports liveness holes within blocks where previously we
expected a phi def.
PrunedLiveness is streamlined for SSA liveness where we expect the
defs and uses to be related in the SSA def-use graph.
MultiDefPrunedLiveness was added on top of SSAPrunedLivenes to handle
extended SSA liveness that occurs with phis, but are still connected
in the def-use graph. Recently MultiDefPrunedLiveness was repurposed
for liveness of addressible storage. This means that we can now have
uses before defs in the same block without a corresponding phi.
Fortunately, handling this case is a straightforward extension of
MultiDefPrunedLiveness that does not complicate or penalize the
streamlined SSA case.
PrunedLiveness only knows about the live blocks and uses.
The PrunedLiveRange subclass is now responsible for updating liveness
based on both the defs and uses. This is in preparation for handling
non-SSA liveness when uses occur before the first def.
This now supports liveness holes within blocks where previously we
expected a phi def.
PrunedLiveness is streamlined for SSA liveness where we expect the
defs and uses to be related in the SSA def-use graph.
MultiDefPrunedLiveness was added on top of SSAPrunedLivenes to handle
extended SSA liveness that occurs with phis, but are still connected
in the def-use graph. Recently MultiDefPrunedLiveness was repurposed
for liveness of addressible storage. This means that we can now have
uses before defs in the same block without a corresponding phi.
Fortunately, handling this case is a straightforward extension of
MultiDefPrunedLiveness that does not complicate or penalize the
streamlined SSA case.
PrunedLiveness only knows about the live blocks and uses.
The PrunedLiveRange subclass is now responsible for updating liveness
based on both the defs and uses. This is in preparation for handling
non-SSA liveness when uses occur before the first def.
Use BasicBlockBitfield to record per-block liveness state. This has
been the intention since BasicBlockBitfield was first introduced.
Remove the per-field bitfield from PrunedLiveBlocks. This
(re)specializes the data structure for scalar liveness and drastically
simplifies the implementation.
This utility is fundamental to all ownership utilities. It will be on
the critical path in many areas of the compiler, including at
-Onone. It needs to be minimal and as easy as possible for compiler
engineers to understand, investigate, and debug.
This is in preparation for fixing bugs related to multi-def liveness
as used by the move checker.
This will let the non-field sensitive version use a more performant
implementation internally. This is important since PrunedLiveBlocks is used in
the hot path when working with Ownership SSA, while the field sensitive version
is only used for certain diagnostics.
NOTE: I did not refactor PrunedLiveness to use the faster implementation... this
is just a quick pass over the code to prepare for that change.
This API is the inverse of visitEnclosingDefs when called on a phi.
This replaces the visitAdjacentReborrowsOfPhi algorithm with a small
loop that simply checks all the phis in the current block.
This should all be fairly efficient once SILArgument has a "reborrow"
flag.
In preparation for adding OwnershipLiveness.
Rename Simple LiveRangeSummary to LiveRangeSummary.
Add initializeDefNode helpers to avoid confusion about the argument
type.
Add defBegin/defEnd iterators in MultiDefPrunedLiveness.
Make sure liveness reports a complete boundary even for OSSA lifetimes
that are incomplete. Definition blocks can be on the liveness boundary
in this case.
Fixes an assert that was meant for a narrow case and accidentally
applied too broadly.
This variation of the liveness utilities wasn't being exercised widely,
and is difficult to test with valid OSSA.
I did this by doing the following:
1. I renamed computeUseBlockLiveness to computeScalarUseBlockLiveness and
changed it to take a specific bit that it is testing for.
2. I changed the logic that already existed in this code path that worked scalar
by scalar to use scalar logic rather than call the broken multi-bit at a time
code path.
3. We took advantage of resultingFoundLiveness now only returning the requested
bits instead of all bits. This ensures that we do not run
computeScalarUseBlockLiveness for those unneeded dead bits resulting in liveness
being inappropriately propagated into predecessors.
With guaranteed phis, we'll be able to encounter cases where the last
users are BranchInsts which use but don't consume a value. But even
without them, we can still test the API.
This allows dynamic dispatch into the fast paths. It's also easier to
understand the fast paths when written as separate loops.
This way, we can used MultiDefPrunedLiveness to compute ownership
liveness, but still benefit from the fact that the common case (owned
non-phi value) is much simpler and faster to compute.
This approach is necessary because we use stack-based data structures
within the liveness result, so we need to statically instantiate
something that can handle all cases.
We can remove a ton of complexity by assuming that SSA uses never
occur before a def in the same block. This will hold true as long as
useless phis are only removed after all unreachable code is first
removed. Then we don't have the self-loop problem.
The SILVerifier already checks this invariant and I added an in-depth
comment in SimplifyCFG.
Computing simple liveness is distinct from computing transitive
liveness. But for safety and consistency, always handle the first
level of liveness transitively. This way, computeSimple can be used on
guaranteed values that need OSSA lifetime fixup.
Simple liveness just means that *inner* borrow and address scopes are
assumed to be complete.
This utility is still only used conservatively because OSSA lifetime
completion is not yet enabled. But, in the future, computeSimple can
be used in the common case.
The API for computing simple liveness now returns a
SimpleLiveRangeSummary. Callers need to decide how to handle reborrows
and pointer escapes. If either condition exists then the resulting
liveness does not necessarily encapsulate the definition's ownership.
Fixes some number of latent bugs w.r.t. liveness clients.
First restore the basic PrunedLiveness abstraction to its original
intention. Move code outside of the basic abstraction that polutes the
abstraction and is fundamentally wrong from the perspective of the
liveness abstraction.
Most clients need to reason about live ranges, including the def
points, not just liveness based on use points. Add a PrunedLiveRange
layer of types that understand where the live range is
defined. Knowing where the live range is defined (the kill set) helps
reliably check that arbitrary points are within the boundary. This
way, the client doesn't need to be manage this on its own. We can also
support holes in the live range for non-SSA liveness. This makes it
safe and correct for the way liveness is now being used. This layer
safety handles:
- multiple defs
- instructions that are both uses and defs
- dead values
- unreachable code
- self-loops
So it's no longer the client's responsibility to check these things!
Add SSAPrunedLiveness and MultiDefPrunedLiveness to safely handle each
situation.
Split code that I can't figure out into
DiagnosticPrunedLiveness. Hopefully it will be deleted soon.
Fixes a bug in the simple PrunedLiveness routine for SSA values.
Without this fix, the init_existential_ref instruction below was not
considered a lifetime-ending use because the non-use type dependednt
operand (which is not really an operand at all) would override the
forwarding operand:
%1 = open_existential_ref %0 : $any P to $@opened("...", any P) Self
%2 = init_existential_ref %1 : $@opened("...", any P) Self : $@opened("...", any P) Self, $AnyObject
This will be tested by silgen_cleanup_complete_ossa.sil.
Some notes:
1. I added support for both loadable/address only types.
2. These tests are based off of porting the move only object tests for inout,
vars, mutating self, etc.
3. I did not include already written tests for address only types in this
specific merge since I need to change us to borrow move only var like types.
Without that, we get a lot of spurious error msgs and the burden of writing that
is not worth it. So instead in a forthcoming commit where I fix that issue in
SILGen, I will commit the corresponding address only tests for this work.
4. I did not include support for trivial types in this. I am going to do
object/address for that at the same time.
This is the same algorithm as pruned liveness but assumes that one is tracking
liveness from an address and uses the same scheme as DI to track liveness as a
bit vector.
The reason why I am doing this is that in order to be able to use
PrunedLivenessBlocks with multiple elements, we can no longer rely on dead being
represented by a block not having any state, since a dead bit could have a
neighboring live bit.
That being said, the only place that this is used now is the current
PrunedLiveness implementation which only stores a single bit... but that still
at least exercises the code and lets us know that it works.
This is useful for making a form of PrunedLiveness that is sensitive to address
fields. I wired up the current PrunedLiveness to just use PrunedLiveBlocks with
num bits set to 1.
SemanticARCOptVisitor::performGuaranteedCopyValueOptimization was
converting this SIL
%borrow = begin_borrow %copiedValue
%copy = copy_value %borrow
%borrowCopy = begin_borrow %copy
end_borrow %borrow
end_borrow %borrowCopy
destroy_value %copy
// something something
unreachable
into
%borrow = begin_borrow %copiedValue
%innerBorrow = begin_borrow %borrow
end_borrow %borrow
end_borrow %innerBorrow
// something something
unreachable
Dead-end blocks are simply irrelevant for this
optimization. Unfortunately, there were multiple layers of attempted
workarounds that were hiding the real problem, except in rare cases.
Thanks Nate Chandler for reducing the test.
This is just an initial prototype for people to play with. It is as always
behind the -enable-experimental-move-only flag.
NOTE: In this PR I implemented this only for 'local let' like things (local
lets/params). I did not implement in this PR support for local var and haven't
done anything with class ivars or globals.
rdar://83957028