Commit Graph

105 Commits

Author SHA1 Message Date
Nate Chandler
f0bd5839a2 [OwnershipUtils] Extracted utility for common use.
Promoted isRedundantLexicalBeginBorrow function to OwnershipUtils so
that it can be used in more than just SemanticARCOpts.
2021-12-07 17:51:18 -08:00
Nate Chandler
cde250a3e3 [CopyPropagation] Add ShrinkBorrowScope.
During copy propagation (for which -enable-copy-propagation must still
be passed), also try to shrink borrow scopes by hoisting end_borrows
using the newly added ShrinkBorrowScope utility.

Allow end_borrow instructions to be hoisted over instructions that are
not deinit barriers for the value which is borrowed.  Deinit barriers
include uses of the value, loads of memory, loads of weak references
that may be zeroed during deinit, and "synchronization points".

rdar://79149830
2021-12-07 09:43:57 -08:00
Andrew Trick
b4a47b6463 Fix findInnerTransitiveUsesForAddress; add AddressUseKind
findInnerTransitiveUsesForAddress was incorrectly returning true for
pointer escapes.

Introduce enum AddressUseKind { NonEscaping, PointerEscape, Unknown };

Clients need to handle each of these cases differently.
2021-10-12 19:58:11 -07:00
Andrew Trick
a336bcdea9 Fix findTransitiveUses to only record leaf uses.
Recording uses is now optional. This utility is also used simply to
check for PointerEscape. When uses are recorded, they are used to
extend a live range. There could be a large number of transitive uses
that we don't want to pass back to the caller.
2021-10-12 19:58:10 -07:00
Andrew Trick
767e094cfe Add AddressOwnership OSSA utility
This replaces the recent BorrowedAddress utility (the address may not
be borrowed!)

APIs:

AddressOwnership::hasLocalOwnershipLifetime() - convenience on top of
AccessBase::hasLocalOwnershipLifetime().

AddressOwnership::getOwnershipReferenceRoot() - convenience on top of
AccessBase::getOwnershipReferenceRoot().

AddressOwnership::findTransitiveUses() - wrapper API over the internal
helper findTransitiveUsesForAddress()

AddressOwnership::areUsesWithinLifetime() - wrapper adds a quick check
on top of BorrowedValue::areUsesWithinLifetime()
2021-10-07 15:34:09 -07:00
Andrew Trick
b1352b490b Rename visitExtendedLocalScopeEndingUses
To visitExtendedScopeEndingUses.

We need to talk about the "local" scope, which does not cross
reborrows, in contrast to the extended scope. So the name was a
contradiction that could cause confusion elsewhere.
2021-10-07 14:32:40 -07:00
Andrew Trick
ac2f7b75ec Add BorrowingOperand::hasEmptyRequiredEndingUses()
Temporary check for malformed borrow scopes. Def-use traversals expect
at least one scope-ending use. Otherwise we can end up missing the
borrow entirely and miscompiling. SIL should guarantee scope-ending
uses on all paths, but that isn't fully enforced yet.
2021-10-07 14:32:32 -07:00
Andrew Trick
8996fb8af2 Add functionality to the OSSA BorrowedValue utility.
This abstraction is heavily used to ask about the current borrow scope
enclosing some uses. Give it the functionality it needs.

Add BorrowedValue::computeLiveness(PrunedLiveness). Get the borrow
scope's live range. A trivial 4-line implementation.

Cleanup BorrowedValue::areUsesWithinScope(). Quick check to see if a set
of uses known to be dominated by the borrow are within the borrow scope.

Add BorrowedValue::hasReborrow(). Is this local borrow scope
reborrowed? If not, then it's local scope-ending operations are the
end of its required lifetime.

Simplify BorrowedValue::gatherReborrows() to use OperandOwnership::Reborrow.
2021-10-06 15:48:08 -07:00
Andrew Trick
70a14ff61b Rename ForwardingOperand.getOwnershipKind()
to getForwardingOwnershipKind() just to distinguish it from
SILValue.getOwnershipKind() and be able to grep for them each.
2021-10-06 15:48:08 -07:00
Andrew Trick
665505b811 Comment: InteriorPointerOperand needs to handle project_box 2021-10-03 19:49:58 -07:00
Andrew Trick
a6cb54511a Add a tiny BorrowedAddress utility.
Determines whether an address might be inside a borrowed scope. If so,
then any address substitution needs to find that scope boundary to
avoid violating its basic guarantee that all uses are within scope.
2021-08-09 11:43:41 -07:00
Andrew Trick
88df7833af Add InteriorPointer::ProjectBox.
InteriorPointer needs to handle all access base kinds that may be borrowed.
2021-08-07 15:26:47 -07:00
Andrew Trick
573df892e7 Fix BorrowingOperand more.
Don't allow an owned call argument to be considered a valid BorrowingOperand.

