Skip to content

[5.6] Copy-propagation enables lexical-lifetimes. #40517

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conversation

nate-chandler
Copy link
Contributor

@nate-chandler nate-chandler commented Dec 11, 2021

Commits needed to enable lexical lifetimes when copy propagation is enabled.

@nate-chandler
Copy link
Contributor Author

@swift-ci please test

@nate-chandler
Copy link
Contributor Author

@swift-ci please clean test windows platform

@atrick
Copy link
Contributor

atrick commented Dec 13, 2021

We'll want to add at least one more once the PR is merged:
#40533

which also contains the test cases for the last commit here 😊

@nate-chandler
Copy link
Contributor Author

nate-chandler commented Dec 13, 2021

@atrick Added! Along with #40519 and #40535.

nate-chandler and others added 23 commits December 20, 2021 13:34
Verify that lexical-lifetimes and copy-propagation interact correctly to
keep objects named by variables alive in a couple interesting cases:
unsafe pointers, synchronization point calls.
Use the result returned from shrinkgBorrowScope to update the changed
flag.
Promoted isRedundantLexicalBeginBorrow function to OwnershipUtils so
that it can be used in more than just SemanticARCOpts.
Previously, CanonicalizeInstruction::eliminateSimpleBorrows bailed out
when encountering any any [lexical] begin_borrow.  While generally such
borrow scopes must be preserved, they may be removed if they are
redundant (when the borrowed value is guaranteed by another means: an
outer lexical borrow scope or a guaranteed function argument).  Here,
removing such redundant lexical borrow scopes is enabled.
A guaranteed value produced by a begin_borrow can't be both used as
an operand of an ownership forwarding-instruction and also reborrowed by
being used as a phi argument.

Avoid that by stopping rotation when encountering a header block
containing an ownership-forwarding instruction whose forwarded ownership
kind is guaranteed; such a rotation may result in using both the
original guaranteed value and the resulting guaranteed value as phi
arguments.
The effect of passing -enable-copy-propagation is both to enable the
CopyPropagation pass to shorten object lifetimes and also to enable
lexical lifetimes to ensure that object lifetimes aren't shortened while
a variable is still in scope and used.

Add a new flag, -enable-lexical-borrow-scopes=true to override
-enable-copy-propagation's effect (setting it to ::ExperimentalLate) on
SILOptions::LexicalLifetimes that sets it to ::Early even in the face of
-enable-copy-propagation.  The old flag -disable-lexical-lifetimes is
renamed to -enable-lexical-borrow-scopes=false but continues to set that
option to ::Off even when -enable-copy-propagation is passed.
Until swiftlang#40392 lands, run destroy
hoisting with lexical lifetimes.
AllocStackHoisting creates new dealloc_stack instructions.  The old
dealloc_stack instructions' locations' kinds are ::CleanupKind  The new
instructions' locations need to be of that same kind too in order not to
trigger the sil verifier failure

    Basic block contains a non-contiguous lexical scope at -Onone

when building at Onone.
AutoDiffAllocateSubcontext and AutoDiffProjectTopLevelSubcontext
return RawPointer. They cannot be ForwardingBorrows. This was
triggering an assert in ForwardingOperand's constructor.
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.
The cases' new names more accurately reflect what behavior occurs when
SILOptions::LexicalLifetimes is assigned that case.
If -disable-copy-propagation is passed, just emit lexical diagnostic
markers but do not enable lexical lifetimes.
More bugs related to dead-end blocks and OSSA lifetimes that aren't
well formed because of that.

