//===--- 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 Worklist; for (auto *Op : Arg->getUses()) { if (auto *CVI = dyn_cast(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(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(I); })) continue; // Extra copy values that we should visit recursively. llvm::SmallVector NewCopyInsts; llvm::SmallVector NewBorrowInsts; if (!all_of(Checker.RegularUsers, [&](SILInstruction *I) -> bool { if (auto *CVI = dyn_cast(I)) { NewCopyInsts.push_back(CVI); return true; } if (!isa(I) && !isa(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(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(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(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(); }