You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
With incomplete lifetimes, a value may have an "incomplete lifetime"--it
may not be destroyed on paths into dead-end regions. When a value lacks
a destroy on a path into a dead-end region, it is effectively destroyed
at its availability boundary (e.g. an `unreachable` instruction).
Indeed, lifetime completion amounts to making such implicit destroys
explicit (with correct nesting).
`SemanticARCOpts`' `performGuaranteedCopyValueOptimization` attempts to
eliminate a `copy_value` of a guaranteed value. To do so, it computes
the live range (`OwnershipLiveRange`) of the copy_value. Among other
things, it checks that all destroys of the copy_value are within the
scope of all guaranteed bases of the copied value. If any destroys are
_not_ within any of those scopes, the copy cannot be eliminated (because
the copy would have been originally live beyond the range which it would
be live in after copy elimination).
A value with an incomplete lifetime is implicitly destroyed at its
availability boundary. So those implicit destroys along the
availability boundary must _also_ be within the scopes of those
guaranteed bases.
Previously, implicit destroys along availability boundaries were not
considered. This resulted in invalid shortening of lifetimes--before
the transformation a value would be unconsumed up to its availability
boundary; after the transformation it would be consumed somewhere before
that. For example:
```
sil [ossa] @f : $@convention(thin) (@owned Outer) -> () {
entry(%o : $*Outer):
%o_borrow = begin_borrow %o : $Outer
%i = struct_extract %o_borrow : $Outer, #Outer.inner
%i_copy = copy_value %i : $Inner
end_borrow %o_borrow : $Outer
destroy_value %o : $Outer
apply @g(%i_copy) : $@convention(thin) (@guaranteed Inner) -> ()
unreachable // %i_copy implicitly destroyed
}
```
Here, `%i_copy` is implicitly destroyed at `unreachable` but `%i` is
consumed at the scope end of `%o_borrow`. That means that an attempt to
RAUW `%i_copy` with `%i` would be invalid because uses of `%i_copy` may
be outside the live range of `%i`.
(Note that this happens not to occur in the above example because
there's a bailout in the case that there are no destroys, but perturbing
the example to include a cond_br on one branch of which the value is
destroyed results in the miscompile described above.)
Here, this is fixed by adding the instructions on the availability
boundary at which the value is implicitly destroyed to the live range.
Do this by adding the instructions on the availability boundary of each
def from which the live range is generated to the live range.
One wrinkle is that the OwnershipLiveRange utility is used in one place
by a utility when SIL is in an invalid state (phi uses have been
replaced with undef so values leaks on those paths). Account for this
by manually fixing up the liveness instance passed to the lifetime
completion utility.
rdar://157291161
0 commit comments