For historical reasons, there was an API to check whether operands were
within the boundary which just checked whether those operands' users
were within the buondary. Make a copy of the method deal in
instructions and factor the original API through it.
Only return false if the visitor returns false. Clients were ignoring the
result.
If the BorrowingOperand does not create a borrow scope, call visitUnknownUse
instead.
Until we have complete lifetimes, to avoid breaking code that cannot handle dead
defs, consider a dead borrow scope to be an unknown use.
This is necessary to fix a recent OSSA bug that breaks common occurrences on
mark_dependence [nonescaping]. Rather than reverting that change above, we make
forward progress toward implicit borrows scopes, as was the original intention.
In the near future, all InteriorPointer instructions will create an implicit
borrow scope. This means we have the option of not emitting extraneous
begin/end_borrow instructions around intructions like ref_element_addr,
open_existential, and project_box. After that, we can also migrate
GuaranteedForwarding instructions like tuple_extract and struct_extract.
According to the documentation,
```
It's ok if postDomBlocks has duplicates or extraneous blocks, as long
as they jointly post-dominate all live blocks that aren't on dead-end
paths.
```
Previously, though, duplicates resulted in trapping: each element of
`postDomBlocks` is pushed into a `BasicBlockWorklist`; when the second
occurrence of an element in `postDomBlocks` was encountered,
`BasicBlockWorklist::push` would trigger an assertion failure.
Here, `pushIfNotVisited` is called instead.
To enable those clients which care only about _blocks_ whose
instructions or dead-defs make up the liveness boundary to avoid
iterating instruction lists, enhance boundary computation with a second
type (`PrunedLivenessBlockBoundary`) and migrate some methods from
liveness onto the boundary types to enable dispatching on the boundary
type to do only the amount of analysis that's necessary.
When checking whether an instruction is contained in a liveness
boundary, a pointer to a DeadEndBlocks instance must always be passed.
When the pointer is null, it is only checked that the instruction occurs
within the direct live region. When the pointer is non-null, it is
checked whether the instruction occurs within the region obtained by
extending the live region up to the availability boundary within
dead-end regions that are adjacent to the non-lifetime-ending portion of
the liveness boundary.
The areUsesWithinBoundary/areUsesOutsideBoundary methods take the
dead-ends because the region that they're checking for containment
within contains more than just (is a (non-strict) superset of) the
pruned live region. On the other hand, the region also contains _less_
than (is a (non-strict) subset of) the available region. Instructions
are in this extended region only if they're in a dead-end block which is
forwards reachable from the non-lifetime-ending liveness boundary.
A branch instruction that is both a lifetime-ending user and also a
non-lifetime-ending of a value should be regarded as a lifetime-ending
user.
Without this, utilities that rely on PrunedLiveness such as
`LinearLiveness` and `InteriorLiveness` both incorrectly report that a
branch featuring a value and also a reborrow or guaranteed phi are
non-ending.
rdar://130427564
Although I don't plan to bring over new assertions wholesale
into the current qualification branch, it's entirely possible
that various minor changes in main will use the new assertions;
having this basic support in the release branch will simplify that.
(This is why I'm adding the includes as a separate pass from
rewriting the individual assertions)
In the C++ sources it is slightly more convenient to dump to stderr than
to print to stdout, but it is rather more unsightly to print to stderr
from the Swift sources. Switch to stdout. Also allows the dump
functions to be marked debug only.
And use it in lifetime extension/maximization.
The new member function differs from updateForUse in that it doesn't
overwrite the old value for lifetime ending associated with the
instruction (calling updateForUse with lifetimeEnding=false overwrites
the flag set by a previous call with lifetimeEnding=true because if an
instruction both consumes and doesn't consume a copy-extended value, the
value must be live after the instruction).
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.