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.
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).
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.
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.
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.
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).
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()
}
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).
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.
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.
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.
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.
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
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
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