Commit Graph

47 Commits

Author SHA1 Message Date
Michael Gottesman
e9a3c7f26b Merge pull request #30497 from gottesmm/pr-71c57ac72c9ecc07f75dd86474bf57988d7aa006
[ownership] Convert a few small visitor methods to be const.
2020-03-22 16:18:50 -07:00
Michael Gottesman
8682002244 [ownership] Rename BorrowScopeOperandKind -> BorrowingOperandKind.
I missed this when I was doing the original refactoring. This was again done
using Xcode's refactoring functionality!
2020-03-18 23:05:40 -07:00
Michael Gottesman
e320ee1cd8 [ownership] Convert a few small visitor methods to be const. 2020-03-18 22:59:00 -07:00
Michael Gottesman
47e74473f1 [ownership] Rename BorrowScopeIntroducingValue -> BorrowedValue and BorrowScopeOperand -> BorrowingOperand.
Andy and I for some time have been discussing the right name for these two
"ownership concepts". What we realized is that the "ing" on
BorrowScopeIntroducingValue is very unfortunate since this value is the result
of a new borrow scope being introduced. So the name should be really:
BorrowScopeIntroducedValue. Given that is sort of unsatisfying, we settled on
the name BorrowedValue.

Once we found the name BorrowedValue, we naturally realized that
BorrowScopeOperand -> BorrowingOperand followed. This is because the operand is
the operand of the instruction that is creating the new borrow scope. So in a
sense the Operand is the "Use" that causes the original value to become
borrowed. So a BorrowingOperand is where the action is and is "active".
2020-03-18 19:43:30 -07:00
Michael Gottesman
f3df836652 [ownership] Treat open_existential_box as an interior pointer producting instruction.
This means that it can only have a guaranteed object as an operandand that we
validate that all uses of the result address of open_existential_box occur only
within the lifetime of said object's borrow scope.
2020-03-15 23:21:45 -07:00
Michael Gottesman
6b88599412 Revert "Merge pull request #30327 from rintaro/revert-30289"
This reverts commit 0a6ccd802f, reversing
changes made to 7c5d74e86b.
2020-03-10 16:52:58 -07:00
Rintaro Ishizaki
fad1b431c2 Revert "[semantic-arc-opts] Implement @owned phi web elimination for phi webs with a single phi node that only have copy_value introducers."
This reverts commit 6fee59bd6a.
2020-03-10 08:43:46 -07:00
Michael Gottesman
6fee59bd6a [semantic-arc-opts] Implement @owned phi web elimination for phi webs with a single phi node that only have copy_value introducers.
This is the most simple initial version that I can commit. The hope is that this will help to bring this up in a nice way.

I am going to handle the multiple phi node and load [copy] case later to reduce
code churn.

<rdar://problem/56720436>
2020-03-09 16:04:48 -07:00
Michael Gottesman
491f826263 [ownership] Implement getSingleOwnedValueIntroducer and getAllOwnedValueIntroducers().
These enable robust walking up the def-use ossa graph to find the value that
introduces an independent underlying owned value, looking through forwarding
insts.
2020-03-06 12:01:13 -08:00
Michael Gottesman
9ab0427528 [ownership] Add a new construct: OwnedValueIntroducer for dealing with non-forwarding owned values at a higher level.
This is a similar concept to "rc-identity" that we talk about in the low level
ARC optimizer. The main idea is that all owned values in a program can be
partitioned into two different kinds of values:

1. Introducer values that exist independently of any other local values in the
function. It is a point of truth from which the owned objects lifetime extends
from and is in a certain sense an initialization (in a category theoretic sense)
in the lifetime of the underlying object that we are manipulating.

2. Forwarding values do not represent an object lifetime that is "truly"
independent of any other value locally: its liveness comes from passing on
liveness from some introducer or some other forwarding value.

The reason why I am adding this new construct is that I am beginning to
implement a new form of ARC optimization that enables us to convert @owned SIL
phi arguments to @guaranteed phi arguments. As part of that, I need to have a
way to in a systematic way finding the underlying incoming values (using the
logic used to determine forwarding in the ownership verifier).

This is the first part of that effort, defining the ontology we are going to
work with. Keep in mind this is just a seed ontology, if I missed any "owned
introducers" (I am sure I did), feel free to add them.
2020-03-06 12:01:13 -08:00
Michael Gottesman
0aec5a7f43 [ownership] Implement InteriorPointer abstraction/validate current recognized ones addresses are not used outside of parent object's borrowed lifetime.
Currently, we only classify ref_element_addr and ref_tail_addr. But we should
expand this to project_box, project_existential_box and open_existential_box as
well.
2020-03-05 20:28:59 -08:00
Michael Gottesman
be16822af9 [ownership] Remove BranchPropagatedUser.
The only reason why BranchPropagatedUser existed was because early on in SIL, we
weren't sure if cond_br should be able to handle non-trivial values in
ossa. Now, we have reached the point where we have enough experience to make the
judgement that it is not worth having in the representation due to it not
holding its weight.

Now that in ToT we have banned cond_br from having non-trivial operands in ossa,
I can just eliminate BranchPropagatedUser and replace it with the operands that
we used to construct them!

A few notes:

1. Part of my motiviation in doing this is that I want to change LiveRange to
store operands instead of instructions. This is because we are interested in
being able to understand the LiveRange at a use granularity in cases where we
have multiple operands. While doing this, I discovered that I needed
SILInstructions to use the Linear Lifetime Checker. Then I realized that now was
the time to just unwind BranchPropagatedUser.

2. In certain places in SemanticARCOpts, I had to do add some extra copies to
transform arrays of instructions from LiveRange into their operand form. I am
going to remove them in a subsequent commit when I change LiveRange to work on
operands. I am doing this split to be incremental.

3. I changed isSingleInitAllocStack to have an out array of Operand *. The only
user of this code is today in SemanticARCOpts and this information is fed to the
Linear Lifetime Checker, so I needed to do it.
2020-03-04 07:35:23 -08:00
Michael Gottesman
847ad07847 [ownership] Move LinearLifetimeChecker from OwnershipUtils.h -> LinearLifetimeChecker.h
OwnershipUtils.h is growing a bit and I want to use it to store abstract higher
level utilities for working with ossa. LinearLifetimeChecker is just a low level
detail of that, so it makes sense to move it out now.
2020-03-03 16:15:25 -08:00
Michael Gottesman
ef309b8830 Merge pull request #30179 from gottesmm/pr-413b415c765eb62debe5d09415f22e89952479af
[ownership] Clean up BorrowScopeIntroducingValue.
2020-03-03 13:21:38 -08:00
Michael Gottesman
6fe7ad2676 [ownership] Clean up BorrowScopeIntroducingValue.
Specifically, I got rid of the constructors for BorrowScopeIntroducingValue and
made it so one can only construct BorrowScopeIntroducingValue from the static
constructor.

I also changed the place where we mapped values from BorrowScopeIntroducingValue
-> BorrowScopeIntroducingValueKind. This simplifies the whole thing.
2020-03-02 22:24:02 -08:00
Michael Gottesman
962b4ed633 [semantic-arc] Change StorageGuaranteesLoadVisitor::visitClassAccess to find borrow scope introducers using utilities from OwnershipUtils.h.
I added a new API into OwnershipUtils called
getSingleBorrowIntroducingValue. This API returns a single
BorrowScopeIntroducingValue for a passed in guaranteed value. If we can not find
such a BorrowScopeIntroducingValue or we find multiple such, we return None.

Using that, I refactored StorageGuaranteesLoadVisitor::visitClassAccess(...) to
use this new API which should make it significantly more robust since the
routine uses the definitions of "guaranteed forwarding" that the ownership
verifier uses when it verifies meaning that we can rely on the routine to be
exhaustive and correct. This means that we now promote load [copy] ->
load_borrow even if our borrow scope introducer feeds through a switch_enum or
checked_cast_br result (the main reason I looked into this change).

To create getSingleBorrowIntroducingValue, I refactored
getUnderlyingBorrowIntroucingValues to use a generator to find all of its
underlying values. Then in getSingleBorrowIntroducingValue, I just made it so
that we call the generator 1-2 times (as appropriate) to implement this query.
2020-03-02 00:35:41 -08:00
Michael Gottesman
b58ea4bb37 Merge pull request #30151 from gottesmm/pr-5d7a1ea2d4ae11d647fceb405a4c2307150f7b06
[ownership] Create a single introducer version of getUnderlyingBorrowIntroducingValues and rename it to getAllBorrowIntroducingValues(...).
2020-03-02 00:29:34 -08:00
Michael Gottesman
bdf6143df5 [ownership] Add simple support for concatenating together simple live ranges that do not need LiveRange analysis.
Specifically, this PR adds support for optimizing simple cases where we do not
need to compute LiveRanges with the idea of first doing simple transforms that
involve small numbers of instructions first. With that in mind, we only optimize
cases where our copy_value has a single consuming user and our owned value has a
single destroy_value. To understand the transform here, consider the following
SIL:

```
  %0 = ...
  %1 = copy_value %0                 (1)
  apply %guaranteedUser(%0)          (2)
  destroy_value %0                   (3)
  apply %cviConsumer(%1)             (4)
```

We want to eliminate (2) and (3), effectively joining the lifetimes of %0 and
%1, transforming the code to:

```
  %0 = ...
  apply %guaranteedUser(%0)          (2)
  apply %cviConsumer(%0)             (4)
```

Easily, we can always do this transform in this case since we know that %0's
lifetime ends strictly before the end of %1's due to (3) being before (4). This
means that any uses that require liveness of %0 must be before (4) and thus no
use-after-frees can result from removing (3) since we are not shrinking the
underlying object's lifetime. Lets consider a different case where (3) and (4)
are swapped.

```
  %0 = ...
  %1 = copy_value %0                 (1)
  apply %guaranteedUser(%0)          (2)
  apply %cviConsumer(%1)             (4)
  destroy_value %0                   (3)
```

In this case, since there aren't any liveness requiring uses of %0 in between
(4) and (3), we can still perform our transform. But what if there was a
liveness requiring user in between (4) and (3). To analyze this, lets swap (2)
and (4), yielding:

```
  %0 = ...
  %1 = copy_value %0                 (1)
  apply %cviConsumer(%1)             (4)
  apply %guaranteedUser(%0)          (2)
  destroy_value %0                   (3)
```

In this case, if we were to perform our transform, we would get a use-after-free
due do the transform shrinking the lifetime of the underlying object here from
ending at (3) to ending at (4):

```
  %0 = ...
  apply %cviConsumer(%1)             (4)
  apply %guaranteedUser(%0)          (2) // *kaboom*
```

So clearly, if (3) is after (4), clearly, we need to know that there aren't any
liveness requiring uses in between them to be able to perform this
optimization. But is this enough? Turns out no. There are two further issues
that we must consider:

1. If (4) is forwards owned ownership, it is not truly "consuming" the
underlying value in the sense of actually destroying the underlying value. This
can be worked with by using the LiveRange abstraction. That being said, this PR
is meant to contain simple transforms that do not need to use LiveRange. So, we
bail if we see a forwarding instruction.

2. At the current time, we may not be able to find all normal uses since all of
the instructions that are interior pointer constructs (e.x.: project_box) have
not been required yet to always be guarded by borrows (the eventual end
state). Thus we can not shrink lifetimes in general safely until that piece of
work is done.

Given all of those constraints, we only handle cases here where (3) is strictly
before (4) so we know 100% we are not shrinking any lifetimes. This effectively
is causing our correctness to rely on SILGen properly scoping lifetimes. Once
all interior pointers are properly guarded, we will be able to be more
aggressive here.

With that in mind, we perform this transform given the following conditions
noting that this pattern often times comes up around return values:

1. If the consuming user is a return inst. In such a case, we know that the
   destroy_value must be before the actual return inst.

2. If the consuming user is in the exit block and the destroy_value is not.

3. If the consuming user and destroy_value are in the same block and the
   consuming user is strictly later in that block than the destroy_value.

In all of these cases, we are able to optimize without the need for LiveRanges.
I am going to add support for this in a subsequent commit.
2020-03-01 19:59:54 -08:00
Michael Gottesman
6e5f036bda [ownership] Create a single introducer version of getUnderlyingBorrowIntroducingValues and rename it to getAllBorrowIntroducingValues(...).
I also added a comment to getAllBorrowIntroducingValues(...) that explained the
situations where one could have multiple borrow introducing values:

1. True phi arguments.
2. Aggregate forming instructions.
2020-03-01 17:30:53 -08:00
Michael Gottesman
1fb56db502 [ownership] Implement movable guaranteed scopes in ossa.
This patch implements movable guaranteed scopes in ossa. This pattern is
currently not generated anywhere in the compiler, but my hope is to begin
emitting these in SemanticARCOpts. The idea is that these model true phi nodes
and thus can be used to fuse multiple guaranteed scopes into one using br
instructions. This is treated similarly to how owned instructions are forwarded
through /all/ terminators. This will enable us to use the SILSSAUpdater with
guaranteed arguments as well as enable the expression of sets of borrow scopes
that minimally jointly-dominate a guaranteed argument. This will enable us to
express +0 merge points like the following:

```
bb1:
  %0a = begin_borrow %0 : $Klass
  br bb3(%0a : $Klass)

bb2:
  %1a = load_borrow %1 : $*Klass
  br bb3(%1a : $Klass)

bb3(%2 : @guaranteed $Klass)
  ...
  end_borrow %2 : $Klass
```

I describe below what the semantics of guaranteed block arguments were
previously, what they are now, and a little bit of interesting things from a
semantic perspective around implicit sub-scope users.

Before this patch in ossa, guaranteed block arguments had two different sets of
semantics:

1. Given a checked_cast_br or a switch_enum, the guaranteed block argument was
   treated like a forwarding instruction. As such, the guaranteed argument's did
   not require an end_borrow and its uses were validated as part of the use list
   of the switch_enum/checked_cast_br operand's borrow introducer. It also was
   not classified as a BorrowScopeValueIntroducer since it was not introducing a
   new scope.

2. Given any other predecessor terminator, we treated the guaranteed argument as
   a complete sub-scope of its incoming values. Thus we required the guaranteed
   argument to have its lifetime eneded by an end_borrow and that all incoming
   values of the guaranteed argument to come from a borrow introducer whose set
   of jointly post-dominating end_borrows also jointly post-dominates the set of
   end_borrows associated with the guaranteed argument itself. Consider the
   following example:

```
bb0:
  %1 = begin_borrow %foo : $Foo   // (1)
  %2 = begin_borrow %foo2 : $Foo2 // (2)
  cond_br ..., bb1, bb2

bb1:
  br bb3(%1 : $Foo)

bb2:
  br bb3(%2 : $Foo)

bb3(%3 : @guaranteed $Foo)
  ...
  end_borrow %3 : $Foo            // (3)
  end_borrow %2 : $Foo            // (4)
  end_borrow %1 : $Foo            // (5)
  ...
```

Notice how due to SSA, (1) and (2) must dominate (4) and (5) and thus must
dominate bb3, preventing the borrows from existing within bb1, bb2.

This dominance property is actively harmful to expressivity in SIL since it
means that guaranteed arguments can not be used to express (without contortion)
sil code patterns where an argument is jointly-dominated by a minimal set of
guaranteed incoming values. For instance, consider the following SIL example:

```
bb0:
  cond_br ..., bb1, bb2

bb1:
  %0 = load [copy] %globalAddr : $Foo
  br bb3(%0 : $Foo)

bb2:
  %1 = copy_value %guaranteedFunctionArg : $Foo
  br bb3(%1 : $Foo):

bb3(%2 : @owned $Foo):
  apply %useFoo(%2)
  destroy_value %2 : $Foo
```

As a quick proof: Assume the previous rules for guaranteed arguments. Then to
promote the load [copy] -> load_borrow and the copy_value to a begin_borrow, we
would need to place an end_borrow in bb3. But neither bb1 or bb2 dominates bb3,
so we would violate SSA dominance rules.

To enable SIL to express this pattern, we introduce a third rule for terminator
in ossa that applies only to branch insts. All other branches that obeyed the
previous rules (cond_br), still follow the old rule. This is not on purpose, I
am just being incremental and changing things as I need to. Specifically,
guaranteed arguments whose incoming values are defined by branch instructions
now act as a move on guaranteed values. The intuition here is that these
arguments are acting as true phis in an SSA sense and thus are just new names
for the incoming values. This implies since it is just a new name (not a
semantic change) that the guaranteed incoming value's guaranteed scopes should
be fused into one scope. The natural way to model this is by treating branch
insts as consuming guaranteed values. This then lets us express the example
above without using copies as follows:

```
bb0:
  cond_br ..., bb1, bb2

bb1:
  %0 = load_borrow %globalAddr : $Foo
  br bb3(%0 : $Foo) // consumes %0 and acts as %0's end_borrow.

bb2:
  // We need to introduce a new begin_borrow here since function
  // arguments are required to never be consumed.
  %1 = begin_borrow %guaranteedFunctionArg : $Foo
  br bb3(%1 : $Foo) // consumes %1 and acts as %1's end_borrow

// %2 continues the guaranteed scope of %0, %1. This time fused with one name.
bb3(%2 : @guaranteed $Foo):
  apply %useFoo(%2)
  // End the lifetime of %2 (which implicitly ends the lifetime of %0, %1).
  end_borrow %2 : $Foo
  ...
```

The main complication for users is that now when attempting to discover the set
of implicit users on an owned or guaranteed value caused by their usage as an
argument of a borrow introducer like begin_borrow. For those who are unaware, a
begin_borrow places an implicit requirement on its parent value that the parent
value is alive for the entire part of the CFG where this begin_borrow is
live. Previously, one could just look for the end_borrows of the
begin_borrow. Now one must additionally look for consuming branch insts. This is
because the original value that is being borrowed from must be alive over the
entire web of guaranteed values. That is the entire web of guaranteed values act
as a liveness requirement on the begin_borrow's operand.

The way this is implemented is given a use that we are validating, if the use is
a BorrowScopeOperand (1), we see if the borrow scope operand consumes the given
guaranteed scope and forwards it into a borrow scope introducer. If so, we add
the list of consuming uses of the borrow scope introducer to the worklist to
visit and then iterate.

In order to avoid working with cycles, for now, the ownership verifier bans
liveness requiring uses that have cycles in them. This still allows us to have
loop carried guaranteed values.

(1) A BorrowScopeOperand is a concept that represents an operand to a SIL
instruction that begins a guaranteed scope of some sort. All BorrowScopeOperand
are thus at a minimum able to compute a compile time the static region in which
they implicitly use their operands. NOTE: We do not require the scope to be
represented as a SILValue in the same function.

We achieve some nice benefit by introducing this. Specifically:

1. We can optimize the pattern I mentioned above. This is a common pattern in
   many frameworks that want to return a default object if a computation fails
   (with the default object usually being some sort of global or static
   var). This will let us optimize that case when the global is a let global.

2. The SSA Updater can now be used with guaranteed values without needing to
   introduce extra copies. This will enable predictable mem opts to introduce
   less copies and for semantic arc opts to optimize the remaining copies that
   PMO exposes but does not insert itself.

