//===--- ARCSequenceOpts.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 "arc-sequence-opts" #include "ARCSequenceOpts.h" #include "swift/SIL/SILBuilder.h" #include "swift/SIL/SILVisitor.h" #include "swift/SILOptimizer/Analysis/ARCAnalysis.h" #include "swift/SILOptimizer/Analysis/AliasAnalysis.h" #include "swift/SILOptimizer/Analysis/EpilogueARCAnalysis.h" #include "swift/SILOptimizer/Analysis/LoopAnalysis.h" #include "swift/SILOptimizer/Analysis/LoopRegionAnalysis.h" #include "swift/SILOptimizer/Analysis/PostOrderAnalysis.h" #include "swift/SILOptimizer/Analysis/ProgramTerminationAnalysis.h" #include "swift/SILOptimizer/Analysis/RCIdentityAnalysis.h" #include "swift/SILOptimizer/PassManager/Passes.h" #include "swift/SILOptimizer/PassManager/Transforms.h" #include "swift/SILOptimizer/Utils/InstOptUtils.h" #include "swift/SILOptimizer/Utils/LoopUtils.h" #include "llvm/ADT/MapVector.h" #include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/Statistic.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" using namespace swift; STATISTIC(NumRefCountOpsRemoved, "Total number of increments removed"); llvm::cl::opt EnableLoopARC("enable-loop-arc", llvm::cl::init(true)); //===----------------------------------------------------------------------===// // Code Motion //===----------------------------------------------------------------------===// // This routine takes in the ARCMatchingSet \p MatchSet and adds the increments // and decrements to the delete list. void ARCPairingContext::optimizeMatchingSet( ARCMatchingSet &MatchSet, llvm::SmallVectorImpl &DeadInsts) { LLVM_DEBUG(llvm::dbgs() << "**** Optimizing Matching Set ****\n"); // Add the old increments to the delete list. for (SILInstruction *Increment : MatchSet.Increments) { MadeChange = true; LLVM_DEBUG(llvm::dbgs() << " Deleting increment: " << *Increment); DeadInsts.push_back(Increment); ++NumRefCountOpsRemoved; } // Add the old decrements to the delete list. for (SILInstruction *Decrement : MatchSet.Decrements) { MadeChange = true; LLVM_DEBUG(llvm::dbgs() << " Deleting decrement: " << *Decrement); DeadInsts.push_back(Decrement); ++NumRefCountOpsRemoved; } } bool ARCPairingContext::performMatching( llvm::SmallVectorImpl &DeadInsts) { bool MatchedPair = false; LLVM_DEBUG(llvm::dbgs() << "**** Computing ARC Matching Sets for " << F.getName() << " ****\n"); /// For each increment that we matched to a decrement, try to match it to a /// decrement -> increment pair. for (auto Pair : IncToDecStateMap) { if (!Pair.has_value()) continue; SILInstruction *Increment = Pair->first; if (!Increment) continue; // blotted LLVM_DEBUG(llvm::dbgs() << "Constructing Matching Set For: " << *Increment); ARCMatchingSetBuilder Builder(DecToIncStateMap, IncToDecStateMap, RCIA); Builder.init(Increment); if (Builder.matchUpIncDecSetsForPtr()) { MatchedPair |= Builder.matchedPair(); auto &Set = Builder.getResult(); for (auto *I : Set.Increments) IncToDecStateMap.erase(I); for (auto *I : Set.Decrements) DecToIncStateMap.erase(I); optimizeMatchingSet(Set, DeadInsts); } } return MatchedPair; } //===----------------------------------------------------------------------===// // Loop ARC //===----------------------------------------------------------------------===// void LoopARCPairingContext::runOnLoop(SILLoop *L) { auto *Region = LRFI->getRegion(L); if (processRegion(Region, false, false)) { // We do not recompute for now since we only look at the top function level // for post dominating releases. processRegion(Region, true, false); } // Now that we have finished processing the loop, summarize the loop. Evaluator.summarizeLoop(Region); } void LoopARCPairingContext::runOnFunction(SILFunction *F) { if (processRegion(LRFI->getTopLevelRegion(), false, false)) { // We recompute the final post dom release since we may have moved the final // post dominated releases. processRegion(LRFI->getTopLevelRegion(), true, true); } } bool LoopARCPairingContext::processRegion(const LoopRegion *Region, bool FreezePostDomReleases, bool RecomputePostDomReleases) { llvm::SmallVector DeadInsts; // We have already summarized all subloops of this loop. Now summarize our // blocks so that we only visit interesting instructions. Evaluator.summarizeSubregionBlocks(Region); bool MadeChange = false; bool NestingDetected = false; bool MatchedPair = false; do { NestingDetected = Evaluator.runOnLoop(Region, FreezePostDomReleases, RecomputePostDomReleases); MatchedPair = Context.performMatching(DeadInsts); if (!DeadInsts.empty()) { LLVM_DEBUG(llvm::dbgs() << "Removing dead interesting insts!\n"); do { SILInstruction *I = DeadInsts.pop_back_val(); LLVM_DEBUG(llvm::dbgs() << " " << *I); Evaluator.removeInterestingInst(I); I->eraseFromParent(); } while (!DeadInsts.empty()); } MadeChange |= MatchedPair; Evaluator.saveMatchingInfo(Region); Evaluator.clearLoopState(Region); Context.DecToIncStateMap.clear(); Context.IncToDecStateMap.clear(); Evaluator.clearSetFactory(); // This ensures we only ever recompute post dominating releases on the first // iteration. RecomputePostDomReleases = false; } while (NestingDetected && MatchedPair); return MadeChange; } //===----------------------------------------------------------------------===// // Non Loop Optimizer //===----------------------------------------------------------------------===// static bool processFunctionWithoutLoopSupport(SILFunction &F, bool FreezePostDomReleases, AliasAnalysis *AA, PostOrderAnalysis *POTA, RCIdentityFunctionInfo *RCIA, EpilogueARCFunctionInfo *EAFI, ProgramTerminationFunctionInfo *PTFI) { // GlobalARCOpts seems to be taking up a lot of compile time when running on // globalinit_func. Since that is not *that* interesting from an ARC // perspective (i.e. no ref count operations in a loop), disable it on such // functions temporarily in order to unblock others. This should be removed. if (F.isGlobalInitOnceFunction()) return false; LLVM_DEBUG(llvm::dbgs() << "***** Processing " << F.getName() << " *****\n"); bool Changed = false; BlockARCPairingContext Context(F, AA, POTA, RCIA, EAFI, PTFI); // Until we do not remove any instructions or have nested increments, // decrements... while (true) { // Compute matching sets of increments, decrements, and their insertion // points. // // We need to blot pointers we remove after processing an individual pointer // so we don't process pairs after we have paired them up. Thus we pass in a // lambda that performs the work for us. bool ShouldRunAgain = Context.run(FreezePostDomReleases); Changed |= Context.madeChange(); // If we did not remove any instructions or have any nested increments, do // not perform another iteration. if (!ShouldRunAgain) break; // Otherwise, perform another iteration. LLVM_DEBUG(llvm::dbgs() << "\n<<< Made a Change! " "Reprocessing Function! >>>\n"); } LLVM_DEBUG(llvm::dbgs() << "\n"); // Return true if we moved or deleted any instructions. return Changed; } //===----------------------------------------------------------------------===// // Loop Optimizer //===----------------------------------------------------------------------===// static bool processFunctionWithLoopSupport( SILFunction &F, AliasAnalysis *AA, PostOrderAnalysis *POTA, LoopRegionFunctionInfo *LRFI, SILLoopInfo *LI, RCIdentityFunctionInfo *RCFI, EpilogueARCFunctionInfo *EAFI, ProgramTerminationFunctionInfo *PTFI) { // GlobalARCOpts seems to be taking up a lot of compile time when running on // globalinit_func. Since that is not *that* interesting from an ARC // perspective (i.e. no ref count operations in a loop), disable it on such // functions temporarily in order to unblock others. This should be removed. if (F.isGlobalInitOnceFunction()) return false; LLVM_DEBUG(llvm::dbgs() << "***** Processing " << F.getName() << " *****\n"); LoopARCPairingContext Context(F, AA, LRFI, LI, RCFI, EAFI, PTFI); return Context.process(); } //===----------------------------------------------------------------------===// // Top Level Driver //===----------------------------------------------------------------------===// namespace { class ARCSequenceOpts : public SILFunctionTransform { /// The entry point to the transformation. void run() override { auto *F = getFunction(); // If ARC optimizations are disabled, don't optimize anything and bail. if (!getOptions().EnableARCOptimizations) return; // FIXME: We should support ownership. if (F->hasOwnership()) return; if (!EnableLoopARC) { auto *AA = getAnalysis(F); auto *POTA = getAnalysis(); auto *RCFI = getAnalysis()->get(F); auto *EAFI = getAnalysis()->get(F); ProgramTerminationFunctionInfo PTFI(F); if (processFunctionWithoutLoopSupport(*F, false, AA, POTA, RCFI, EAFI, &PTFI)) { processFunctionWithoutLoopSupport(*F, true, AA, POTA, RCFI, EAFI, &PTFI); invalidateAnalysis(SILAnalysis::InvalidationKind::CallsAndInstructions); } return; } auto *LA = getAnalysis(); auto *LI = LA->get(F); auto *DA = getAnalysis(); auto *DI = DA->get(F); // Canonicalize the loops, invalidating if we need to. if (canonicalizeAllLoops(DI, LI)) { // We preserve loop info and the dominator tree. DA->lockInvalidation(); LA->lockInvalidation(); PM->invalidateAnalysis(F, SILAnalysis::InvalidationKind::FunctionBody); DA->unlockInvalidation(); LA->unlockInvalidation(); } auto *AA = getAnalysis(F); auto *POTA = getAnalysis(); auto *RCFI = getAnalysis()->get(F); auto *EAFI = getAnalysis()->get(F); auto *LRFI = getAnalysis()->get(F); ProgramTerminationFunctionInfo PTFI(F); if (processFunctionWithLoopSupport(*F, AA, POTA, LRFI, LI, RCFI, EAFI, &PTFI)) { invalidateAnalysis(SILAnalysis::InvalidationKind::CallsAndInstructions); } } }; } // end anonymous namespace SILTransform *swift::createARCSequenceOpts() { return new ARCSequenceOpts(); }