Skip to content

6.1: [OSSACanonicalizeOwned] Fix liveness passed to completion. #78674

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 Jan 16, 2025

Explanation: Fix a use-after-free on dead-end paths.

To ensure that it doesn't illegally hoist the destroys of any values across deinit barriers, CopyPropagation's OSSACanonicalizeOwned utility must add to liveness instructions at the end of a value's lifetime on every path. Concretely, the final destroys of a value are added to liveness. Until values have complete lifetimes throughout the OSSA pipeline, however, a value need not have a final destroy on a dead-end path. The utility must still add something to liveness to avoid "hoisting" a destroy from "the end of the dead-end path" to somewhere above it. To find the appropriate instructions to add to liveness, the utility relies on OSSACompleteLifetime to visit the availability boundary of the value; that boundary describes where the value's lifetime ends on dead-end paths (not always simply just before unreachable instructions, say). OSSACompleteLifetime depends on an instance of liveness for its work, and OSSACanonicalizeOwned provides it with a perturbed version of its own.

OSSACanonicalizeOwned operates on a "copy-extended value". OSSACompleteLifetime operates on a standard OSSA value. The liveness produced by OSSACanonicalizeOwned reflects that copy-extended-ness: it may contain consuming uses in the middle (corresponding to a consume of a copy of the base value in the middle). OSSACompleteLifetime relies on its liveness being that for a standard OSSA value: it may not have consuming uses in the middle (that would correspond to a use-after-free). So there is a mismatch in the characteristics of the liveness produced by the former and used by the latter.

Fix this by perturbing even more the copy of liveness passed from OSSACanonicalizeOwned to OSSACompleteLifetime: demote consuming uses in the middle to non-consuming uses.

Scope: Affects optimized code.
Issue: rdar://142846936
Original PR: #78624
Risk: Low.
Testing: Added tests.
Reviewer: Andrew Trick ( @atrick )

The pass is structured to drain an instruction worklist and perform a
sequence of operations on each popped instruction.  Extract that
sequence of operations into a new processInstruction function. Enables
testing the sequence on a single instruction.
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 determine where a lifetime ends within dead-end blocks,
OSSACanonicalizeOwned uses OSSACompleteLifetime's
visitAvailabilityBoundary.  This API takes a liveness which it uses to
walk forward to the availability boundary.  Specifically, the liveness
passed from OSSACanonicalizeOwned to OSSACompleteLifetime is a variation
of OSSACanonicalizeOwned's own liveness (it has destroys added).

There is a mismatch in the characteristics of livenesses created by
OSSACanonicalizeOwned and OSSACompleteLifetime:  The former deals with
not only direct uses of a value but also uses of its copies; that
introduces the possibility for consuming uses in the middle of liveness.
The latter on the other hand deals only with uses of a single value
(nestedly, but at each level of nesting only a single value).  Passing a
liveness from the former to the latter without handling this mismatch
is incorrect: OSSACompleteLifetime understands consuming uses to always
end a lifetime, even when they are in the middle of a copy-extended
liveness.  The result is that OSSACompleteLifetime produces non-sensical
results when provided with such a liveness.

To address this, fixup the liveness passed from OSSACanonicalizeOwned to
OSSACompleteLifetime by demoting consuming uses that appear within
(that's to say _not_ on the boundary) of liveness to non-consuming uses.

rdar://142846936
On main type annotations are not always required but on 6.1 they are.
@nate-chandler
Copy link
Contributor Author

@swift-ci please test

@nate-chandler nate-chandler marked this pull request as ready for review January 16, 2025 15:53
@nate-chandler nate-chandler requested a review from a team as a code owner January 16, 2025 15:53
@nate-chandler nate-chandler merged commit 1b9d4c7 into swiftlang:release/6.1 Jan 16, 2025
5 checks passed
@nate-chandler nate-chandler deleted the cherrypick/release/6.1/rdar142520491_2 branch January 16, 2025 15:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants