OSSA utilities have a compile-time micro-optimization for computing the liveness
boundary. It takes advantage of the OSSA property that the consuming uses
post-dominate all uses. But when we extend liveness to incorporate new uses of a
guaranteed value, that optimization no longer applies. As a result, we end up
deleting a destroy_value that is in the middle of the new live range without
creating a new compensating destroy_value. Simply remove the optimization in
this case.
Fixes rdar://172778316: Found a leak due to a consuming
post-dominance failure
In the optimizer (= non mandatory) pipeline we don't need to maintain the lexical flags of `begin_borrow`, etc. anymore.
This enables more optimizations to be done.
The OwnershipRAUW utility is called by CSE, SILCombine, etc. whenever OSSA
values are substituted or combined. It handles ownership corner cases by
creating new copies. Destroys need to be insert for those new copies after all
original uses. It is impossible to do that when phis are involved. The utility
already checks for phis involving owned or guaranteed values, but unowned phis
were not anticipated.
Fixes rdar://168620481 swift-frontend crash: While running pass
SILFunctionTransform "GenericSpecializer"
This new OSSA invariant simplifies many optimizations because they don't have to take care of the corner case of incomplete lifetimes in dead-end blocks.
The implementation basically consists of these changes:
* add the lifetime completion utility
* add a flag in SILFunction which tells optimization that they need to run the lifetime completion utility
* let all optimizations complete lifetimes if necessary
* enable the ownership verifier to check complete lifetimes
Pass-invocations can be nested if a module pass creates an inner context for a specific function to modify.
This change
* removes the SwiftPassInvocation instance from SILCombine and let SILCombine use the current SwiftPassInvocation
* correctly use the innermost SwiftPassInvocation in various bridged utilities
When taking the location from the builder's insertion point, we must make sure it's not a return location.
Fixes an assertion failure.
I found this during my work on simplifying borrow scopes
The utility was computing the boundary of the `copy_value` with the
`end_borrow`s as users. But there may not be any `end_borrow`s thanks
to dead-ends. Instead, consider both guaranteed users and any
`end_borrow`s.
Fixes a miscompile in CSE.
rdar://158353230
CSE "looks through" ownership operations, which can lead to problematic substitutions. This fix cleans up owned operands even when the newly substituted value has no ownership.
For example:
%0 = enum $Optional<Interface>, #Optional.none!enumelt
%1 = move_value [lexical] %0
%2 = enum $Optional<Interface>, #Optional.none!enumelt
%3 = struct $EndpointCommon (%2)
%4 = struct $EndpointCommon (%1)
CSE combines the two .none enums:
%0 = enum $Optional<Interface>, #Optional.none!enumelt
%2 = enum $Optional<Interface>, #Optional.none!enumelt
Then combines the two structs:
%3 = struct $EndpointCommon (%2)
%4 = struct $EndpointCommon (%1)
Leaving a dead move_value:
%1 = move_value [lexical] %0
which is invalid OSSA. Now, when replacing the owned struct, we add destroys for its operands.
Fixes rdar://156431548 Error! Found a leaked owned value that was never consumed.
To handle borrowing operands that produce a dependence value but do not create a
nested borrow scope. This includes non-reborrow borrowed-from and guaranteed
mark_dependence [nonescaping].
CSE uses OSSA rauw which creates copies and copies that are created to optimize
across borrow scopes are unoptimizable. This PR avoids this situation for now.
To find if all the uses are within a guaranteed value, we should find all borrow introducers.
swift::findOwnershipReferenceAggregate looks only through forwarding operations with single operands.
For simplicity, continue using swift::findOwnershipReferenceAggregate, but return false when it does
not find a borrow introducer.
This is needed after running the SSAUpdater for an existing OSSA value, because the updater can
insert unnecessary phis in the middle of the original liverange which breaks up the original
liverange into smaller ones:
```
%1 = def_of_owned_value
%2 = begin_borrow %1
...
br bb2(%1)
bb2(%3 : @owned $T): // inserted by SSAUpdater
...
end_borrow %2 // use after end-of-lifetime!
destroy_value %3
```
It's not needed to run this utility if SSAUpdater is used to create a _new_ OSSA liverange.
Compute, update and handle borrowed-from instruction in various utilities and passes.
Also, used borrowed-from to simplify `gatherBorrowIntroducers` and `gatherEnclosingValues`.
Replace those utilities by `Value.getBorrowIntroducers` and `Value.getEnclosingValues`, which return a lazily computed Sequence of borrowed/enclosing values.
For years, optimizer engineers have been hitting a common bug caused by passes
assuming all SILValues have a parent function only to be surprised by SILUndef.
Generally we see SILUndef not that often so we see this come up later in
testing. This patch eliminates that problem by making SILUndef uniqued at the
function level instead of the module level. This ensures that it makes sense for
SILUndef to have a parent function, eliminating this possibility since we can
define an API to get its parent function.
rdar://123484595
llvm::SmallSetVector changed semantics
(https://reviews.llvm.org/D152497) resulting in build failures in Swift.
The old semantics allowed usage of types that did not have an
`operator==` because `SmallDenseSet` uses `DenseSetInfo<T>::isEqual` to
determine equality. The new implementation switched to using
`std::find`, which internally uses `operator==`. This type is used
pretty frequently with `swift::Type`, which intentionally deletes
`operator==` as it is not the canonical type and therefore cannot be
compared in normal circumstances.
This patch adds a new type-alias to the Swift namespace that provides
the old semantic behavior for `SmallSetVector`. I've also gone through
and replaced usages of `llvm::SmallSetVector` with the
`Swift::SmallSetVector` in places where we're storing a type that
doesn't implement or explicitly deletes `operator==`. The changes to
`llvm::SmallSetVector` should improve compile-time performance, so I
left the `llvm::SmallSetVector` where possible.
Add TermInst::forwardedOperand.
Add SILArgument::forwardedTerminatorResultOperand. This API will be
moved into a proper TerminatorResult abstraction.
Remove getSingleTerminatorOperand, which could be misused because it's
not necessarilly forwarding ownership.
Remove the isTransformationTerminator API, which is not useful or well
defined.
Rewrite several instances of complex logic to handle block arguments
with the simple terminator result API. This defines away potential
bugs where we don't detect casts that perform implicit conversion.
Replace uses of the SILPhiArgument type and code that explicitly
handle block arguments. Control flow is irrelevant in these
situations. SILPhiArgument needs to be deleted ASAP. Instead, use
simple APIs like SILArgument::isTerminatorResult(). Eventually this
will be replaced by a TerminatorResult type.
First restore the basic PrunedLiveness abstraction to its original
intention. Move code outside of the basic abstraction that polutes the
abstraction and is fundamentally wrong from the perspective of the
liveness abstraction.
Most clients need to reason about live ranges, including the def
points, not just liveness based on use points. Add a PrunedLiveRange
layer of types that understand where the live range is
defined. Knowing where the live range is defined (the kill set) helps
reliably check that arbitrary points are within the boundary. This
way, the client doesn't need to be manage this on its own. We can also
support holes in the live range for non-SSA liveness. This makes it
safe and correct for the way liveness is now being used. This layer
safety handles:
- multiple defs
- instructions that are both uses and defs
- dead values
- unreachable code
- self-loops
So it's no longer the client's responsibility to check these things!
Add SSAPrunedLiveness and MultiDefPrunedLiveness to safely handle each
situation.
Split code that I can't figure out into
DiagnosticPrunedLiveness. Hopefully it will be deleted soon.
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.
This fix unblocks unrelated optimizer commits. A unit test will be
introduced with those commits.
The RAUW utility does not correctly handle reborrows. It is in the
middle of being rewritten. For now, simply bail out since this isn't
an important case to optimize.
RAUWing a lexical value with a non-lexical value is illegal because it
would result in the value's lifetime being shortened without regard to
deinit barriers. RAUWing _with_ a lexical value is LEGAL so long as
doing so doesn't extend its lifetime.
Replacing all uses of a lexical value with another value, even another
lexical value, may change the lifetime of the value in ways that aren't
permitted for a lexical lifetime.
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.
This cleans up 90 instances of this warning and reduces the build spew
when building on Linux. This helps identify actual issues when
building which can get lost in the stream of warning messages. It also
helps restore the ability to build the compiler with gcc.
The InstructionDeleter needs to move the callbacks during construction
to prevent the client code from using the old callbacks.
Fixes iterator invalidation bugs.