rdar://56720519
2020-02-17 00:29:49 -08:00
Michael Gottesman
16efe40688 [ownership] Add copy constructor/assignment operator for BorrowScopeOperand.
NFC.
2020-02-16 14:35:50 -08:00
Michael Gottesman
5f45e0f569 [ownership] Move BorrowScopeOperand::visitEndScopeInstructions() from .h into .cpp. 2020-02-14 16:27:11 -08:00
Michael Gottesman
225b6e86fa [ownership] Add dumper/print methods for various OwnershipUtils.h helper structs. 2020-02-14 16:27:11 -08:00
Michael Gottesman
70a417c3f5 [ownership] Refactor checked conversion from ArrayRef<SILInstruction *> -> ArrayRef<BranchPropagatedUser> onto BranchPropagatedUser.
A branch propagated user that isn't a cond_br is layout compatible with a
SILInstruction *. This helper function converts from ArrayRef<SILInstruction *>
-> ArrayRef<BranchPropagatedUser> but also in asserts builds checks that our
invariant (namely all of the 'SILInstruction *' are not cond_br.
2020-02-02 11:54:35 -08:00
Michael Gottesman
d815c603c2 [ownership] Change BorrowScopeIntroducerValue::areInstructionsWithinScope to take SILInstructions instead of BranchPropagatedUser.
All non cond_br SILInstructions are layout compatible with BranchPropagatedUser
since BPU just stores a PointerIntPair that leaves its low bits as zero unless
one adds a cond_br. Since in these cases, the SILInstructions are all not
cond_br, we can use this alternative API.

I also added some asserts to validate that the assumption that all such
SILInstructions are not cond_br is respected.
2019-12-11 16:55:13 -08:00
Michael Gottesman
61e5653000 [semantic-arc-opts] Convert load [copy] -> load_borrow given single init alloc_stack. 2019-12-09 11:33:50 -08:00
Brent Royal-Gordon
99faa033fc [NFC] Standardize dump() methods in frontend
By convention, most structs and classes in the Swift compiler include a `dump()` method which prints debugging information. This method is meant to be called only from the debugger, but this means they’re often unused and may be eliminated from optimized binaries. On the other hand, some parts of the compiler call `dump()` methods directly despite them being intended as a pure debugging aid. clang supports attributes which can be used to avoid these problems, but they’re used very inconsistently across the compiler.

This commit adds `SWIFT_DEBUG_DUMP` and `SWIFT_DEBUG_DUMPER(<name>(<params>))` macros to declare `dump()` methods with the appropriate set of attributes and adopts this macro throughout the frontend. It does not pervasively adopt this macro in SILGen, SILOptimizer, or IRGen; these components use `dump()` methods in a different way where they’re frequently called from debugging code. Nor does it adopt it in runtime components like swiftRuntime and swiftReflection, because I’m a bit worried about size.

Despite the large number of files and lines affected, this change is NFC.
2019-10-31 18:37:42 -07:00
Michael Gottesman
f9f3f53420 [ownership] Add support for adding {end,abort}_apply to the regular user list of owned parameters passed as guaranteed parameters to a begin_apply.
This just follows automagically from the recent work that I did with introducing
BorrowScopeIntroducingOperands.

I confirmed locally that the negative tests do not fail as expected when
visiting the destroys of the guaranteed parameters.
2019-09-25 19:06:59 -07:00
Michael Gottesman
59a57ed044 [ownership] Introduce a new BorrowScopeIntroducingOperand that is used to work with implicit uses of owned parameters implied by their use by a borrow scope introducing value.
For now this only is used for ensuring that an owned value that has a
begin_borrow use, adds the end_borrows matched to the begin_borrow to the owned
values implicit regular user list.

I am going to use this same functionality to model the requirements around
begin_apply and mark_dependence (when I add an end_mark_dependence instruction).
2019-09-25 19:06:59 -07:00
Michael Gottesman
12aa95ac1e [ownership] Only allow BranchPropagatedUser to be constructed from Operands.
This commit only changes how BranchPropagatedUser is constructed and does not
change the internal representation. This is a result of my noticing that
BranchPropagatedUser could also use an operand internally to represent its
state. To simplify how I am making the change, I am splitting the change into
two PRs that should be easy to validate:

1. A commit that maps how the various users of BranchPropagatedUser have been
constructing BPUs to a single routine that takes an Operand. This leaves
BranchPropagatedUser's internal state alone as well as its user the Linear
Lifetime Checker.

2. A second commit that changes the internal bits of the BranchPropagatedUser to
store an Operand instead of a PointerUnion.

This will allow me to use the first commit to validate the second.
2019-09-23 17:24:55 -07:00
Michael Gottesman
7e3608e007 [ownership] Add a new checkValue overload to LinearLifetimeChecker that validates the linear lifetime of the passed in value and returns bool to indicate success/failure.
This is the first in a series of patches that begin to hide the underlying
linear lifetime implementation underneath the facade of the linear lifetime
checker. I only updated the users that this applies to.
2019-09-17 14:41:30 -07:00
Michael Gottesman
dceca2bc3b [ownership] Create a context structure for the linear lifetime checker.
This is just a simple refactoring commit in preparation for hiding more of the
details of the linear lifetime checker. This is NFC, just moving around code.
2019-09-16 20:13:56 -07:00
Michael Gottesman
bb4df032e7 [ownership] Add a new class BorrowScopeIntroducingValue that enables code to work abstractly with values that introduce a new borrow scope.
The idea is that this can be used to work with things like load_borrow,
begin_borrow, SILFunctionArgument, results of begin_apply(in the future) and the like in a
generic way using exhaustive switches to make sure this code stays up to date.

I refactored code in SemanticARCOpts and some utilities in OwnershipUtils.cpp to
use these new APIs. The code looks a lot nicer and should be quite easy to
expand to handle new borrow introducers (e.x.: end_apply).
2019-09-16 09:33:49 -07:00
Andrew Trick
207a4f714b Add LoadBorrow::getEndBorrows() and minor cleanup.
Instructions that start a scope should have a (discoverable) method
that retrieves the end of scope. This is a basic structural property
of the instruction.

I removed the makeEndBorrowRange helper because it adds overall
complexity and doesn't provide any value. If some code wants to be
generic over BeginBorrow/LoadBorrow, then that code should have it's
own trivial generic helper:

EndBorrowRange getEndBorrows<T>(T *beginBorrow) {
  return beginBorrow->getEndBorrows()
}
2019-05-07 11:35:10 -07:00
Michael Gottesman
f2d7dcaffd [ownership] Create a generic makeEndBorrowRange that can find end_borrow of {begin,load}_borrow.
We had a version of this specially for BeginBorrow. Makes sense to generalize
it.
2019-04-02 16:00:24 -07:00
Slava Pestov
8915f96e3e SIL: Replace SILType::isTrivial(SILModule) with isTrivial(SILFunction) 2019-03-12 01:16:04 -04:00
Michael Gottesman
a310f23b8a [ownership] Add support for load_borrow in predictable mem opt.
This reduces the diff in between -Onone output when stripping before/after
serialization.

We support load_borrow by translating it to the load [copy] case. Specifically,
for +1, we normally perform the following transform.

  store %1 to [init] %0
  ...
  %2 = load [copy] %0
  ...
  use(%2)
  ...
  destroy_value %2

=>

  %1a = copy_value %1
  store %1 to [init] %0
  ...
  use(%1a)
  ...
  destroy_value %1a

We analogously can optimize load_borrow by replacing the load with a
begin_borrow:

  store %1 to [init] %0
  ...
  %2 = load_borrow %0
  ...
  use(%2)
  ...
  end_borrow %2

=>

  %1a = copy_value %1
  store %1 to [init] %0
  ...
  %2 = begin_borrow %1a
  ...
  use(%2)
  ...
  end_borrow %2
  destroy_value %1a

The store from outside a loop being used by a load_borrow inside a loop is a
similar transformation as the +0 version except that we use a begin_borrow
inside the loop instead of a copy_value (making it even more efficient).
2019-02-11 00:54:28 -08:00
Michael Gottesman
a8dfac4788 [ownership] Use the new LinearLifetimeError to propagate back to callers why the checker failed. 2019-01-28 15:23:15 -08:00
Michael Gottesman
224c8a5801 [ownership] Use a richer error result than bool.
I am starting to use the linear lifetime checker in an optimizer role where it
no longer asserts but instead tells the optimizer pass what is needed to cause
the lifetime to be linear. To do so I need to be able to return richer
information to the caller such as whether or not a leak, double consume, or
use-after-free occurs.
2019-01-28 12:47:40 -08:00
Michael Gottesman
6f767f8b34 [sil] Delete dead code.
Originally I wanted to try to use this code to optimize on ownership sil without
assuming verification passed (since the utility would check verification). In
the end, this was not useful.
2019-01-24 20:02:02 -08:00
Michael Gottesman
f8f11fadf6 [ownership] Allow for callers of the linear lifetime checker to ask it to return leaking blocks instead of erroring.
A common pattern when working in ossa is the introduction of a copy for lifetime
reasons at a dominating point in a program with a single use that may not
post-dominate the use. This API gives a "tight lifetime" bound by returning the
blocks where the value needs to be destroyed to prevent leaks.

NOTE: I was not 100% sure if consume errors should still assert. It seems like a
double consume error would be user error, so an assert is probably fine.
2019-01-22 01:34:39 -08:00
Michael Gottesman
099cae5692 [semantic-arc-opts] Handle arg extract + copy + borrow + use.
Specifically, now the optimizer can take the following code:

sil @foo: $@convention(thin) (@guaranteed NativeObjectPair) -> () {
bb0(%0 : @guaranteed $NativeObjectPair):
  %1 = struct_extract %0 : $NativeObjectPair, #NativeObjectPair.obj1
  %2 = copy_value %1 : $Builtin.NativeObject
  %3 = begin_borrow %2 : $Builtin.NativeObject
  %foo = function_ref ...
  apply %foo(%3) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> ()
  end_borrow %3 : $Builtin.NativeObject
  destroy_value %2 : $Builtin.NativeObject
  ...
}

and eliminate all ownership instructions. I.e.:

sil @foo: $@convention(thin) (@guaranteed NativeObjectPair) -> () {
bb0(%0 : @guaranteed $NativeObjectPair):
  %1 = struct_extract %0 : $NativeObjectPair, #NativeObjectPair.obj1
  %foo = function_ref ....
  apply %foo(%1)
  ...
}

Before this the optimizer could only eliminate 200 insts in the stdlib. With
this change, we now can eliminate ~7000.
2018-11-13 12:07:48 -08:00
Michael Gottesman
1baf38cb07 [sil] Split operand ownership classification from SILOwnershipVerifier.cpp into OperandOwnership.cpp.
Last week I split out operand ownership classification from the
SILOwnershipVerifier into the OperandOwnershipKindClassifier. Now move that
classifier code to another file so that SILOwnershipVerifier.cpp just consists
of the actual checker code. This makes sense since this type of classifier is
describing a separate structural aspect of SIL rather than something intrinsic
to the ownership verifier.

Keep in mind that this is not the final form of this classifier. Just an
incremental step forward.
2018-10-10 17:24:53 -07:00
Michael Gottesman
157091d5c6 [ownership] Extract out from SILOwnershipVerifier the OperandOwnershipKindMapClassifier
NOTE: This is not the final form of how operand ownership restraints will be
represented. This patch is instead an incremental change that extracts out this
functionality from the ownership verifier as a pure refactor.

rdar://44667493
2018-10-01 22:14:41 -07:00
Michael Gottesman
c599539044 [sil] Eliminate the src parameter from end_borrow.
This does not eliminate the entrypoints on SILBuilder yet. I want to do this in
two parts so that it is functionally easier to disentangle changing the APIs
above SILBuilder and changing the underlying instruction itself.

rdar://33440767
2018-09-04 16:38:24 -07:00
Michael Gottesman
fa157871f0 [func-sig-opts][+0->+1] Extract out the LinearLifetimeChecker from the Ownership verifier.
I am doing this for two reasons:

1. I am going to use this in function signature opts to prove that a guaranteed
parameter is "consumed".
2. It puts the ownership verifier on a diet.

rdar://38196046
2018-05-08 16:47:17 -07:00
Michael Gottesman
0de2b76ec9 [func-sig-opts][+0->+1] Rename OwnershipChecker.h => OwnershipUtils.h
I am going to be expanding this to contain more general utilities so naming the
file OwnershipChecker.h really doesn't make sense.

rdar://38196046
2018-05-08 11:21:06 -07:00