mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
244 lines
7.6 KiB
C++
244 lines
7.6 KiB
C++
//===--- ScopedAddressUtils.cpp -------------------------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "swift/SIL/ScopedAddressUtils.h"
|
|
#include "swift/SIL/OwnershipUtils.h"
|
|
#include "swift/SIL/PrunedLiveness.h"
|
|
#include "swift/SIL/SILArgument.h"
|
|
#include "swift/SIL/SILBuilder.h"
|
|
#include "swift/SIL/SILInstruction.h"
|
|
#include "swift/SILOptimizer/Utils/InstructionDeleter.h"
|
|
#include "swift/SILOptimizer/Utils/OwnershipOptUtils.h"
|
|
|
|
using namespace swift;
|
|
|
|
void ScopedAddressValueKind::print(llvm::raw_ostream &os) const {
|
|
switch (value) {
|
|
case ScopedAddressValueKind::Invalid:
|
|
llvm_unreachable("Using invalid case?!");
|
|
case ScopedAddressValueKind::StoreBorrow:
|
|
os << "StoreBorrow";
|
|
return;
|
|
case ScopedAddressValueKind::BeginAccess:
|
|
os << "BeginAccess";
|
|
return;
|
|
}
|
|
llvm_unreachable("Covered switch isn't covered?!");
|
|
}
|
|
|
|
llvm::raw_ostream &swift::operator<<(llvm::raw_ostream &os,
|
|
ScopedAddressValueKind kind) {
|
|
kind.print(os);
|
|
return os;
|
|
}
|
|
|
|
bool ScopedAddressValue::isScopeEndingUse(Operand *op) const {
|
|
switch (kind) {
|
|
case ScopedAddressValueKind::Invalid:
|
|
llvm_unreachable("Using invalid case?!");
|
|
case ScopedAddressValueKind::StoreBorrow: {
|
|
if (auto *endBorrow = dyn_cast<EndBorrowInst>(op->getUser())) {
|
|
return endBorrow->getOperand() == value;
|
|
}
|
|
return false;
|
|
}
|
|
case ScopedAddressValueKind::BeginAccess: {
|
|
if (auto *endAccess = dyn_cast<EndAccessInst>(op->getUser())) {
|
|
return endAccess->getOperand() == value;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool ScopedAddressValue::visitScopeEndingUses(
|
|
function_ref<bool(Operand *)> visitor) const {
|
|
switch (kind) {
|
|
case ScopedAddressValueKind::Invalid:
|
|
llvm_unreachable("Using invalid case?!");
|
|
case ScopedAddressValueKind::StoreBorrow: {
|
|
for (auto *use : value->getUses()) {
|
|
if (isa<EndBorrowInst>(use->getUser())) {
|
|
if (!visitor(use))
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
case ScopedAddressValueKind::BeginAccess: {
|
|
for (auto *use : value->getUses()) {
|
|
if (isa<EndAccessInst>(use->getUser())) {
|
|
if (!visitor(use))
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool ScopedAddressValue::computeLiveness(SSAPrunedLiveness &liveness) const {
|
|
SmallVector<Operand *, 4> uses;
|
|
// Collect all uses that need to be enclosed by the scope.
|
|
auto addressKind = findTransitiveUsesForAddress(value, &uses);
|
|
if (addressKind != AddressUseKind::NonEscaping) {
|
|
return false;
|
|
}
|
|
|
|
liveness.initializeDef(value);
|
|
for (auto *use : uses) {
|
|
// Update all collected uses as non-lifetime ending.
|
|
liveness.updateForUse(use->getUser(), /* lifetimeEnding */ false);
|
|
}
|
|
visitScopeEndingUses([&](Operand *endOp) {
|
|
liveness.updateForUse(endOp->getUser(), /* isLifetimeEnding */ true);
|
|
return true;
|
|
});
|
|
return true;
|
|
}
|
|
|
|
void ScopedAddressValue::createScopeEnd(SILBasicBlock::iterator insertPt,
|
|
SILLocation loc) const {
|
|
switch (kind) {
|
|
case ScopedAddressValueKind::StoreBorrow: {
|
|
SILBuilderWithScope(insertPt).createEndBorrow(loc, value);
|
|
return;
|
|
}
|
|
case ScopedAddressValueKind::BeginAccess: {
|
|
SILBuilderWithScope(insertPt).createEndAccess(loc, value, false);
|
|
return;
|
|
}
|
|
case ScopedAddressValueKind::Invalid:
|
|
llvm_unreachable("Using invalid case?!");
|
|
}
|
|
}
|
|
|
|
void ScopedAddressValue::endScopeAtLivenessBoundary(
|
|
SSAPrunedLiveness *liveness) const {
|
|
// If no users exist, create scope ending instruction immediately after the
|
|
// scoped address value.
|
|
if (liveness->empty()) {
|
|
createScopeEnd(value->getNextInstruction()->getIterator(),
|
|
RegularLocation::getAutoGeneratedLocation());
|
|
return;
|
|
}
|
|
|
|
PrunedLivenessBoundary scopedAddressBoundary;
|
|
liveness->computeBoundary(scopedAddressBoundary);
|
|
// Go over the boundary and create scope ending instructions.
|
|
scopedAddressBoundary.visitInsertionPoints(
|
|
[&](SILBasicBlock::iterator insertPt) {
|
|
createScopeEnd(insertPt, RegularLocation::getAutoGeneratedLocation());
|
|
});
|
|
}
|
|
|
|
bool swift::hasOtherStoreBorrowsInLifetime(StoreBorrowInst *storeBorrow,
|
|
SSAPrunedLiveness *liveness,
|
|
DeadEndBlocks *deadEndBlocks) {
|
|
SmallVector<StoreBorrowInst *, 4> otherStoreBorrows;
|
|
// Collect all other store_borrows to the destination of \p storeBorrow
|
|
for (auto *destUse : storeBorrow->getDest()->getUses()) {
|
|
if (auto *user = dyn_cast<StoreBorrowInst>(destUse->getUser())) {
|
|
if (user == storeBorrow) {
|
|
continue;
|
|
}
|
|
otherStoreBorrows.push_back(user);
|
|
}
|
|
}
|
|
|
|
for (auto *otherStoreBorrow : otherStoreBorrows) {
|
|
// Return true, if otherStoreBorrow was in \p storeBorrow's scope
|
|
if (liveness->isWithinBoundary(otherStoreBorrow)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool swift::extendStoreBorrow(StoreBorrowInst *sbi,
|
|
SmallVectorImpl<Operand *> &newUses,
|
|
DeadEndBlocks *deadEndBlocks,
|
|
InstModCallbacks callbacks) {
|
|
ScopedAddressValue scopedAddress(sbi);
|
|
|
|
SmallVector<SILBasicBlock *, 4> discoveredBlocks;
|
|
SSAPrunedLiveness storeBorrowLiveness(&discoveredBlocks);
|
|
bool success = scopedAddress.computeLiveness(storeBorrowLiveness);
|
|
|
|
// If all new uses are within store_borrow boundary, no need for extension.
|
|
if (storeBorrowLiveness.areUsesWithinBoundary(newUses, deadEndBlocks)) {
|
|
return true;
|
|
}
|
|
|
|
if (!success) {
|
|
return false;
|
|
}
|
|
|
|
// store_borrow extension is possible only when there are no other
|
|
// store_borrows to the same destination within the store_borrow's lifetime
|
|
// built from newUsers.
|
|
if (hasOtherStoreBorrowsInLifetime(sbi, &storeBorrowLiveness,
|
|
deadEndBlocks)) {
|
|
return false;
|
|
}
|
|
|
|
InstModCallbacks tempCallbacks = callbacks;
|
|
InstructionDeleter deleter(std::move(tempCallbacks));
|
|
GuaranteedOwnershipExtension borrowExtension(deleter, *deadEndBlocks,
|
|
sbi->getFunction());
|
|
auto status = borrowExtension.checkBorrowExtension(
|
|
BorrowedValue(sbi->getSrc()), newUses);
|
|
if (status == GuaranteedOwnershipExtension::Invalid) {
|
|
return false;
|
|
}
|
|
|
|
borrowExtension.transform(status);
|
|
|
|
SmallVector<Operand *, 4> endBorrowUses;
|
|
// Collect old scope-ending instructions.
|
|
scopedAddress.visitScopeEndingUses([&](Operand *op) {
|
|
endBorrowUses.push_back(op);
|
|
return true;
|
|
});
|
|
|
|
for (auto *use : newUses) {
|
|
// Update newUsers as non-lifetime ending.
|
|
storeBorrowLiveness.updateForUse(use->getUser(),
|
|
/* lifetimeEnding */ false);
|
|
}
|
|
|
|
// Add new scope-ending instructions.
|
|
scopedAddress.endScopeAtLivenessBoundary(&storeBorrowLiveness);
|
|
|
|
// Remove old scope-ending instructions.
|
|
for (auto *endBorrowUse : endBorrowUses) {
|
|
callbacks.deleteInst(endBorrowUse->getUser());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void ScopedAddressValue::print(llvm::raw_ostream &os) const {
|
|
os << "ScopedAddressIntroducingValue:\n"
|
|
"Kind: "
|
|
<< kind
|
|
<< "\n"
|
|
"Value: "
|
|
<< value;
|
|
}
|
|
|
|
llvm::raw_ostream &swift::operator<<(llvm::raw_ostream &os,
|
|
const ScopedAddressValue &value) {
|
|
value.print(os);
|
|
return os;
|
|
}
|