mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
173 lines
5.7 KiB
C++
173 lines
5.7 KiB
C++
//===--- SemanticARCOpts.cpp ----------------------------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2017 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#define DEBUG_TYPE "sil-semantic-arc-opts"
|
|
#include "swift/SIL/OwnershipChecker.h"
|
|
#include "swift/SIL/SILArgument.h"
|
|
#include "swift/SIL/SILInstruction.h"
|
|
#include "swift/SIL/BasicBlockUtils.h"
|
|
#include "swift/SILOptimizer/Analysis/PostOrderAnalysis.h"
|
|
#include "swift/SILOptimizer/PassManager/Passes.h"
|
|
#include "swift/SILOptimizer/PassManager/Transforms.h"
|
|
#include "llvm/ADT/SmallPtrSet.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "llvm/ADT/Statistic.h"
|
|
|
|
using namespace swift;
|
|
|
|
STATISTIC(NumEliminatedInsts, "number of removed instructions");
|
|
|
|
static bool optimizeGuaranteedArgument(SILArgument *Arg,
|
|
OwnershipChecker &Checker) {
|
|
bool MadeChange = false;
|
|
|
|
// Gather all copy_value users of Arg.
|
|
llvm::SmallVector<CopyValueInst *, 4> Worklist;
|
|
for (auto *Op : Arg->getUses()) {
|
|
if (auto *CVI = dyn_cast<CopyValueInst>(Op->getUser())) {
|
|
Worklist.push_back(CVI);
|
|
}
|
|
}
|
|
|
|
// Then until we run out of copies...
|
|
while (!Worklist.empty()) {
|
|
auto *CVI = Worklist.pop_back_val();
|
|
|
|
// Quickly see if copy has only one use and that use is a destroy_value. In
|
|
// such a case, we can always eliminate both the copy and the destroy.
|
|
if (auto *Op = CVI->getSingleUse()) {
|
|
if (auto *DVI = dyn_cast<DestroyValueInst>(Op->getUser())) {
|
|
DVI->eraseFromParent();
|
|
CVI->eraseFromParent();
|
|
NumEliminatedInsts += 2;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Ok, now run the checker on the copy value. If it fails, then we just
|
|
// continue.
|
|
if (!Checker.checkValue(CVI))
|
|
continue;
|
|
|
|
// Otherwise, lets do a quick check on what the checker thinks the lifetime
|
|
// ending and non-lifetime ending users. To be conservative, we bail unless
|
|
// each lifetime ending use is a destroy_value and if each non-lifetime
|
|
// ending use is one of the following instructions:
|
|
//
|
|
// 1. copy_value.
|
|
// 2. begin_borrow.
|
|
// 3. end_borrow.
|
|
if (!all_of(Checker.LifetimeEndingUsers, [](SILInstruction *I) -> bool {
|
|
return isa<DestroyValueInst>(I);
|
|
}))
|
|
continue;
|
|
|
|
// Extra copy values that we should visit recursively.
|
|
llvm::SmallVector<CopyValueInst *, 8> NewCopyInsts;
|
|
llvm::SmallVector<SILInstruction *, 8> NewBorrowInsts;
|
|
if (!all_of(Checker.RegularUsers, [&](SILInstruction *I) -> bool {
|
|
if (auto *CVI = dyn_cast<CopyValueInst>(I)) {
|
|
NewCopyInsts.push_back(CVI);
|
|
return true;
|
|
}
|
|
|
|
if (!isa<BeginBorrowInst>(I) && !isa<EndBorrowInst>(I))
|
|
return false;
|
|
|
|
NewBorrowInsts.push_back(I);
|
|
return true;
|
|
}))
|
|
continue;
|
|
|
|
// Ok! we can remove the copy_value, destroy_values!
|
|
MadeChange = true;
|
|
CVI->replaceAllUsesWith(CVI->getOperand());
|
|
CVI->eraseFromParent();
|
|
++NumEliminatedInsts;
|
|
|
|
while (!Checker.LifetimeEndingUsers.empty()) {
|
|
Checker.LifetimeEndingUsers.pop_back_val()->eraseFromParent();
|
|
++NumEliminatedInsts;
|
|
}
|
|
|
|
// Then add the copy_values that were users of our original copy value to
|
|
// the worklist.
|
|
while (!NewCopyInsts.empty()) {
|
|
Worklist.push_back(NewCopyInsts.pop_back_val());
|
|
}
|
|
|
|
// Then remove any begin/end borrow that we found. These are unneeded since
|
|
// the lifetime guarantee from the argument exists above and beyond said
|
|
// scope.
|
|
while (!NewBorrowInsts.empty()) {
|
|
SILInstruction *I = NewBorrowInsts.pop_back_val();
|
|
if (auto *BBI = dyn_cast<BeginBorrowInst>(I)) {
|
|
// Any copy_value that is used by the begin borrow is added to the
|
|
// worklist.
|
|
for (auto *BBIUse : BBI->getUses()) {
|
|
if (auto *BBIUseCopyValue =
|
|
dyn_cast<CopyValueInst>(BBIUse->getUser())) {
|
|
Worklist.push_back(BBIUseCopyValue);
|
|
}
|
|
}
|
|
BBI->replaceAllUsesWith(BBI->getOperand());
|
|
BBI->eraseFromParent();
|
|
++NumEliminatedInsts;
|
|
continue;
|
|
}
|
|
|
|
// This is not necessary, but it does add a check.
|
|
auto *EBI = cast<EndBorrowInst>(I);
|
|
EBI->eraseFromParent();
|
|
++NumEliminatedInsts;
|
|
}
|
|
}
|
|
|
|
return MadeChange;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Top Level Entrypoint
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
namespace {
|
|
|
|
struct SemanticARCOpts : SILFunctionTransform {
|
|
void run() override {
|
|
bool MadeChange = false;
|
|
SILFunction *F = getFunction();
|
|
|
|
DeadEndBlocks DEBlocks(F);
|
|
OwnershipChecker Checker{F->getModule(), DEBlocks, {}, {}, {}};
|
|
|
|
// First as a special case, handle guaranteed SIL function arguments.
|
|
//
|
|
// The reason that this is special is that we do not need to consider the
|
|
// end of the borrow scope since the end of the function is the end of the
|
|
// borrow scope.
|
|
for (auto *Arg : F->getArguments()) {
|
|
if (Arg->getOwnershipKind() != ValueOwnershipKind::Guaranteed)
|
|
continue;
|
|
MadeChange |= optimizeGuaranteedArgument(Arg, Checker);
|
|
}
|
|
|
|
if (MadeChange) {
|
|
invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions);
|
|
}
|
|
}
|
|
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
SILTransform *swift::createSemanticARCOpts() { return new SemanticARCOpts(); }
|