// Analyzes current loop for hosting/sinking potential: // Computes set of instructions we may be able to move out of the loop // Important Note: // We can't bail out of this method! we have to run it on all loops. // We *need* to discover all MayWrites - // even if the loop is otherwise skipped! // This is because outer loops will depend on the inner loop's writes. void LoopTreeOptimization::analyzeCurrentLoop( std::unique_ptr<LoopNestSummary> &CurrSummary) { WriteSet &MayWrites = CurrSummary->MayWrites; SILLoop *Loop = CurrSummary->Loop; LLVM_DEBUG(llvm::dbgs() << " Analyzing accesses.\n"); // Contains function calls in the loop, which only read from memory. SmallVector<ApplyInst *, 8> ReadOnlyApplies; // Contains Loads inside the loop. SmallVector<LoadInst *, 8> Loads; // Contains fix_lifetime, we might be able to sink them. SmallVector<FixLifetimeInst *, 8> FixLifetimes; // Contains begin_access, we might be able to hoist them. SmallVector<BeginAccessInst *, 8> BeginAccesses; // Contains all applies - used for begin_access SmallVector<FullApplySite, 8> fullApplies; for (auto *BB : Loop->getBlocks()) { for (auto &Inst : *BB) { switch (Inst.getKind()) { case SILInstructionKind::FixLifetimeInst: { auto *FL = dyn_cast<FixLifetimeInst>(&Inst); assert(FL && "Expected a FixLifetime instruction"); FixLifetimes.push_back(FL); // We can ignore the side effects of FixLifetimes break; } case SILInstructionKind::LoadInst: { auto *LI = dyn_cast<LoadInst>(&Inst); assert(LI && "Expected a Load instruction"); Loads.push_back(LI); break; } case SILInstructionKind::BeginAccessInst: { auto *BI = dyn_cast<BeginAccessInst>(&Inst); assert(BI && "Expected a Begin Access"); BeginAccesses.push_back(BI); checkSideEffects(Inst, MayWrites); break; } case SILInstructionKind::RefElementAddrInst: { auto *REA = static_cast<RefElementAddrInst *>(&Inst); SpecialHoist.push_back(REA); break; } case swift::SILInstructionKind::CondFailInst: { // 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. HoistUp.insert(&Inst); checkSideEffects(Inst, MayWrites); break; } case SILInstructionKind::ApplyInst: { auto *AI = dyn_cast<ApplyInst>(&Inst); assert(AI && "Expected an Apply Instruction"); if (isSafeReadOnlyApply(SEA, AI)) { ReadOnlyApplies.push_back(AI); } // check for array semantics and side effects - same as default LLVM_FALLTHROUGH; } default: { if (auto fullApply = FullApplySite::isa(&Inst)) { fullApplies.push_back(fullApply); } checkSideEffects(Inst, MayWrites); if (canHoistUpDefault(&Inst, Loop, DomTree, RunsOnHighLevelSIL)) { HoistUp.insert(&Inst); } break; } } } } auto *Preheader = Loop->getLoopPreheader(); if (!Preheader) { // Can't hoist/sink instructions return; } for (auto *AI : ReadOnlyApplies) { if (!mayWriteTo(AA, SEA, MayWrites, AI)) { HoistUp.insert(AI); } } for (auto *LI : Loads) { if (!mayWriteTo(AA, MayWrites, LI)) { HoistUp.insert(LI); } } bool mayWritesMayRelease = std::any_of(MayWrites.begin(), MayWrites.end(), [&](SILInstruction *W) { return W->mayRelease(); }); for (auto *FL : FixLifetimes) { if (!DomTree->dominates(FL->getOperand()->getParentBlock(), Preheader)) { continue; } if (!mayWriteTo(AA, MayWrites, FL) || !mayWritesMayRelease) { SinkDown.push_back(FL); } } for (auto *BI : BeginAccesses) { if (!handledEndAccesses(BI, Loop)) { LLVM_DEBUG(llvm::dbgs() << "Skipping: " << *BI); LLVM_DEBUG(llvm::dbgs() << "Some end accesses can't be handled\n"); continue; } if (analyzeBeginAccess(BI, BeginAccesses, fullApplies, MayWrites, ASA, DomTree)) { SpecialHoist.push_back(BI); } } }
// Attempt to insert a new access in the loop preheader. If successful, insert // the new access in DominatedAccessAnalysis so it can be used to dominate other // accesses. Also convert the current access to static and update the current // storageToDomMap since the access may already have been recorded (when it was // still dynamic). // // This function cannot add or remove instructions in the current block, but // may add instructions to the current loop's preheader. // // The required conditions for inserting a new dominating access are: // // 1. The new preheader access is not enclosed in another scope that doesn't // also enclose the current scope. // // This is inferred from the loop structure; any scope that encloses the // preheader must also enclose the entire loop. // // 2. The current access is not enclosed in another scope that doesn't also // enclose the preheader. // // As before, it is sufficient to check this access' isInner flags in // DominatedAccessAnalysis; if this access isn't enclosed by any scope within // the function, then it can't be enclosed within a scope inside the loop. // // 3. The current header has no nested conflict within its scope. // // 4. The access' source operand is available in the loop preheader. void DominatedAccessRemoval::tryInsertLoopPreheaderAccess( BeginAccessInst *BAI, DomAccessedStorage currAccessInfo) { // 2. the current access may be enclosed. if (currAccessInfo.isInner()) return; // 3. the current access must be instantaneous. if (!BAI->hasNoNestedConflict()) return; SILLoop *currLoop = loopInfo->getLoopFor(BAI->getParent()); if (!currLoop) return; SILBasicBlock *preheader = currLoop->getLoopPreheader(); if (!preheader) return; // 4. The source operand must be available in the preheader. auto sourceOperand = BAI->getOperand(); auto *sourceBB = sourceOperand->getParentBlock(); if (!domInfo->dominates(sourceBB, preheader)) return; // Insert a new access scope immediately before the // preheader's terminator. TermInst *preheaderTerm = preheader->getTerminator(); SILBuilderWithScope scopeBuilder(preheaderTerm); BeginAccessInst *newBegin = scopeBuilder.createBeginAccess( preheaderTerm->getLoc(), sourceOperand, BAI->getAccessKind(), SILAccessEnforcement::Dynamic, true /*no nested conflict*/, BAI->isFromBuiltin()); scopeBuilder.createEndAccess(preheaderTerm->getLoc(), newBegin, false); LLVM_DEBUG(llvm::dbgs() << "Created loop preheader access: " << *newBegin << "\n" << "dominating: " << *BAI << "\n"); BAI->setEnforcement(SILAccessEnforcement::Static); hasChanged = true; // Insert the new dominating instruction in both DominatedAccessAnalysis and // storageToDomMap if it has uniquely identifiable storage. if (!currAccessInfo.isUniquelyIdentifiedOrClass()) return; AccessedStorage storage = static_cast<AccessedStorage>(currAccessInfo); storage.resetSubclassData(); // Create a DomAccessedStorage for the new access with no flags set. DAA.accessMap.try_emplace(newBegin, DomAccessedStorage(storage)); // Track the new access as long as no other accesses from the same storage are // already tracked. This also necessarily replaces the current access, which // was just made static. DominatingAccess newDomAccess(newBegin, domInfo->getNode(preheader)); auto iterAndInserted = storageToDomMap.try_emplace(storage, newDomAccess); if (!iterAndInserted.second) { DominatingAccess &curDomAccess = iterAndInserted.first->second; if (curDomAccess.beginAccess == BAI) curDomAccess = newDomAccess; } }