void LoopTreeOptimization::analyzeCurrentLoop( std::unique_ptr<LoopNestSummary> &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<ApplyInst *, 8> ReadOnlyApplies; for (auto *BB : Loop->getBlocks()) { for (auto &Inst : *BB) { // Ignore fix_lifetime instructions. if (isa<FixLifetimeInst>(&Inst)) continue; // Collect loads. auto LI = dyn_cast<LoadInst>(&Inst); if (LI) { if (!mayWriteTo(AA, MayWrites, LI)) SafeReads.insert(LI); continue; } if (auto *AI = dyn_cast<ApplyInst>(&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); } }
// 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); } } }