//===--- LICM.cpp - Loop invariant code motion ----------------------------===// // // 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-licm" #include "swift/SIL/Dominance.h" #include "swift/SILOptimizer/Analysis/AliasAnalysis.h" #include "swift/SILOptimizer/Analysis/Analysis.h" #include "swift/SILOptimizer/Analysis/DominanceAnalysis.h" #include "swift/SILOptimizer/Analysis/LoopAnalysis.h" #include "swift/SILOptimizer/Analysis/ArraySemantic.h" #include "swift/SILOptimizer/Analysis/SideEffectAnalysis.h" #include "swift/SILOptimizer/PassManager/Passes.h" #include "swift/SILOptimizer/PassManager/Transforms.h" #include "swift/SILOptimizer/Utils/CFG.h" #include "swift/SILOptimizer/Utils/SILSSAUpdater.h" #include "swift/SILOptimizer/Utils/Local.h" #include "swift/SIL/SILArgument.h" #include "swift/SIL/SILBuilder.h" #include "swift/SIL/SILInstruction.h" #include "swift/SIL/InstructionUtils.h" #include "llvm/ADT/DepthFirstIterator.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/Support/Debug.h" #include "llvm/Support/CommandLine.h" using namespace swift; /// Instructions which read from memory, e.g. loads, or function calls without /// side effects. using ReadSet = llvm::SmallPtrSet; /// Instructions which (potentially) write memory. using WriteSet = SmallVector; /// Returns true if the \p MayWrites set contains any memory writes which may /// alias with the memory addressed by \a LI. static bool mayWriteTo(AliasAnalysis *AA, WriteSet &MayWrites, LoadInst *LI) { for (auto *W : MayWrites) if (AA->mayWriteToMemory(W, LI->getOperand())) { DEBUG(llvm::dbgs() << " mayWriteTo\n" << *W << " to " << *LI << "\n"); return true; } return false; } /// Returns true if the \p MayWrites set contains any memory writes which may /// alias with any memory which is read by \p AI. static bool mayWriteTo(AliasAnalysis *AA, SideEffectAnalysis *SEA, WriteSet &MayWrites, ApplyInst *AI) { SideEffectAnalysis::FunctionEffects E; SEA->getEffects(E, AI); assert(E.getMemBehavior(RetainObserveKind::IgnoreRetains) <= SILInstruction::MemoryBehavior::MayRead && "apply should only read from memory"); if (E.getGlobalEffects().mayRead() && !MayWrites.empty()) { // We don't know which memory is read in the callee. Therefore we bail if // there are any writes in the loop. return true; } for (unsigned Idx = 0, End = AI->getNumArguments(); Idx < End; ++Idx) { auto &ArgEffect = E.getParameterEffects()[Idx]; assert(!ArgEffect.mayRelease() && "apply should only read from memory"); if (!ArgEffect.mayRead()) continue; SILValue Arg = AI->getArgument(Idx); // Check if the memory addressed by the argument may alias any writes. for (auto *W : MayWrites) { if (AA->mayWriteToMemory(W, Arg)) { DEBUG(llvm::dbgs() << " mayWriteTo\n" << *W << " to " << *AI << "\n"); return true; } } } return false; } static void removeWrittenTo(AliasAnalysis *AA, ReadSet &Reads, SILInstruction *ByInst) { // We can ignore retains, cond_fails, and dealloc_stacks. if (isa(ByInst) || isa(ByInst) || isa(ByInst) || isa(ByInst)) return; SmallVector RS(Reads.begin(), Reads.end()); for (auto R : RS) { auto *LI = dyn_cast(R); if (LI && !AA->mayWriteToMemory(ByInst, LI->getOperand())) continue; DEBUG(llvm::dbgs() << " mayWriteTo\n" << *ByInst << " to " << *R << "\n"); Reads.erase(R); } } static bool hasLoopInvariantOperands(SILInstruction *I, SILLoop *L) { auto Opds = I->getAllOperands(); return std::all_of(Opds.begin(), Opds.end(), [=](Operand &Op) { ValueBase *Def = Op.get(); // Operand is defined outside the loop. if (auto *Inst = Def->getDefiningInstruction()) return !L->contains(Inst->getParent()); if (auto *Arg = dyn_cast(Def)) return !L->contains(Arg->getParent()); return false; }); } /// Check if an address does not depend on other values in a basic block. static SILInstruction *addressIndependent(SILValue Addr) { Addr = stripCasts(Addr); if (auto *SGAI = dyn_cast(Addr)) return SGAI; if (auto *SEAI = dyn_cast(Addr)) return addressIndependent(SEAI->getOperand()); return nullptr; } /// Check if two addresses can potentially access the same memory. /// For now, return true when both can be traced to the same global variable. static bool addressCanPairUp(SILValue Addr1, SILValue Addr2) { SILInstruction *Origin1 = addressIndependent(Addr1); return Origin1 && Origin1 == addressIndependent(Addr2); } /// Move cond_fail down if it can potentially help register promotion later. static bool sinkCondFail(SILLoop *Loop) { // Only handle innermost loops for now. if (!Loop->getSubLoops().empty()) return false; bool Changed = false; for (auto *BB : Loop->getBlocks()) { // A list of CondFails that can be moved down. SmallVector CFs; // A pair of load and store that are independent of the CondFails and // can potentially access the same memory. LoadInst *LIOfPair = nullptr; bool foundPair = false; for (auto &Inst : *BB) { if (foundPair) { // Move CFs to right before Inst. for (unsigned I = 0, E = CFs.size(); I < E; I++) { DEBUG(llvm::dbgs() << "sinking cond_fail down "); DEBUG(CFs[I]->dump()); DEBUG(llvm::dbgs() << " before "); DEBUG(Inst.dump()); CFs[I]->moveBefore(&Inst); } Changed = true; foundPair = false; LIOfPair = nullptr; } if (auto CF = dyn_cast(&Inst)) { CFs.push_back(CF); } else if (auto LI = dyn_cast(&Inst)) { if (addressIndependent(LI->getOperand())) { LIOfPair = LI; } else { CFs.clear(); LIOfPair = nullptr; } } else if (auto SI = dyn_cast(&Inst)) { if (addressIndependent(SI->getDest())) { if (LIOfPair && addressCanPairUp(SI->getDest(), LIOfPair->getOperand())) foundPair = true; } else { CFs.clear(); LIOfPair = nullptr; } } else if (Inst.mayHaveSideEffects()) { CFs.clear(); LIOfPair = nullptr; } } } return Changed; } /// Checks if \p Inst has no side effects which prevent hoisting. /// The \a SafeReads set contain instructions which we already proved to have /// no such side effects. static bool hasNoSideEffect(SILInstruction *Inst, ReadSet &SafeReads) { // We can (and must) hoist cond_fail instructions if the operand is // invariant. We must hoist them so that we preserve memory safety. A // cond_fail that would have protected (executed before) a memory access // must - after hoisting - also be executed before said access. if (isa(Inst)) return true; // Can't hoist if the instruction could read from memory and is not marked // as safe. if (SafeReads.count(Inst)) return true; if (Inst->getMemoryBehavior() == SILInstruction::MemoryBehavior::None) return true; return false; } static bool canHoistInstruction(SILInstruction *Inst, SILLoop *Loop, ReadSet &SafeReads) { // Can't hoist terminators. if (isa(Inst)) return false; // Can't hoist allocation and dealloc stacks. if (isa(Inst) || isa(Inst)) return false; // Can't hoist instructions which may have side effects. if (!hasNoSideEffect(Inst, SafeReads)) return false; // The operands need to be loop invariant. if (!hasLoopInvariantOperands(Inst, Loop)) { DEBUG(llvm::dbgs() << " loop variant operands\n"); return false; } return true; } static bool hoistInstructions(SILLoop *Loop, DominanceInfo *DT, ReadSet &SafeReads, bool RunsOnHighLevelSil) { auto Preheader = Loop->getLoopPreheader(); if (!Preheader) return false; DEBUG(llvm::dbgs() << " Hoisting instructions.\n"); auto HeaderBB = Loop->getHeader(); bool Changed = false; // Traverse the dominator tree starting at the loop header. Hoisting // instructions as we go. auto DTRoot = DT->getNode(HeaderBB); SmallVector ExitingBBs; Loop->getExitingBlocks(ExitingBBs); for (llvm::df_iterator It = llvm::df_begin(DTRoot), E = llvm::df_end(DTRoot); It != E;) { auto *CurBB = It->getBlock(); // Don't decent into control-dependent code. Only traverse into basic blocks // that dominate all exits. if (!std::all_of(ExitingBBs.begin(), ExitingBBs.end(), [=](SILBasicBlock *ExitBB) { return DT->dominates(CurBB, ExitBB); })) { DEBUG(llvm::dbgs() << " skipping conditional block " << *CurBB << "\n"); It.skipChildren(); continue; } // We now that the block is guaranteed to be executed. Hoist if we can. for (auto InstIt = CurBB->begin(), E = CurBB->end(); InstIt != E; ) { SILInstruction *Inst = &*InstIt; ++InstIt; DEBUG(llvm::dbgs() << " looking at " << *Inst); if (canHoistInstruction(Inst, Loop, SafeReads)) { DEBUG(llvm::dbgs() << " hoisting to preheader.\n"); Changed = true; Inst->moveBefore(Preheader->getTerminator()); } else if (RunsOnHighLevelSil) { ArraySemanticsCall semCall(Inst); switch (semCall.getKind()) { case ArrayCallKind::kGetCount: case ArrayCallKind::kGetCapacity: if (hasLoopInvariantOperands(Inst, Loop) && semCall.canHoist(Preheader->getTerminator(), DT)) { Changed = true; semCall.hoist(Preheader->getTerminator(), DT); } break; default: break; } } } // Next block in dominator tree. ++It; } return Changed; } static bool sinkFixLifetime(SILLoop *Loop, DominanceInfo *DomTree, SILLoopInfo *LI) { DEBUG(llvm::errs() << " Sink fix_lifetime attempt\n"); auto Preheader = Loop->getLoopPreheader(); if (!Preheader) return false; // Only handle innermost loops for now. if (!Loop->getSubLoops().empty()) return false; // Only handle single exit blocks for now. auto *ExitBB = Loop->getExitBlock(); if (!ExitBB) return false; auto *ExitingBB = Loop->getExitingBlock(); if (!ExitingBB) return false; // We can sink fix_lifetime instructions if there are no reference counting // instructions in the loop. SmallVector FixLifetimeInsts; for (auto *BB : Loop->getBlocks()) { for (auto &Inst : *BB) { if (auto FLI = dyn_cast(&Inst)) { FixLifetimeInsts.push_back(FLI); } else if (Inst.mayHaveSideEffects() && !isa(&Inst) && !isa(&Inst)) { DEBUG(llvm::errs() << " mayhavesideeffects because of" << Inst); DEBUG(Inst.getParent()->dump()); return false; } } } // Sink the fix_lifetime instruction. bool Changed = false; for (auto *FLI : FixLifetimeInsts) if (DomTree->dominates(FLI->getOperand()->getParentBlock(), Preheader)) { auto Succs = ExitingBB->getSuccessors(); for (unsigned EdgeIdx = 0; EdgeIdx < Succs.size(); ++EdgeIdx) { SILBasicBlock *BB = Succs[EdgeIdx]; if (BB == ExitBB) { auto *SplitBB = splitCriticalEdge(ExitingBB->getTerminator(), EdgeIdx, DomTree, LI); auto *OutsideBB = SplitBB ? SplitBB : ExitBB; // Update the ExitBB. ExitBB = OutsideBB; DEBUG(llvm::errs() << " moving fix_lifetime to exit BB " << *FLI); FLI->moveBefore(&*OutsideBB->begin()); Changed = true; } } } else { DEBUG(llvm::errs() << " does not dominate " << *FLI); } return Changed; } namespace { /// \brief Summary of may writes occurring in the loop tree rooted at \p /// Loop. This includes all writes of the sub loops and the loop itself. struct LoopNestSummary { SILLoop *Loop; WriteSet MayWrites; LoopNestSummary(SILLoop *Curr) : Loop(Curr) {} void copySummary(LoopNestSummary &Other) { MayWrites.append(Other.MayWrites.begin(), Other.MayWrites.end()); } LoopNestSummary(const LoopNestSummary &) = delete; LoopNestSummary &operator=(const LoopNestSummary &) = delete; LoopNestSummary(LoopNestSummary &&) = delete; }; /// \brief Optimize the loop tree bottom up propagating loop's summaries up the /// loop tree. class LoopTreeOptimization { llvm::DenseMap> LoopNestSummaryMap; SmallVector BotUpWorkList; SILLoopInfo *LoopInfo; AliasAnalysis *AA; SideEffectAnalysis *SEA; DominanceInfo *DomTree; bool Changed; /// True if LICM is done on high-level SIL, i.e. semantic calls are not /// inlined yet. In this case some semantic calls can be hoisted. bool RunsOnHighLevelSil; public: LoopTreeOptimization(SILLoop *TopLevelLoop, SILLoopInfo *LI, AliasAnalysis *AA, SideEffectAnalysis *SEA, DominanceInfo *DT, bool RunsOnHighLevelSil) : LoopInfo(LI), AA(AA), SEA(SEA), DomTree(DT), Changed(false), RunsOnHighLevelSil(RunsOnHighLevelSil) { // Collect loops for a recursive bottom-up traversal in the loop tree. BotUpWorkList.push_back(TopLevelLoop); for (unsigned i = 0; i < BotUpWorkList.size(); ++i) { auto *L = BotUpWorkList[i]; for (auto *SubLoop : *L) BotUpWorkList.push_back(SubLoop); } } /// \brief Optimize this loop tree. bool optimize(); protected: /// \brief Propagate the sub-loops' summaries up to the current loop. void propagateSummaries(std::unique_ptr &CurrSummary); /// \brief Collect a set of reads that can be hoisted to the loop's preheader. void analyzeCurrentLoop(std::unique_ptr &CurrSummary, ReadSet &SafeReads); /// \brief Optimize the current loop nest. void optimizeLoop(SILLoop *CurrentLoop, ReadSet &SafeReads); }; } // end anonymous namespace bool LoopTreeOptimization::optimize() { // Process loops bottom up in the loop tree. while (!BotUpWorkList.empty()) { SILLoop *CurrentLoop = BotUpWorkList.pop_back_val(); DEBUG(llvm::dbgs() << "Processing loop " << *CurrentLoop); // Collect all summary of all sub loops of the current loop. Since we // process the loop tree bottom up they are guaranteed to be available in // the map. auto CurrLoopSummary = llvm::make_unique(CurrentLoop); propagateSummaries(CurrLoopSummary); // Analyze the current loop for reads that can be hoisted. ReadSet SafeReads; analyzeCurrentLoop(CurrLoopSummary, SafeReads); optimizeLoop(CurrentLoop, SafeReads); // Store the summary for parent loops to use. LoopNestSummaryMap[CurrentLoop] = std::move(CurrLoopSummary); } return Changed; } void LoopTreeOptimization::propagateSummaries( std::unique_ptr &CurrSummary) { for (auto *SubLoop : *CurrSummary->Loop) { assert(LoopNestSummaryMap.count(SubLoop) && "Must have data for sub loops"); CurrSummary->copySummary(*LoopNestSummaryMap[SubLoop]); LoopNestSummaryMap.erase(SubLoop); } } void LoopTreeOptimization::analyzeCurrentLoop( std::unique_ptr &CurrSummary, ReadSet &SafeReads) { WriteSet &MayWrites = CurrSummary->MayWrites; SILLoop *Loop = CurrSummary->Loop; DEBUG(llvm::dbgs() << " Analyzing accesses.\n"); // Contains function calls in the loop, which only read from memory. SmallVector ReadOnlyApplies; for (auto *BB : Loop->getBlocks()) { for (auto &Inst : *BB) { // Ignore fix_lifetime instructions. if (isa(&Inst)) continue; // Collect loads. auto LI = dyn_cast(&Inst); if (LI) { if (!mayWriteTo(AA, MayWrites, LI)) SafeReads.insert(LI); continue; } if (auto *AI = dyn_cast(&Inst)) { // In contrast to load instructions, we first collect all read-only // function calls and add them later to SafeReads. SideEffectAnalysis::FunctionEffects E; SEA->getEffects(E, AI); auto MB = E.getMemBehavior(RetainObserveKind::ObserveRetains); if (MB <= SILInstruction::MemoryBehavior::MayRead) ReadOnlyApplies.push_back(AI); } if (Inst.mayHaveSideEffects()) { MayWrites.push_back(&Inst); // Remove clobbered loads we have seen before. removeWrittenTo(AA, SafeReads, &Inst); } } } for (auto *AI : ReadOnlyApplies) { if (!mayWriteTo(AA, SEA, MayWrites, AI)) SafeReads.insert(AI); } } void LoopTreeOptimization::optimizeLoop(SILLoop *CurrentLoop, ReadSet &SafeReads) { Changed |= sinkCondFail(CurrentLoop); Changed |= hoistInstructions(CurrentLoop, DomTree, SafeReads, RunsOnHighLevelSil); Changed |= sinkFixLifetime(CurrentLoop, DomTree, LoopInfo); } namespace { /// Hoist loop invariant code out of innermost loops. /// /// Transforms are identified by type, not instance. Split this /// Into two types: "High-level Loop Invariant Code Motion" /// and "Loop Invariant Code Motion". class LICM : public SILFunctionTransform { public: LICM(bool RunsOnHighLevelSil) : RunsOnHighLevelSil(RunsOnHighLevelSil) {} /// True if LICM is done on high-level SIL, i.e. semantic calls are not /// inlined yet. In this case some semantic calls can be hoisted. /// We only hoist semantic calls on high-level SIL because we can be sure that /// e.g. an Array as SILValue is really immutable (including its content). bool RunsOnHighLevelSil; void run() override { SILFunction *F = getFunction(); SILLoopAnalysis *LA = PM->getAnalysis(); SILLoopInfo *LoopInfo = LA->get(F); if (LoopInfo->empty()) { DEBUG(llvm::dbgs() << "No loops in " << F->getName() << "\n"); return; } DominanceAnalysis *DA = PM->getAnalysis(); AliasAnalysis *AA = PM->getAnalysis(); SideEffectAnalysis *SEA = PM->getAnalysis(); DominanceInfo *DomTree = nullptr; DEBUG(llvm::dbgs() << "Processing loops in " << F->getName() << "\n"); bool Changed = false; for (auto *TopLevelLoop : *LoopInfo) { if (!DomTree) DomTree = DA->get(F); LoopTreeOptimization Opt(TopLevelLoop, LoopInfo, AA, SEA, DomTree, RunsOnHighLevelSil); Changed |= Opt.optimize(); } if (Changed) { LA->lockInvalidation(); DA->lockInvalidation(); PM->invalidateAnalysis(F, SILAnalysis::InvalidationKind::FunctionBody); LA->unlockInvalidation(); DA->unlockInvalidation(); } } }; } // end anonymous namespace SILTransform *swift::createLICM() { return new LICM(false); } SILTransform *swift::createHighLevelLICM() { return new LICM(true); }