More generally, make sure there is a perfect equivalence between valid
BorrowingOperand and the corresponding OperandOwnership kind.
2021-06-30 15:20:19 -07:00
Andrew Trick
87ba9ccea6 Fix BorrowingOperand for branches.
An owned phi is obviously not a borrowing operand.
2021-06-28 21:48:00 -07:00
Saleem Abdulrasool
25f437e17d mark some switches as covered (NFCI)
Unfortunately, MSVC does not detect covered switches as clang.  Mark
some of the switches as covered to avoid an unnecessary warning from
MSVC.
2021-06-05 15:30:25 -07:00
Erik Eckstein
4affa11604 OwnershipUtils: let visitTransitiveEndBorrows work with a BorrowedValue.
... and not only with a begin_borrow
2021-05-18 08:56:22 +02:00
Erik Eckstein
09755659a1 SIL: remove the sub-classes of MultipleValueInstructionResult
They are not really needed, because they don't contain any stored properties and for isa-checks we can check the parent instruction.
2021-04-11 19:14:34 +02:00
Andrew Trick
4e048a85a6 Expose findInnerTransitiveGuaranteedUses 2021-03-18 00:14:13 -07:00
Michael Gottesman
421addacce [ownership] Change store_borrow to be an interior pointer and update interior pointer formalism to use it.
For those who are unaware, a store_borrow is an instruction that is needed so
that we can without adding ARC traffic materialize a guaranteed object value
into memory so we can pass it as an in_guaranteed argument. It has not had its
model completely implemented due to time. I am going to add some information
about it in this commit message for others.

From a model semantic perspective, a store_borrow is meant to allow for a
guaranteed value to be materialized into memory safely for a scoped region of
time and be able to guarantee that:

1. The guaranteed value that was stored is always live.
2. The memory is never written to within that region.

The natural way to model this type of guaranteeing behavior from an object to an
address is via a safe interior pointer formulation and thus with that in mind I
made it so that store_borrow returned an address that was an interior pointer of
the guaranteed value. Sadly, we have not changed the compiler yet to use this
effectively since we in store_borrow code paths generally just use the input
address. That being said, in this PR I begin to move us toward this world by
making store_borrow's src operand an InteriorPointerOperand. This means that we
will require the borrowed value to be alive at all use points of the
store_borrow result. That will not have a large effect today but I am going to
loop back around and extend that.

There is additional work here that I wish to accomplish in the future is that I
would like to define an end_store_borrow instruction to explicitly end the scope
in which there is a condition on the borrow. Then we would require that the
memory we are using to for sure never be written to in that region and maybe
additionally try to give some sort of guarantee around exclusivity (consider if
we made it so that we could only store_borrow into an alloc_stack with certain
conditions). Not sure about the last one yet. But philosophically since we are
using this to just handle reabstraction suggests we can do something more
constrained.
2021-02-08 10:53:56 -08:00
Meghana Gupta
10492c9efc Merge pull request #35718 from meg-gupta/dceossafixespr
Fixes for OSSA DCE
2021-02-04 15:16:13 -08:00
Meghana Gupta
a78acfe4b0 Disable handling of nested borrows in DCE
Nested borrow handling can be complex in the presence of reborrows. So
it is not handled currently.
2021-02-03 11:38:40 -08:00
Meghana Gupta
9dd3ce5545 Add a new utility findTransitiveReborrowBaseValuePairs
This visits unique pairs of reborrowPhi and its base value.
The code for the utility is mostly refactored from the ReborrowVerifier.

This utility will be used later in DCE.
2021-02-03 11:38:03 -08:00
Michael Gottesman
ed368ab7d9 [sil-combine] Update COWBufferForReading for OSSA.
I was able to use the interior pointer operand API to great effect here.
2021-02-02 15:54:44 -08:00
Michael Gottesman
6684934702 [ownership] Change BorrowedValue::visitInteriorPointerOperand to pass IntPtrOperand as a value, not a const ref.
1. The reason to do this is that an InteriorPointerOperand is a value type.
2. Sometimes one wants to be able to use the IntPtrOperand that is not a const &
   and the value type formulation always works.

Functionality wise, this is NFCI.
2021-02-02 15:54:44 -08:00
Andrew Trick
2095c0cf04 OSSA: Use a public explicit constructor for BorrowedValue. 2021-02-01 18:03:24 -08:00
Andrew Trick
caefb9afaa Centralize and document low-level OSSA utilities
...for handling borrow scopes:

- find[Extended]TransitiveGuaranteedUses

- BorrowingOperand::visit[Extended]ScopeEndingUses

- BorrowedValue BorrowingOperand::getBorrowIntroducingUserResult()

And document the logic.

Mostly NFC in this commit, but more RAUW cases should be correctly
handled now.

Particularly, ensure that we can cleanly handle all manner of
reborrows. This is necessary to ensure both CanonicalizeOSSA and
replace-all-uses higher-level utilities handle all cases.

This generalizes some of the logic in CanonicalizeOSSA so it can be
shared by other high-level OSSA utilities.

These utilities extend the fundamental BorrowingOperand and
BorrowedValue functionality that has been designed recently. It builds
on and replaces a mix of piece-meal functionality that was needed
during bootstrapping. These APIs are now consistent with the more
recently designed code. It should be obvious what they mean and how to
use them, should be very hard to use them incorrectly, and should be
as efficient as possible, since they're fundamental to other
higher-level utilities.

Most importantly, there were several very subtle correctness issues
that were not handled cleanly in a centralized way. There are now a
mix of higher-level utilities trying to use this underlying
functionality, but it was hard to tell if all those higher-level
utilities were covering all the subtle cases:

- checking for PointerEscapes everywhere we need to

- the differences between uses of borrow introducers and other
  guaranteed values

- handling the uses of nested borrow scopes

- transitively handling reborrows

- the difference between nested and top-level reborrows

- forwarding destructures (which can cause exponential explosion)

In short, I was fundamentally confused and getting things wrong before
designing these utilities.
2021-02-01 18:03:24 -08:00
Michael Gottesman
dbd66d8e2d [ownership] Change BorrowingOperand to no longer be convertable to Operand * and provide a conversion to bool instead.
A few notes:

1. We already have provide an operator* that does this and even better makes the
   conversion explicit in the source.

2. The bool operator checks both that kind is not invalid and that the operand
   is non-null. This ensures we can use an `if` to properly check for a valid
   BorrowingOperand.
2021-01-28 12:10:16 -08:00
Erik Eckstein
ddd0f4d3a9 SIL: replace SmallPtrSet<SILBasicBlock *> with BasicBlockSet in LinearLifetimeChecker
This makes it unnecessary to share a single instance of visited blocks across multiple calls the the checker.
2021-01-27 10:31:17 +01:00
Meghana Gupta
3e330abf44 Fix use-after-free in ownership rauw
Instead of saving BorrowingOperand on the context save the SILBasicBlock
and index of the terminator operand.
This avoids the use-after-free in eliminateReborrowsOfRecursiveBorrows.
Previously, eliminateReborrowsOfRecursiveBorrows called helper
insertOwnedBaseValueAlongBranchEdge, which can delete a branch
instruction (reborrow) that could have been cached in
recursiveReborrows.
2021-01-26 20:21:16 -08:00
Andrew Trick
a2fac95f9f OSSA ownership optimization RAUW utility fixes.
Verify that the OwnershipRAUWUtility always preserves the original
borrow scope by exhaustively switching over OperandOwnership.

And related cleanup.
2021-01-23 18:15:58 -08:00
Erik Eckstein
2c7027da97 SIL: fix a header-file layering violation: SILArgument.h should not include SILFunction.h
"Containers" should include their "content" and not the other way round.
Also, remove some unused stuff of SILArgument.
2021-01-22 10:05:09 +01:00
Michael Gottesman
2243ffefa4 Merge pull request #35461 from gottesmm/ossa-interior-ptr-fixup
[ownership] Implement Interior Pointer handling API for RAUWing addresses
2021-01-20 09:53:48 -08:00
Andrew Trick
f509d67e7c Fix -canonical-ossa-rewrite-borrows but leave it disabled.
Add support for interleaved borrow scopes:

%b1 = begin_borrow %a
%c = copy
%b2 = begin_borrow %b1
end_borrow %b1
use %c
end_borrow %b2

Will be transformed to:

%c = copy %a
%b1 = begin_borrow %a
%b2 = begin_borrow %b1
end_borrow %b1
use %c
end_borrow %b2

This was the original intention but the implementation was incomplete.

