This does not enable it by default. Use either of the flags:
```
-enable-copy-propagation
-enable-copy-propagation=always
```
to enable it in -Onone. The previous frontend flag
`-enable-copy-propagation=true` has been renamed to
`-enable-copy-propagation=optimizing`, which is currently default.
rdar://107610971
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)
Only pass DeadEndBlocks to verifyOwnership if OSSAVerifyComplete is not
set. The verifier keys off of the nullness of DeadEndBlocks to decide
whether to verify complete lifetimes or not.
Owned lifetime canonicalization bails on move-only values.
Previously, though, every value that was fed to canonicalization was
then attempted to be deleted. For dead move-only values, the result
could be to shorten move-only lifetimes, which is illegal per language
rules.
Here, this is fixed by not attempting to delete owned values for which
canonicalization bailed.
rdar://114323803
Currently, CopyPropagation only canonicalizes defs that are "canonical",
that is, the root of the copy_value tree. When that canonical
def is lexical, however, the canonicalization respects deinit barriers.
But copies of lexical values are not themselves lexical, so their
lifetimes can be shortened without respect to deinit barriers.
Here, immediate copies of lexical values are canonicalized before the
lexical values themselves are.
rdar://107197935
Previously, the utility bailed out on lexical lifetimes because it
didn't respect deinit barriers. Here, deinit barriers are found and
added to liveness if the value is lexical. This enables copies to be
propagated without hoisting destroys over deinit barriers.
rdar://104630103
If a move_value is determined to be redundant and removed, take the
opportunity its removal makes available to canonicalize the moved-from
value without the obstruction of the move_value.
Add a run of ComputeSideEffects before the first run of CopyPropagation.
Allow hoisting over applies of functions that are able to be analyzed
not to be deinit barriers at this early point.
SemanticARCOpts already eliminates move values that are redundant that
block its optimizations. But it's always run after CopyPropagation.
Because move_values divide copy-extended lifetimes, move_values obstruct
lifetime canonicalization. If a move_value isn't separating lifetimes
with different characteristics (specifically: lexicallity, escaping),
then it is only obstructing lifetime canonicalization. Remove it
before canonicalizing the lifetime of the moved-from value.
Pass a BasicCalleeAnalysis instance to isDeinitBarrier. This enables
LexicalDestroyHoisting to hoist destroys over applies of functions which
are not deinit barriers.
Pass a BasicCalleeAnalysis instance to isDeinitBarrier. This will allow
ShrinkBorrowScope to hoist end_borrows over applies of functions which
are not deinit barriers.
To improve the debugging experience of values whose lifetimes are
canonicalized without compromising the semantics expressed in the source
language, when canonicalizing OSSA lifetimes at Onone, lengthen
lifetimes as much as possible without incurring copies that would be
eliminated at O.
rdar://99618502
Andy some time ago already created the new API but didn't go through and update
the old occurences. I did that in this PR and then deprecated the old API. The
tree is clean, so I could just remove it, but I decided to be nicer to
downstream people by deprecating it first.
Previously, the lifetimes of only values which were copied were
canonicalized during the non-mandatory CopyPropagation pass. The
mandatory pass (i.e. MandatoryCopyPropagation) has been disabled,
meaning that non-copied values lifetimes were never canonicalized. So
enable canonicalization of all lifetimes during regular CopyPropagation.
rdar://54335055
When canonicalizing an owned value's lifetime, also check whether the
value is dead. If it is, track it for deletion. In particular, this
eliminates dead copy_values.
Assertion failed: (succeed && "should be filtered by
FindBorrowScopeUses"), function canonicalizeFunctionArgument, file
CanonicalizeBorrowScope.cpp, line 798
Canonicalization for guaranteed function arguments is triggered by
SILCombine without any up-front analysis. Because the canonicalization
rewrites the function argument's copies in place, it must always
succeed.
Fix the visitBorrowScopeUses utility to be aware that it is being
invoked on a function argument and avoid bailing out.
Mandatory copy propagation was primarily a stop-gap until lexcial
lifetimes were implemented. It supposedly made variables lifetimes
more consistent between -O and -Onone builds. Now that lexical
lifetimes are enabled, it is no longer needed for that purpose (and
will never satisfactorily meet that goal anyway).
Mandatory copy propagation may be enabled again later as a -Onone "
optimization. But that requires a more careful audit of the effect on
debug information.
For now, it should be disabled.
Replaced ShrinkBorrowScope's own data flow with the general
BackwardReachability.
Took this opportunity to refactor and document the utility.
Taken together these changes make ShrinkBorrowScope serve as a template
for a future LexicalDestroyHoisting which will operate on owned lexical
values (rather than guaranteed as here) and hoist destroy_values (rather
than end_borrows as here) but should otherwise be quite similar.
So that CopyPropagation and other clients can react accordingly, pass
back a list of copy_value instructions that were rewritten by
ShrinkBorrowScope. In CopyPropagation, add each modified copy to the
copy worklist.
During copy propagation (for which -enable-copy-propagation must still
be passed), also try to shrink borrow scopes by hoisting end_borrows
using the newly added ShrinkBorrowScope utility.
Allow end_borrow instructions to be hoisted over instructions that are
not deinit barriers for the value which is borrowed. Deinit barriers
include uses of the value, loads of memory, loads of weak references
that may be zeroed during deinit, and "synchronization points".
rdar://79149830
OSSA rauw cleans up end of scope markers before rauw'ing.
This can lead to inadvertant deleting of end_lifetime, later
resulting in an ownership verifier error indicating a leak.
This PR stops treating end_lifetime scope ending like end_borrow/end_access.
This rewrites functionality that was mostly disabled but is now ready
to be enabled.
Allow lifetime canonicalization of owned values and function arguments
as a simple stand-alone utility. This is now being called from within
SILCombine, so we should only do the kind of canonicalization that
makes sense in that context.
Canonicalizing other borrow scopes should *not* be invoked as a
single-value cleanup because it affects other lifetimes outside the
borrow scope boundary. It is a somewhat complicated process that
hoists and sinks forwarding instructions and can generate surrounding
compensation code. The copy propagation pass knows how to post-process
the related lifetimes in just the right order. So borrow scope
rewriting should only be done in the copy propagation pass.
Similarly, only do simple canonicalization of owned values and
function arguments at -Onone.
The feature to canoncalize borrow scopes is now ready to be
enabled (-canonical-ossa-rewrite-borrows), but flipping the switch
should be a separate commit. So most of the functionality that was
affected is not exposed by this PR.
Changes:
Split canonicalization of owned lifetimes vs. borrowed lifetimes into
separate utilities. The owned lifetime utility is now back to being
the simple utility that I originally envisioned. So not much happened
to it other than removing complexity.
We now have a separate entry point for finding the starting point for
rewriting borrow scopes:
CanonicalizeBorrowScope::getCanonicalBorrowedDef.
We now have a utility that defines forwarding instructions that we can
treat consistently as part of a guaranteed lifetime,
CanonicalizeBorrowScope::isRewritableOSSAForward.
We now have a utility that defines the uses of a borrowed value that
are considered part of its lifetime,
CanonicalizeBorrowScope::visitBorrowScopeUses. This single utility is
used to implement three different parts of the alogrithm:
1. Find any uses of the borrowed value that need to be propagated
outside the borrow scope
2. RewriteInnerBorrowUses for SILFunction arguments and borrow scopes
with no outer uses.
3. RewriteOuterBorrowUses for borrow scopes with outer uses. Handling
these involves creating new copies outside the borrow scope and
hoisting forwarding instructions.
The end result is that a lot of borrow scopes can be eliminated and
owned values can be forwarded to destructures, reducing copies and
destroys.
If we stop generating borrow scopes for all interior pointers, then
we'll need to design a comparable optimization that works on
"implicit" borrow scopes:
%ownedDef = ...
%element struct_extract %ownedDef
%copy = copy_value %element
apply(@guaranteed %element)
apply(@owned %copy)
destroy %ownedDef
Should be:
%ownedDef = ...
%borrowedElement = destructure_struct @guaranteed %ownedDef
apply(@guaranteed %borrowedElement)
%ownedElement = destructure_struct %ownedDef
apply(@owned %copy)
It now handles looking through forwarding consumes such as
destructures.
Expected to be NFC since borrow consolidation is still disabled by
default.
TODO: Write unit tests for various forwarding consumes in addition to
destructure.
struct_extract and tuple_extract do not belong in OSSA (except to
workaround certain extreme cases). They completely defeat
simplification that OSSA provides for optimizing owned lifetimes.
Copy propagation uses this utility to canonicalize owned values before
canonicalizing their lifetime.
Ensure that any object lifetime that will be shortened ends in
destroy_value [poison], indicating that the reference should not be
dereferenced (e.g. by the debugger) beyond this point.
This has no way of knowing whether the object will actually be
deallocated at this point. It conservatively avoids showing garbage to
debuggers.
Part 1/2: rdar://75012368 (-Onone compiler support for early object
deinitialization with sentinel dead references)