[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).
This commit is contained in:
Michael Gottesman
2019-09-11 14:49:17 -07:00
parent 94c7d04567
commit bb4df032e7
3 changed files with 283 additions and 76 deletions

View File

@@ -14,6 +14,7 @@
#define SWIFT_SIL_OWNERSHIPUTILS_H
#include "swift/Basic/LLVM.h"
#include "swift/SIL/BranchPropagatedUser.h"
#include "swift/SIL/SILArgument.h"
#include "swift/SIL/SILInstruction.h"
#include "swift/SIL/SILValue.h"
@@ -164,10 +165,152 @@ bool isOwnershipForwardingInst(SILInstruction *i);
bool isGuaranteedForwardingInst(SILInstruction *i);
struct BorrowScopeIntroducerKind {
using UnderlyingKindTy = std::underlying_type<ValueKind>::type;
/// Enum we use for exhaustive pattern matching over borrow scope introducers.
enum Kind : UnderlyingKindTy {
LoadBorrow = UnderlyingKindTy(ValueKind::LoadBorrowInst),
BeginBorrow = UnderlyingKindTy(ValueKind::BeginBorrowInst),
SILFunctionArgument = UnderlyingKindTy(ValueKind::SILFunctionArgument),
};
static Optional<BorrowScopeIntroducerKind> get(ValueKind kind) {
switch (kind) {
default:
return None;
case ValueKind::LoadBorrowInst:
return BorrowScopeIntroducerKind(LoadBorrow);
case ValueKind::BeginBorrowInst:
return BorrowScopeIntroducerKind(BeginBorrow);
case ValueKind::SILFunctionArgument:
return BorrowScopeIntroducerKind(SILFunctionArgument);
}
}
Kind value;
BorrowScopeIntroducerKind(Kind newValue) : value(newValue) {}
BorrowScopeIntroducerKind(const BorrowScopeIntroducerKind &other)
: value(other.value) {}
operator Kind() const { return value; }
/// Is this a borrow scope that begins and ends within the same function and
/// thus is guaranteed to have an "end_scope" instruction.
///
/// In contrast, borrow scopes that are non-local (e.x. from
/// SILFunctionArguments) rely a construct like a SILFunction as the begin/end
/// of the scope.
bool isLocalScope() const {
switch (value) {
case BorrowScopeIntroducerKind::BeginBorrow:
case BorrowScopeIntroducerKind::LoadBorrow:
return true;
case BorrowScopeIntroducerKind::SILFunctionArgument:
return false;
}
llvm_unreachable("Covered switch isnt covered?!");
}
void print(llvm::raw_ostream &os) const;
LLVM_ATTRIBUTE_DEPRECATED(void dump() const, "only for use in the debugger");
};
llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
BorrowScopeIntroducerKind kind);
/// A higher level construct for working with values that represent the
/// introduction of a new borrow scope.
///
/// DISCUSSION: A "borrow introducer" is a SILValue that represents the
/// beginning of a borrow scope that the ownership verifier validates. The idea
/// is this API allows one to work in a generic way with all of the various
/// introducers.
///
/// Some examples of borrow introducers: guaranteed SILFunctionArgument,
/// LoadBorrow, BeginBorrow, guaranteed BeginApply results.
///
/// NOTE: It is assumed that if a borrow introducer is a value of a
/// SILInstruction with multiple results, that the all of the SILInstruction's
/// guaranteed results are borrow introducers. In practice this means that
/// borrow introducers can not have guaranteed results that are not creating a
/// new borrow scope. No such instructions exist today.
struct BorrowScopeIntroducingValue {
BorrowScopeIntroducerKind kind;
SILValue value;
BorrowScopeIntroducingValue(LoadBorrowInst *lbi)
: kind(BorrowScopeIntroducerKind::LoadBorrow), value(lbi) {}
BorrowScopeIntroducingValue(BeginBorrowInst *bbi)
: kind(BorrowScopeIntroducerKind::BeginBorrow), value(bbi) {}
BorrowScopeIntroducingValue(SILFunctionArgument *arg)
: kind(BorrowScopeIntroducerKind::SILFunctionArgument), value(arg) {
assert(arg->getOwnershipKind() == ValueOwnershipKind::Guaranteed);
}
BorrowScopeIntroducingValue(SILValue v)
: kind(*BorrowScopeIntroducerKind::get(v->getKind())), value(v) {
assert(v.getOwnershipKind() == ValueOwnershipKind::Guaranteed);
}
/// If value is a borrow introducer return it after doing some checks.
static Optional<BorrowScopeIntroducingValue> get(SILValue value) {
auto kind = BorrowScopeIntroducerKind::get(value->getKind());
if (!kind || value.getOwnershipKind() != ValueOwnershipKind::Guaranteed)
return None;
return BorrowScopeIntroducingValue(*kind, value);
}
/// If this value is introducing a local scope, gather all local end scope
/// instructions and append them to \p scopeEndingInsts. Asserts if this is
/// called with a scope that is not local.
///
/// NOTE: To determine if a scope is a local scope, call
/// BorrowScopeIntoducingValue::isLocalScope().
void getLocalScopeEndingInstructions(
SmallVectorImpl<SILInstruction *> &scopeEndingInsts) const;
/// If this value is introducing a local scope, gather all local end scope
/// instructions and pass them individually to visitor. Asserts if this is
/// called with a scope that is not local.
///
/// The intention is that this method can be used instead of
/// BorrowScopeIntroducingValue::getLocalScopeEndingInstructions() to avoid
/// introducing an intermediate array when one needs to transform the
/// instructions before storing them.
///
/// NOTE: To determine if a scope is a local scope, call
/// BorrowScopeIntoducingValue::isLocalScope().
void visitLocalScopeEndingInstructions(
function_ref<void(SILInstruction *)> visitor) const;
bool isLocalScope() const { return kind.isLocalScope(); }
/// Returns true if the passed in set of instructions is completely within the
/// lifetime of this borrow introducer.
///
/// NOTE: Scratch space is used internally to this method to store the end
/// borrow scopes if needed.
bool areInstructionsWithinScope(
ArrayRef<BranchPropagatedUser> instructions,
SmallVectorImpl<BranchPropagatedUser> &scratchSpace,
SmallPtrSetImpl<SILBasicBlock *> &visitedBlocks,
DeadEndBlocks &deadEndBlocks) const;
private:
/// Internal constructor for failable static constructor. Please do not expand
/// its usage since it assumes the code passed in is well formed.
BorrowScopeIntroducingValue(BorrowScopeIntroducerKind kind, SILValue value)
: kind(kind), value(value) {}
};
/// Look up through the def-use chain of \p inputValue, recording any "borrow"
/// introducers that we find into \p out.
bool getUnderlyingBorrowIntroducers(SILValue inputValue,
SmallVectorImpl<SILValue> &out);
/// introducing values that we find into \p out. If at any point, we find a
/// point in the chain we do not understand, we bail and return false. If we are
/// able to understand all of the def-use graph, we know that we have found all
/// of the borrow introducing values, we return true.
bool getUnderlyingBorrowIntroducingValues(
SILValue inputValue, SmallVectorImpl<BorrowScopeIntroducingValue> &out);
} // namespace swift