This option can be enabled as soon as we need it for performance.

The intention is also to handle multi-block borrows, but that hasn't
been implemented.
2021-01-19 19:07:54 -08:00
Michael Gottesman
73ba521e56 [ownership] Add a new API to OwnershipFixupContext::replaceAllAddressUsesFixingInteriorPointerOwnership.
In OSSA, we enforce that addresses from interior pointer instructions are scoped
within a borrow scope. This means that it is invalid to use such an address
outside of its parent borrow scope and as a result one can not just RAUW an
address value by a dominating address value since the latter may be invalid at
the former. I foresee that I am going to have to solve this problem and so I
decided to write this API to handle the vast majority of cases.

The way this API works is that it:

1. Computes an access path with base for the new value. If we do not have a base
value and a valid access path with root, we bail.

2. Then we check if our base value is the result of an interior pointer
instruction. If it isn't, we are immediately done and can RAUW without further
delay.

3. If we do have an interior pointer instruction, we see if the immediate
guaranteed value we projected from has a single borrow introducer value. If not,
we bail. I think this is reasonable since with time, all guaranteed values will
always only have a single borrow introducing value (once struct, tuple,
destructure_struct, destructure_tuple become reborrows).

4. Then we gather up all inner uses of our access path. If for some reason that
fails, we bail.

5. Then we see if all of those uses are within our borrow scope. If so, we can
RAUW without any further worry.

6. Otherwise, we perform a copy+borrow of our interior pointer's operand value
at the interior pointer, create a copy of the interior pointer instruction upon
this new borrow and then RAUW oldValue with that instead. By construction all
uses of oldValue will be within this new interior pointer scope.
2021-01-17 20:08:24 -08:00
Michael Gottesman
2ae43f97af [ownership] Eliminate Optional return value APIs from OwnershipUtils in favor of an Invalid enum case.
Should be NFC.
2021-01-14 11:02:10 -08:00
Michael Gottesman
cd75a68ec0 [ownership] Fix a small thinko I made when changing InteriorPointerOperandKind to use an Invalid case instead of an Optional. 2021-01-13 11:48:29 -08:00
Michael Gottesman
c103503061 [ownership] Add a new helper InteriorPointerOperand::getSingleBaseValue().
This returns the underlying BorrowedValue that introduce's the interior pointer
operand's associated value.
2021-01-13 10:43:41 -08:00
Michael Gottesman
a1a133f865 [ownership] Add helper methods to InteriorPointerOperand that let you easily use its internal operand.
Specifically, I added a '*' and a '->' operator so one can cast explicitly using
'*' and access Operand methods using '->'.
2021-01-13 10:43:41 -08:00
Michael Gottesman
839c389147 [ownership] Add a helper API on InteriorPointerOperand that lets you infer the InteriorPointerOperand from the interior pointer operand's user instruction's result.
Useful when needing to walk use->def and get this info.
2021-01-13 10:43:41 -08:00
Michael Gottesman
d505686e6d [ownership] Add some helpers to BorrowedValue that lets you use it easily as a value.
Specifically I provided a '->' and a '*' operator. So now you can do:

```
borrowedValue->callSILValueMethod();
```

and explicitly cast to SILValue using '*'.
2021-01-13 10:43:41 -08:00
Michael Gottesman
31c9d769f9 [ownership] Make ForwardingOperand truly a loose wrapper around an operand.
This implies making -> and * return an Operand * instead of an
OwnershipForwardingInst. So one can thus do:

ForwardingOperand op;
op.myForwardingOperandMethod();
op->myOperandMethod();
2021-01-13 10:43:41 -08:00
Michael Gottesman
368f8acc4b [sil] Eliminate a confusing optional method result by changing type dependent operands to have OwnershipKind::None instead of returning Optional::None from Operand::getOperandOwnership().
This eliminates when talking about this API an ambiguity in between
Optional::None (the C++ ADT) and OwnershipKind::None (the ownership kind).
2020-12-18 19:33:27 -08:00
Andrew Trick
b1dba2554e Introduce OperandOwnership to classify OSSA uses.
Migrating to this classification was made easy by the recent rewrite
of the OSSA constraint model. It's also consistent with
instruction-level abstractions for working with different kinds of
OperandOwnership that are being designed.

This classification vastly simplifies OSSA passes that rewrite OSSA
live ranges, making it straightforward to reason about completeness
and correctness. It will allow a simple utility to canonicalize OSSA
live ranges on-the-fly.

