diff --git a/include/swift/SIL/OwnershipUtils.h b/include/swift/SIL/OwnershipUtils.h index b9c0d68326bd8..2cedc9bedcf60 100644 --- a/include/swift/SIL/OwnershipUtils.h +++ b/include/swift/SIL/OwnershipUtils.h @@ -1206,6 +1206,14 @@ void visitTransitiveEndBorrows( BorrowedValue beginBorrow, function_ref visitEndBorrow); +/// Whether the specified lexical begin_borrow instruction is nested. +/// +/// A begin_borrow [lexical] is nested if the borrowed value's lifetime is +/// guaranteed by another lexical scope. That happens if: +/// - the value is a guaranteed argument to the function +/// - the value is itself a begin_borrow [lexical] +bool isNestedLexicalBeginBorrow(BeginBorrowInst *bbi); + } // namespace swift #endif diff --git a/lib/SIL/Utils/OwnershipUtils.cpp b/lib/SIL/Utils/OwnershipUtils.cpp index abf65317d9f79..6c60b718d8bac 100644 --- a/lib/SIL/Utils/OwnershipUtils.cpp +++ b/lib/SIL/Utils/OwnershipUtils.cpp @@ -1487,3 +1487,21 @@ void swift::visitTransitiveEndBorrows( } } } + +/// Whether the specified lexical begin_borrow instruction is nested. +/// +/// A begin_borrow [lexical] is nested if the borrowed value's lifetime is +/// guaranteed by another lexical scope. That happens if: +/// - the value is a guaranteed argument to the function +/// - the value is itself a begin_borrow [lexical] +bool swift::isNestedLexicalBeginBorrow(BeginBorrowInst *bbi) { + assert(bbi->isLexical()); + auto value = bbi->getOperand(); + if (auto *outerBBI = dyn_cast(value)) { + return outerBBI->isLexical(); + } + if (auto *arg = dyn_cast(value)) { + return arg->getOwnershipKind() == OwnershipKind::Guaranteed; + } + return false; +} diff --git a/lib/SILOptimizer/SemanticARC/BorrowScopeOpts.cpp b/lib/SILOptimizer/SemanticARC/BorrowScopeOpts.cpp index 7c72a534a7a97..2ff6f9cdeeb41 100644 --- a/lib/SILOptimizer/SemanticARC/BorrowScopeOpts.cpp +++ b/lib/SILOptimizer/SemanticARC/BorrowScopeOpts.cpp @@ -24,34 +24,15 @@ using namespace swift; using namespace swift::semanticarc; -/// Whether the provided lexical begin_borrow instruction is redundant. -/// -/// A begin_borrow [lexical] is redundant if the borrowed value's lifetime is -/// otherwise guaranteed. That happens if: -/// - the value is a guaranteed argument to the function -/// - the value is itself a begin_borrow [lexical] -static bool isRedundantLexicalBeginBorrow(BeginBorrowInst *bbi) { - assert(bbi->isLexical()); - auto value = bbi->getOperand(); - if (auto *outerBBI = dyn_cast(value)) { - return outerBBI->isLexical(); - } - if (auto *arg = dyn_cast(value)) { - return arg->getOwnershipKind() == OwnershipKind::Guaranteed; - } - return false; -} - bool SemanticARCOptVisitor::visitBeginBorrowInst(BeginBorrowInst *bbi) { // Quickly check if we are supposed to perform this transformation. if (!ctx.shouldPerform(ARCTransformKind::RedundantBorrowScopeElimPeephole)) return false; - // Lexical borrow scopes must remain in order to ensure that value lifetimes - // are not observably shortened. - if (bbi->isLexical()) { - if (!isRedundantLexicalBeginBorrow(bbi)) - return false; + // Non-redundant, lexical borrow scopes must remain in order to ensure that + // value lifetimes are not observably shortened. + if (bbi->isLexical() && !isNestedLexicalBeginBorrow(bbi)) { + return false; } auto kind = bbi->getOperand().getOwnershipKind(); diff --git a/lib/SILOptimizer/Transforms/CopyPropagation.cpp b/lib/SILOptimizer/Transforms/CopyPropagation.cpp index 20b9dee700a30..b5c5646d85449 100644 --- a/lib/SILOptimizer/Transforms/CopyPropagation.cpp +++ b/lib/SILOptimizer/Transforms/CopyPropagation.cpp @@ -438,7 +438,7 @@ void CopyPropagation::run() { // blocks and pushing begin_borrows as we see them and then popping them // off the end will result in shrinking inner borrow scopes first. while (auto *bbi = beginBorrowsToShrink.pop()) { - shrinkBorrowScope(bbi, deleter); + changed |= shrinkBorrowScope(bbi, deleter); } // canonicalizer performs all modifications through deleter's callbacks, so we diff --git a/lib/SILOptimizer/Utils/CanonicalizeInstruction.cpp b/lib/SILOptimizer/Utils/CanonicalizeInstruction.cpp index 2959caf8afe14..22c9f87ff1843 100644 --- a/lib/SILOptimizer/Utils/CanonicalizeInstruction.cpp +++ b/lib/SILOptimizer/Utils/CanonicalizeInstruction.cpp @@ -445,9 +445,13 @@ static SILBasicBlock::iterator eliminateSimpleBorrows(BeginBorrowInst *bbi, CanonicalizeInstruction &pass) { auto next = std::next(bbi->getIterator()); - // Never eliminate lexical borrow scopes. They must be kept to ensure that - // value lifetimes aren't observably shortened. - if (bbi->isLexical()) + // Lexical borrow scopes can only be eliminated under certain circumstances: + // (1) They can never be eliminated if the module is in the raw stage, because + // they may be needed for diagnostic. + // (2) They can never be eliminated if there is no enclosing lexical scope + // which guarantees the lifetime of the value. + if (bbi->isLexical() && (bbi->getModule().getStage() == SILStage::Raw || + !isNestedLexicalBeginBorrow(bbi))) return next; // We know that our borrow is completely within the lifetime of its base value