The optimization replaces a `load [copy]` with a `load_borrow` if possible.
```
%1 = load [copy] %0
// no writes to %0
destroy_value %1
```
->
```
%1 = load_borrow %0
// no writes to %0
end_borrow %1
```
The new implementation uses alias-analysis (instead of a simple def-use walk), which is much more powerful.
rdar://115315849
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.
Reformatting everything now that we have `llvm` namespaces. I've
separated this from the main commit to help manage merge-conflicts and
for making it a bit easier to read the mega-patch.
This is phase-1 of switching from llvm::Optional to std::optional in the
next rebranch. llvm::Optional was removed from upstream LLVM, so we need
to migrate off rather soon. On Darwin, std::optional, and llvm::Optional
have the same layout, so we don't need to be as concerned about ABI
beyond the name mangling. `llvm::Optional` is only returned from one
function in
```
getStandardTypeSubst(StringRef TypeName,
bool allowConcurrencyManglings);
```
It's the return value, so it should not impact the mangling of the
function, and the layout is the same as `std::optional`, so it should be
mostly okay. This function doesn't appear to have users, and the ABI was
already broken 2 years ago for concurrency and no one seemed to notice
so this should be "okay".
I'm doing the migration incrementally so that folks working on main can
cherry-pick back to the release/5.9 branch. Once 5.9 is done and locked
away, then we can go through and finish the replacement. Since `None`
and `Optional` show up in contexts where they are not `llvm::None` and
`llvm::Optional`, I'm preparing the work now by going through and
removing the namespace unwrapping and making the `llvm` namespace
explicit. This should make it fairly mechanical to go through and
replace llvm::Optional with std::optional, and llvm::None with
std::nullopt. It's also a change that can be brought onto the
release/5.9 with minimal impact. This should be an NFC change.
In the context of promoting a `load [copy]` to a `load_borrow`, allow
the value representing the lifetime of the load to differ from the `load
[copy]` itself--it may either be the load or its only user.
If promoting the `load [copy]` to a `load_borrow` with the load itself
as the lifetime representative fails, look for a single `move_value` use
of the load. If found, attempt the optimization again with the
`move_value` as the lifetime representative.
rdar://108514447
Adds to SemanticARCOpts a new step of removing move_value instructions
if they are redundant. A move_value is redundant if it adds no new
information or optimization opportunities.
An example of adding information: a lifetime becoming lexical. The new
lifetime's destroys cannot be hoisted over deinit barriers.
An example of adding an optimization opportunity: the original value
escapes but the value produced by the move_value does not escape. So
destroys of the new value can be hoisted more aggressively.
`getValue` -> `value`
`getValueOr` -> `value_or`
`hasValue` -> `has_value`
`map` -> `transform`
The old API will be deprecated in the rebranch.
To avoid merge conflicts, use the new API already in the main branch.
rdar://102362022
Without this when constructing an InstModCallback it is hard to distinguish
which closure is meant for which operation when passed to the constructor of
InstModCallback (if this was in Swift, we could use argument labels, but we do
not have such things in c++).
This new value type sort of formulation makes it unambiguous which callback is
used for what when constructing one of these.
This bleeds into the implementation where "guaranteed" is used
everywhere to talk about optimization of guaranteed values. We need to
use mandatory to indicate we're talking about the pass pipeline.
Importantly this also lets us use the analysis framework to validate that we do
properly invalidate DeadEndBlocks, preventing bugs.
I did not thread this all over the compiler. Instead I just used it for now in
SemanticARCOpts just to add some coverage without threading it into too many
places.
Instead, I just added some static helper methods that perform the same
operations without needing to deal with generics/etc on OwnershipForwardingMixin
itself. The reason why I did this is that this Mixin is not part of the SILNode
heirarchy so we shouldn't use utilities tied to the SILNode hierarchy.
This allows for me to do a couple of things improving quality/correctness/ease of use:
1. I reimplemented InstMod's RAUW and RAUW/erase helpers on top of
setUseValue/deleteInst. Beyond allowing the caller to specify less things, we
gain an orthogonality preventing bugs like overriding erase/RAUW but not
overriding erase or having the erase used in erase/RAUW act differently than
the erase for deleteInst.
2. There were a bunch of places using InstModCallback that also were setting
uses without having the ability for InstModCallbacks perform it (since it
only supported RAUW). This is an anti-pattern and could cause subtle bugs to
be introduced by appropriate state in the caller not being updated.
Specifically, we check that all of the unowned's value uses are within the
lifetime of the unchecked_ownership_conversion's operand. If so, we eliminate
it.
Our worklist already uses values, so it makes sense to use a SILValueVisitor. It
will also let me throw in a few ARC dead argument elimination optimizations so
clean up a pattern from my ARC RAUW thing. Once we have simplify-cfg it will not
be needed, but I want to know I am good before I flip the switch.
This commit is doing a few things:
1. It is centralizing all decisions about whether an operand's owner instruction
or a value's parent instruction is forwarding in each SILInstruction
itself. This will prevent this information from getting out of sync.
2. This allowed me to hide the low level queries in OwnershipUtils.h that
determined if a SILNodeKind was "forwarding". I tried to minimize the amount of
churn in this PR and thus didn't remove the
is{Owned,Ownership,Guaranteed}Forwarding{Use,Value} checks. Instead I left them
alone but added in asserts to make sure that if the old impl ever returns true,
the neew impl does as well. In a subsequent commit, I am going to remove the old
impl in favor of isa queries.
3. I also in the process discovered that there were some instructions that were
being inconsistently marked as forwarding. All of the asserts in the PR caught
these and I fixed these inconsistencies.
This split will ensure we are testing only what a specific optimization is doing
rather than all together.
NOTE: The way I split up the tests is I first split up each of the tests by
subject area by hand that I thought were specifically testing one pass. Then any
tests where the FileCheck tests started to fail due to us not running the other
passes, I put back a copy in the original semantic-arc-opts.sil to ensure we do
not regress.
Note, the tests that I copied for each of these passes
are originally from semantic-arc-opts.sil. I left them there as well so we could
see all of the opts together. I also only copied the ones that were testing pass
specific functionality.
This doesn't happen as much as the guaranteed + owned value optimization but can
occur once we start eliminating borrow scopes. I noticed that I needed this when
looking at the arbitrary RAUW for ownership API.
I think what was happening here was that we were using one of the superclass
classofs and were getting lucky since in the place I was using this I was
guaranteed to have single value instructions and that is what I wrote as my
first case X ).
I also added more robust checks tieing the older isGuaranteed...* APIs to the
ForwardingOperand API. I also eliminated the notion of Branch being an owned
forwarding instruction. We only used this in one place in the compiler (when
finding owned value introducers), yet we treat a phi as an introducer, so we
would never hit a branch in our search since we would stop at the phi argument.
The bigger picture here is that this means that all "forwarding instructions"
either forward ownership for everything or for everything but owned/unowned.
And for those listening in, I did find one instruction that was from an
ownership forwarding subclass but was not marked as forwarding:
DifferentiableFunctionInst. With this change, we can no longer by mistake have
such errors enter the code base.
This patch moves state from the main SemanticARCOptVisitor struct to instead be
on a context object. Sub-transformations should not need to know about the
visitor since how it processes things is orthogonal from the transformations
themselves.
This is so I can move individual sub-optimizations on SemanticARCOptVisitor into
their own files. So for instance, there will be one file containing the load
[copy] optimization and another containing the phi optimization, etc.