This avoids the need for OSSA-based utilities and passes to hard-code
SIL opcodes. This will allow several of those unmaintainable pieces of
code to be replaced with a trivial OperandOwnership check.

It's extremely important for SIL maintainers to see a list of all SIL
opcodes associated with a simple OSSA classification and set of
well-specified rules for each opcode class, without needing to guess
or reverse-engineer the meaning from the implementation. This
classification does that while eliminating a pile of unreadable
macros.

This classification system is the model that CopyPropagation was
initially designed to use. Now, rather than relying on a separate
pass, a simple, lightweight utility will canonicalize OSSA
live ranges.

The major problem with writing optimizations based on OperandOwnership
is that some operations don't follow structural OSSA requirements,
such as project_box and unchecked_ownership_conversion. Those are
classified as PointerEscape which prevents the compiler from reasoning
about, or rewriting the OSSA live range.

Functional Changes:

As a side effect, this corrects many operand constraints that should
in fact require trivial operand values.
2020-12-16 01:58:53 -08:00
Michael Gottesman
259d2bb182 [ownership] Commit a generic replaceAllUsesAndEraseFixingOwnership api and enable SimplifyInstruction on OSSA.
This is a generic API that when ownership is enabled allows one to replace all
uses of a value with a value with a differing ownership by transforming/lifetime
extending as appropriate.

This API supports all pairings of ownership /except/ replacing a value with
OwnershipKind::None with a value without OwnershipKind::None. This is a more
complex optimization that we do not support today. As a result, we include on
our state struct a helper routine that callers can use to know if the two values
that they want to process can be handled by the algorithm.

My moticiation is to use this to to update InstSimplify and SILCombiner in a
less bug prone way rather than just turn stuff off.

Noting that this transformation inserts ownership instructions, I have made sure
to test this API in two ways:

1. With Mandatory Combiner alone (to make sure it works period).

2. With Mandatory Combiner + Semantic ARC Opts to make sure that we can
   eliminate the extra ownership instructions it inserts.

As one can see from the tests, the optimizer today is able to handle all of
these transforms except one conditional case where I need to eliminate a dead
phi arg. I have a separate branch that hits that today but I have exposed unsafe
behavior in ClosureLifetimeFixup that I need to fix first before I can land
that. I don't want that to stop this PR since I think the current low level ARC
optimizer may be able to help me here since this is a simple transform it does
all of the time.
2020-12-09 11:53:56 -08:00
Michael Gottesman
09ae2ef071 [ownership] Centralize all info about SILInstruction forwarding in the SILInstruction class hierarchy itself.
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.
2020-12-01 17:36:19 -08:00
Michael Gottesman
85965ed202 [semantic-arc] Optimize more lifetime joining when a copy_value, destroy_value are in the same block.
Specifically the optimization that is being performed here is the elimination of
lifetimes that hand off ownership in between two regions of code. Example:

```
%1 = copy_value %0
...
destroy_value %0
...
apply %consumingUser(%1)
```

We really want to handle this case since it is a natural thing that comes up in
programs and will let me eliminate the *evil* usage of emitDestroyValueOperation
in TypeLowering without needing to modify huge amounts of tests.
2020-11-25 21:57:24 -08:00
Michael Gottesman
63e43b6b2f [ownership] Rename BorrowingOperand::visitLocalEndScope{Instruction,Use}s(Operand *)
This originally trafficked in Instructions and for some reason the name was
never changed.

I also changed the result type to be a bool and added the ability for the passed
in closure to signal failure (and iteration stop) by returning false. This also
makes it possible to use visitLocalEndScopeUses in if statements which can be
useful.
2020-11-25 21:54:47 -08:00
Michael Gottesman
c35ff33a1f [ownership] Add a subclass for all OwnershipForwarding instructions and fix the classof to the various ownership abstract classes so isa/dyn_cast work.
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.
2020-11-18 12:34:18 -08:00
Michael Gottesman
a718e7d156 [ownership] Centralize the concept of isReborrow on BorrowingOperand.
A reborrow occurs when a Borrowing Operand ends the lifetime of a borrowed value
and propagates forward a new guaranteed value that continues the guaranteed
lifetime of the value.
2020-11-15 16:41:54 -08:00
Michael Gottesman
a294ab61ad [ownership] Eliminate OperandOwnershipKindMap in favor of OwnershipConstraint.
I also used this as a moment to clarify the lattice that related
ValueOwnershipKind and OwnershipConstraint.
2020-11-10 19:07:30 -08:00