Independently, I'm working on prohibiting ill-formed OSSA even in the
presence of dead-end blocks. That makes these workarounds unnecessary,
but we urgently need a narrow fix here.
Previously the following pairs were prohibited incorrectly:
- -enable-lexical-borrow-scopes=true, -enable-experimental-move-only
- -enable-lexical-lifetimes=true, -enable-experimental-move-only
and the following pairs were allowed incorrectly:
- -enable-lexical-borrow-scopes=false, -enable-experimental-move-only
- -enable-lexical-lifetimes=false, -enable-experimental-move-only
Here, that's fixed.  The first two pairs are allowed and the second two
pairs prohibited.
Diagnose passing both -enable-copy-propagation and
-disable-copy-propagation.
Replaced the quad-state (of state which one was illegal) of two booleans
(EnableCopyPropagation and DisableCopyPropagation) with an enum.
In preparation to change the default values, specify explicitly what
they are.
Previously, the default value for SILOptions::LexicalLifetimes was based
on a copy propagation behavior (which can then be overridden by the
flags for lexical lifetimems) only when the copy propagation was
explicitly specified.  Instead, set base the default value for this
option (SILOptions::LexicalLifetimes) on the effective copy propagation
behavior (i.e. SILOptions::CopyPropagation) regardless of whether an
explicit behavior has been specified.

Doing so will ensure that the desired behavior occurs as the default
behavior for copy-propagation changes, but for now this change is NFC.
Replaced the -disable-copy-propagation flag with
-enable-copy-propagation=false where the latter is a new multi-var
-enable-copy-propagation= which can take one of three values:
- true
- requested-passes-only
- false
Replaced findInnerTransitiveGuaranteeedUsesOfBorrowedValue with
findExtendedUsesOfSimpleBorrowedValue.  Starting from a borrowed value,
it finds all extended (i.e., seeing through copies) uses of the borrow
and its projections within the simple (i.e. without considering
reborrowing) borrow scope.
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.
The blocksWithReachedTops variable is only ever used while finding
barriers and in a small helper function called at that time.  Make it
local to that function and make that helper a lambda in that function.
If an end_borrow cannot be hoisted, do not rewrite it.
Previously, ShrinkBorrowScope was only run alongside
CanonicalizeBorrowScope under the flag -canonical-ossa-rewrite-borrows.
That is no longer so, so remove that flag.  Update a test case to undo
the canonicalization which CanonicalizeBorrowScope was previously doing.
The new file tests the -O pipeline up to just before OSSA lowering.  It
will be used for verifying the effects of ShrinkBorrowScope on swift
code directly, to complement the tests done on SIL by just running a
single pass.
Previously, after determining that a block was dead, all its
predecessors were added to the worklist.  That is a problem, with the
current algorithm, if any of the predecessors have a terminator over
which the borrow scope cannot be shrunk.  Here, before adding a dead
block's predecessors to the worklist of blocks through which to shrink,
ensure that all those predecessors have terminators over which the scope
can be shrunk.  Finally, when beginning to process a block, if it
doesn't have a starting instruction, first hoist over thet terminator.
@nate-chandler nate-chandler force-pushed the cherrypick/lexical-lifetimes/enable-with-copy-propagation/release/5.6 branch from 3bf1ddb to 284559f Compare December 20, 2021 21:34
@nate-chandler
Copy link
Contributor Author

@swift-ci please test

@nate-chandler
Copy link
Contributor Author

@swift-ci please test source compatibility

@swift-ci
Copy link
Contributor

Build failed
Swift Test Linux Platform
Git Sha - 284559f

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - 284559f

The new Xllvm flag prints the module to stdout just before the OSSA
lowering pass runs.
@nate-chandler
Copy link
Contributor Author

@swift-ci please test

@nate-chandler
Copy link
Contributor Author

@swift-ci please test source compatibility

Copy link
Contributor

@atrick atrick left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for gathering all the dependent commits here.

@nate-chandler nate-chandler merged commit d6d848c into swiftlang:release/5.6 Dec 21, 2021
@nate-chandler nate-chandler deleted the cherrypick/lexical-lifetimes/enable-with-copy-propagation/release/5.6 branch December 21, 2021 22:02
@AnthonyLatsis AnthonyLatsis added 🍒 release cherry pick Flag: Release branch cherry picks swift 5.6 labels Jan 8, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🍒 release cherry pick Flag: Release branch cherry picks
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants