Commit Graph

93 Commits

Author SHA1 Message Date
Joe Groff
1dcf271fe0 SIL: Treat store_borrow as borrowing its source, and have the move-only checker account for borrow scopes.
When rewriting uses of a noncopyable value, the move-only checker failed to take into account
the scope of borrowing uses when establishing the final lifetimes of values. One way this
manifested was when borrowed values get reabstracted from value to in-memory representations,
using a store_borrow instruction, the lifetime of the original borrow would be ended immediately
after the store_borrow begins rather than after the matching end_borrow. Fix this by, first,
changing `store_borrow` to be treated as a borrowing use of its source rather than an
interior-pointer use; this should be more accurate overall since `store_borrow` borrows the
entire source value for a well-scoped duration balanced by `end_borrow` instructions. That done,
change MoveOnlyBorrowToDestructureUtils so that when it sees a borrow use, it ends the borrow
at the end(s) of the use's borrow scope, instead of immediately after the beginning of the use.
2023-10-16 09:12:25 -07:00
Nate Chandler
9ca6b9ac1f [Test] Print to stdout.
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.
2023-10-10 08:19:44 -07:00
Nate Chandler
2824bf3125 [CanOSSALifetime] Record "unreachable" insts.
OSSALifetimeCompletion needs to insert not at unreachable instructions
that appear after the non-lifetime-ending boundary of a value but rather
at the terminators of the availability boundary of the value within that
region.  Once it does so, it will no longer be sufficient to check
whether the insertion point is an unreachable because such terminators
may be another terminator that appears on the availability boundary.
Prepare for that by recording the instructions that were found and
checking whether the destroy insertion point is such an instruction
before bailing rather than specifically checking for `unreachable`.
2023-10-04 16:48:06 -07:00
Nate Chandler
093d6a1bec [CanOSSALifetime] Treat deinit barrier as non-use.
Use PrunedLiveness' new extendToNonUse API to extend liveness up to
deinit barriers.
2023-09-29 17:02:33 -07:00
Nate Chandler
89140ecd13 [CanOSSALifetime] Treat end_access as non-use.
Use PrunedLiveness' new extendToNonUse API to extend liveness across
overlapping access scopes.
2023-09-26 20:07:23 -07:00
Nate Chandler
c1716c9dfe [PrunedLiveness] Add extendToNonUse.
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).
2023-09-26 11:45:22 -07:00
Nate Chandler
6c6776234a [CanOSSALife] Lexical lifetimes go to unreachables
When canonicalizing the lifetime of a lexical value, deinit barriers are
respected. This is done by walking backwards from lifetime ends and
adding encountered deinit barriers to liveness.

Only destroy lifetime ends were walked back from under the assumption
that lifetimes would be complete. Without complete OSSA lifetimes,
however, it's necessary to also necessary to consider lifetimes that end
with unreachables. Unfortunately, we can't simply walk back from those
unreachables because there may be instructions which are secretly users
of the value being canonicalized (e.g. destroys of `partial_apply`s to
which a `begin_borrow` of the value was passed). Such uses don't appear
in the use list because lifetime canonicalization expects complete
lifetimes and only visits lifetime ends of `begin_borrow`s.

Here, instead, the instructions before the relevant unreachables are
added to liveness. In order to determine which unreachables are
relevant, it's necessary to have a liveness that includes the original
destroys. So a copy of liveness is created and those destroys are added
to it.

rdar://115468707
2023-09-21 14:02:43 -07:00
Nate Chandler
9006be10e3 [CanOSSALifetime] Respect lexical lifetimes flag.
Don't respect deinit barriers when canonicalizing if lexical lifetimes
are disabled.
2023-09-20 13:40:08 -07:00
Nate Chandler
e912c036f6 [Reachability] NFC: Split initial from barrier.
Although by analogy with def instructions as barrier instructions one
could understand how a block where the def appears as a phi could be
regarded as a barrier block, the analogy is nonobvious.

Reachability knows the difference between an initial block and a barrier
block.  Although most current clients don't care about this distinction,
one does.  Here, Reachability calls back with visitInitialBlock for the
former and visitBarrierBlock for the latter.

Most clients are updated to have the same implementation in both
visitBarrierBlock and visitInitialBlock.  The findBarriersBackward
client is updated to retain the distinction and pass it on to its
clients.  Its one client, CanonicalizeOSSALifetime is updated to have a
simpler handling for barrier edges and to ignore the initial blocks.
2023-09-14 17:11:20 -07:00
Nate Chandler
d65f1132f7 [CanOSSALife] Respect barrier boundary edges.
When canonicalizing the lifetime of a lexical value, deinit barriers are
respected.  This is done by walking backwards from destroys and adding
encountered deinit barriers to liveness.

Previously, barrier edges did not result in any additions to liveness on
the theory that they would be rediscovered.  This is not always true (as
in the case of dead defs).

Here, each barrier edge different from the def block results in
additions to liveness.  Specifically, it results in the back of the
single predecessor being added to liveness.

rdar://115410893
2023-09-13 11:13:49 -07:00
Nate Chandler
c0783aa70f [Test] Ensourced canonicalize-ossa-lifetime.
Moved the test next to the code it calls.
2023-07-04 11:52:13 -07:00
nate-chandler
ca29d53e0d Merge pull request #66716 from nate-chandler/rdar110913116
[SILOptimizer] Don't optimize move-only lifetimes.
2023-06-16 19:42:09 -07:00
Nate Chandler
5d7aa84890 [SILOpt] Don't opt move-only lifetimes.
According to language rules, such lifetimes are fixed and the relative
order of their deinits is guaranteed.

rdar://110913116
2023-06-16 13:18:53 -07:00
nate-chandler
49e9284064 Merge pull request #66680 from nate-chandler/rdar110854874
[CanonicalizeOSSALifetime] Bail early in lifetime extension backwards walk.
2023-06-16 07:02:01 -07:00
Nate Chandler
57e226d470 [CanOSSALifetime] Bail early in lifetime extension
While collecting originalLiveBlocks, walking backward from consuming
blocks, if a visited block is already in originalLiveBlocks, don't visit
its predecessors.

Continuing the backwards walk is wasteful.

rdar://110854874
2023-06-15 13:03:15 -07:00
Nate Chandler
3e8985bd18 [CanOSSALifetime] Gardening: Tweaked comments. 2023-06-12 17:53:15 -07:00
Nate Chandler
3c4275a422 [CanonicalizeOSSALifetime] Renamed member.
The member just clears the values.  After it adopted BitfieldRef, the
call to invalidate on the liveness instance was already superfluous.
2023-05-02 11:51:52 -07:00
Nate Chandler
63e3ca7e17 [CanOSSALifetime] Xfer lexical value to callees.
When canonicalizing a lexical lifetime, don't treat applies which
consume a copy of the value as deinit barriers.  Doing so forces another
copy of the def being canonicalized to remain after the apply.

Instead, allow the lifetime to be transferred into the callee.  This is
the same behavior that already exists for lexical lifetimes represented
with the `begin_borrow [lexical]` + `copy_value` instruction sequence.
2023-04-03 16:29:21 -07:00
Nate Chandler
ec3f005f31 [CanonOSSALifetime] Run on lexical lifetimes.
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
2023-03-25 21:17:26 -07:00
Nate Chandler
f88ca5108b [CanonOSSALifetime] Fixed term boundary extension.
If multiple terminators which branch to the same merge block are added
to the boundary, depending on whether a destroy_value can be found
within the block either (a) every terminator must be added to the
boundary or (b) the destroy_value must be added to the boundary exactly
once.
2023-03-25 16:04:14 -07:00
Nate Chandler
7493b7e5ed [CopyPropagation] Correctly recorded destroys.
Now that destory_values implicitly end borrow scopes for
`partial_apply [on_stack]`s, they show up as users of values whose
lifetimes are being canonicalized.  Handle that properly by
(1) only adding the `destroy_value`s whose operand is a transitive copy
    of the def to the set of destroys
(2) not considering a `destroy_value` with another operand (i.e. one
    which does not destroy a transitive copy of the def) to be lifetime
    ending

rdar://107198526
2023-03-24 13:20:11 -07:00
Nate Chandler
0b09ed6cc1 NFC: CopyPropagation: Replaced copy walk with set.
Whenever attempting to determine whether a given instruction is a
destroy of the def being canonicalized, just check for membership in the
set of destroys that's already collected.
2023-03-24 13:19:55 -07:00
Andrew Trick
cbe856e53a Cleanup PrunedLiveBlocks
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.
2023-03-22 02:36:57 -07:00
Andrew Trick
ae64ff5cb0 Rename PrunedLiveness.clear() to invalidate()
Because SILBitfield cannot be cleared.
2023-03-22 01:36:48 -07:00
Joe Groff
8753d4847a CanonicalizeOSSALifetime: Distinguish destroys of the current def 2023-02-17 09:57:35 -08:00
Andrew Trick
4224d940ec Add visitInnerAdjacentPhis OSSA helper
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.
2023-02-06 23:11:23 -08:00
Michael Gottesman
20479c96fb [move-only] Refactor CanonicalizeOSSALifetime::canonicalizeValueLifetime into an API that computes liveness and a second API that rewrites copies/destroys and fix up MoveOnly checkers to use it.
For those who are unaware, CanonicalizeOSSALifetime::canonicalizeValueLifetime()
is really a high level driver routine for the functionality of
CanonicalizeOSSALifetime that computes liveness and then rewrites copies using
boundary information. This change introduces splits the implementation of
canonicalizeValueLifetime into two parts: a first part called computeLiveness
and a second part called rewriteLifetimes. Internally canonicalizeValueLifetime
still just calls these two methods.

The reason why I am doing this is that it lets the move only object checker use
the raw liveness information computed before the rewriting mucks with the
analysis information. This information is used by the checker to compute the raw
liveness boundary of a value and use that information to determine the list of
consuming uses not on the boundary, consuming uses on the boundary, and
non-consuming uses on the boundary. This is then used by later parts of the
checker to emit our errors.

Some additional benefits of doing this are:

1. I was able to eliminate callbacks in the rewriting stage of
CanonicalOSSALifetimes which previously gave the checker this information.

2. Previously the move checker did not have access to the non-consuming boundary
uses causing us to always fail appropriately, but sadly not emit a note showing
the non-consuming use. I am going to wire this up in a subsequent commit.

The other change to the implementation of the move checker that this caused is
that I needed to add an extra diagnostic check for instructions that consume the
value twice or consume the value and use the value. The reason why this must be
done is that liveness does not distinguish in between different operands on the
same instruction meaning such an error would be lost.
2023-02-04 10:43:13 -08:00
Nate Chandler
4f845ccc52 [CanOSSALifetime] Option to shrink to scopes.
For most uses, some access scopes must be "respected"--if an extended
value's original lifetime originally extends beyond an access scope, its
canonicalized lifetime must not end _within_ such scopes (although
ending before them is fine).  Currently, to be conservative, the utility
applies this behavior to all access scopes.

For move-only values, however, lifetimes end at final consumes without
regard to access scopes.

Allow this behavior to be controlled by whether or not a
NonLocalAccessBlockAnalysis is provided to the utility in its
constructor.

rdar://104635319
2023-01-28 10:23:22 -08:00
nate-chandler
2fc7659ed7 Merge pull request #60670 from nate-chandler/lexical_lifetimes/owned_arguments
[SIL] Maintain owned argument lifetimes at inlining.
2023-01-26 18:30:24 -08:00
Nate Chandler
10e86d6653 [CanonicalizeBorrowScope] Look through moves.
When encountering inside a borrow scope a non-lexical move_value or a
move_value [lexical] where the borrowed value is itself already lexical,
delete the move_value and regard its uses as uses of the moved-from
value.
2023-01-25 16:32:09 -08:00
Anthony Latsis
8eee0fff5e Merge pull request #62677 from valeriyvan/CanonicalizeOSSALifetime-enum 2023-01-26 02:30:45 +03:00
Valeriy Van
735faf9f19 Fix looking odd logical expression mixing bool and enum 2023-01-25 17:04:12 +02:00
Andrew Trick
f5480ade92 Cleanup canonicalize OSSA reborrow handling and add tests 2023-01-24 22:15:12 -08:00
Andrew Trick
599b1d1ae4 Handle guaranteed phis conservatively in a few more places. 2023-01-13 08:55:16 -08:00
Meghana Gupta
511739b494 Delete OperandOwnership::GuaranteedForwardingPhi
Use OperandOwnership::GuaranteedForwarding instead.
2022-12-13 12:51:31 -08:00
Meghana Gupta
786eb94853 Support @guaranteed forwarding phis 2022-10-19 19:54:27 -07:00
Meghana Gupta
b1f719709b Rename ForwardingBorrow -> GuaranteedForwarding 2022-10-19 19:54:27 -07:00
Nate Chandler
03253dbf48 [CanonicalizeOSSALifetime] Extend Onone lifetimes.
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
2022-10-08 16:08:35 -07:00
Nate Chandler
0289a6b8ce [Gardening] Tweaked whitespace in comment. 2022-10-08 16:08:35 -07:00
Nate Chandler
701ff06976 [Gardening] Clarified comment. 2022-10-08 16:08:35 -07:00
Nate Chandler
c1bbfc1f8a [CanonicalizeOSSALifetime] Separated steps.
Rather than having finding the boundary be a single combined step,
separate finding the original boundary from extending that boundary.
This enables inserting an optional step between those steps, namely to
extend unconsumed liveness to its original extent at Onone.
2022-10-08 16:08:35 -07:00
Nate Chandler
8cf65c6c8a [CanonicalizeOSSALifetime] Handle live phis.
It is possible for phis to be marked live.  With guaranteed phis, they
will be the last uses and be non-consuming.  In this case, the
merge block will have multiple predecessors whose terminators are on the
boundary.  When inserting destroys, track whether a merge point has been
visited previously.

To facilitate this, restructure the boundary extension and destroy
insertion code.

Previously, the extender was building up a list of places at which to
insert destroys.  In particular it was using the "boundary edge"
collection for all blocks at the beginning of which a destroy should be
created.  In particular, it would add merge blocks. Such blocks are not
boundary blocks.

Here, the extender produces a PrunedLivenessBoundary which doesn't
violate that invariant.

This required some changes to the destroy insertion code to find where
to insert destroys.  It is now similar to
PrunedLivenessBoundary::visitInsertionPoints and could be used as a
template for a PrunedLivenessBoundary::visitBoundaryPoints through which
::visitInsertionPoints might be factored.
2022-10-08 15:56:23 -07:00
Nate Chandler
4fc42a63a3 [CanonicalizeOSSALifetime] Renamed file.
Matched to the name of the utility.
2022-10-05 17:07:05 -07:00