106c3fb27SDimitry Andric //===-- MoveAutoInit.cpp - move auto-init inst closer to their use site----===// 206c3fb27SDimitry Andric // 306c3fb27SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 406c3fb27SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 506c3fb27SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 606c3fb27SDimitry Andric // 706c3fb27SDimitry Andric //===----------------------------------------------------------------------===// 806c3fb27SDimitry Andric // 906c3fb27SDimitry Andric // This pass moves instruction maked as auto-init closer to the basic block that 1006c3fb27SDimitry Andric // use it, eventually removing it from some control path of the function. 1106c3fb27SDimitry Andric // 1206c3fb27SDimitry Andric //===----------------------------------------------------------------------===// 1306c3fb27SDimitry Andric 1406c3fb27SDimitry Andric #include "llvm/Transforms/Utils/MoveAutoInit.h" 1506c3fb27SDimitry Andric #include "llvm/ADT/STLExtras.h" 1606c3fb27SDimitry Andric #include "llvm/ADT/Statistic.h" 1706c3fb27SDimitry Andric #include "llvm/Analysis/MemorySSA.h" 1806c3fb27SDimitry Andric #include "llvm/Analysis/MemorySSAUpdater.h" 1906c3fb27SDimitry Andric #include "llvm/Analysis/ValueTracking.h" 2006c3fb27SDimitry Andric #include "llvm/IR/DebugInfo.h" 2106c3fb27SDimitry Andric #include "llvm/IR/Dominators.h" 2206c3fb27SDimitry Andric #include "llvm/IR/IRBuilder.h" 2306c3fb27SDimitry Andric #include "llvm/IR/Instructions.h" 2406c3fb27SDimitry Andric #include "llvm/IR/IntrinsicInst.h" 2506c3fb27SDimitry Andric #include "llvm/Support/CommandLine.h" 2606c3fb27SDimitry Andric #include "llvm/Transforms/Utils.h" 2706c3fb27SDimitry Andric #include "llvm/Transforms/Utils/LoopUtils.h" 2806c3fb27SDimitry Andric 2906c3fb27SDimitry Andric using namespace llvm; 3006c3fb27SDimitry Andric 3106c3fb27SDimitry Andric #define DEBUG_TYPE "move-auto-init" 3206c3fb27SDimitry Andric 3306c3fb27SDimitry Andric STATISTIC(NumMoved, "Number of instructions moved"); 3406c3fb27SDimitry Andric 3506c3fb27SDimitry Andric static cl::opt<unsigned> MoveAutoInitThreshold( 3606c3fb27SDimitry Andric "move-auto-init-threshold", cl::Hidden, cl::init(128), 3706c3fb27SDimitry Andric cl::desc("Maximum instructions to analyze per moved initialization")); 3806c3fb27SDimitry Andric 3906c3fb27SDimitry Andric static bool hasAutoInitMetadata(const Instruction &I) { 4006c3fb27SDimitry Andric return I.hasMetadata(LLVMContext::MD_annotation) && 4106c3fb27SDimitry Andric any_of(I.getMetadata(LLVMContext::MD_annotation)->operands(), 4206c3fb27SDimitry Andric [](const MDOperand &Op) { return Op.equalsStr("auto-init"); }); 4306c3fb27SDimitry Andric } 4406c3fb27SDimitry Andric 4506c3fb27SDimitry Andric static std::optional<MemoryLocation> writeToAlloca(const Instruction &I) { 4606c3fb27SDimitry Andric MemoryLocation ML; 4706c3fb27SDimitry Andric if (auto *MI = dyn_cast<MemIntrinsic>(&I)) 4806c3fb27SDimitry Andric ML = MemoryLocation::getForDest(MI); 4906c3fb27SDimitry Andric else if (auto *SI = dyn_cast<StoreInst>(&I)) 5006c3fb27SDimitry Andric ML = MemoryLocation::get(SI); 5106c3fb27SDimitry Andric else 525f757f3fSDimitry Andric return std::nullopt; 5306c3fb27SDimitry Andric 5406c3fb27SDimitry Andric if (isa<AllocaInst>(getUnderlyingObject(ML.Ptr))) 5506c3fb27SDimitry Andric return ML; 5606c3fb27SDimitry Andric else 5706c3fb27SDimitry Andric return {}; 5806c3fb27SDimitry Andric } 5906c3fb27SDimitry Andric 6006c3fb27SDimitry Andric /// Finds a BasicBlock in the CFG where instruction `I` can be moved to while 6106c3fb27SDimitry Andric /// not changing the Memory SSA ordering and being guarded by at least one 6206c3fb27SDimitry Andric /// condition. 6306c3fb27SDimitry Andric static BasicBlock *usersDominator(const MemoryLocation &ML, Instruction *I, 6406c3fb27SDimitry Andric DominatorTree &DT, MemorySSA &MSSA) { 6506c3fb27SDimitry Andric BasicBlock *CurrentDominator = nullptr; 6606c3fb27SDimitry Andric MemoryUseOrDef &IMA = *MSSA.getMemoryAccess(I); 6706c3fb27SDimitry Andric BatchAAResults AA(MSSA.getAA()); 6806c3fb27SDimitry Andric 6906c3fb27SDimitry Andric SmallPtrSet<MemoryAccess *, 8> Visited; 7006c3fb27SDimitry Andric 7106c3fb27SDimitry Andric auto AsMemoryAccess = [](User *U) { return cast<MemoryAccess>(U); }; 7206c3fb27SDimitry Andric SmallVector<MemoryAccess *> WorkList(map_range(IMA.users(), AsMemoryAccess)); 7306c3fb27SDimitry Andric 7406c3fb27SDimitry Andric while (!WorkList.empty()) { 7506c3fb27SDimitry Andric MemoryAccess *MA = WorkList.pop_back_val(); 7606c3fb27SDimitry Andric if (!Visited.insert(MA).second) 7706c3fb27SDimitry Andric continue; 7806c3fb27SDimitry Andric 7906c3fb27SDimitry Andric if (Visited.size() > MoveAutoInitThreshold) 8006c3fb27SDimitry Andric return nullptr; 8106c3fb27SDimitry Andric 8206c3fb27SDimitry Andric bool FoundClobberingUser = false; 8306c3fb27SDimitry Andric if (auto *M = dyn_cast<MemoryUseOrDef>(MA)) { 8406c3fb27SDimitry Andric Instruction *MI = M->getMemoryInst(); 8506c3fb27SDimitry Andric 8606c3fb27SDimitry Andric // If this memory instruction may not clobber `I`, we can skip it. 8706c3fb27SDimitry Andric // LifetimeEnd is a valid user, but we do not want it in the user 8806c3fb27SDimitry Andric // dominator. 8906c3fb27SDimitry Andric if (AA.getModRefInfo(MI, ML) != ModRefInfo::NoModRef && 9006c3fb27SDimitry Andric !MI->isLifetimeStartOrEnd() && MI != I) { 9106c3fb27SDimitry Andric FoundClobberingUser = true; 9206c3fb27SDimitry Andric CurrentDominator = CurrentDominator 9306c3fb27SDimitry Andric ? DT.findNearestCommonDominator(CurrentDominator, 9406c3fb27SDimitry Andric MI->getParent()) 9506c3fb27SDimitry Andric : MI->getParent(); 9606c3fb27SDimitry Andric } 9706c3fb27SDimitry Andric } 9806c3fb27SDimitry Andric if (!FoundClobberingUser) { 9906c3fb27SDimitry Andric auto UsersAsMemoryAccesses = map_range(MA->users(), AsMemoryAccess); 10006c3fb27SDimitry Andric append_range(WorkList, UsersAsMemoryAccesses); 10106c3fb27SDimitry Andric } 10206c3fb27SDimitry Andric } 10306c3fb27SDimitry Andric return CurrentDominator; 10406c3fb27SDimitry Andric } 10506c3fb27SDimitry Andric 10606c3fb27SDimitry Andric static bool runMoveAutoInit(Function &F, DominatorTree &DT, MemorySSA &MSSA) { 10706c3fb27SDimitry Andric BasicBlock &EntryBB = F.getEntryBlock(); 10806c3fb27SDimitry Andric SmallVector<std::pair<Instruction *, BasicBlock *>> JobList; 10906c3fb27SDimitry Andric 11006c3fb27SDimitry Andric // 11106c3fb27SDimitry Andric // Compute movable instructions. 11206c3fb27SDimitry Andric // 11306c3fb27SDimitry Andric for (Instruction &I : EntryBB) { 11406c3fb27SDimitry Andric if (!hasAutoInitMetadata(I)) 11506c3fb27SDimitry Andric continue; 11606c3fb27SDimitry Andric 11706c3fb27SDimitry Andric std::optional<MemoryLocation> ML = writeToAlloca(I); 11806c3fb27SDimitry Andric if (!ML) 11906c3fb27SDimitry Andric continue; 12006c3fb27SDimitry Andric 12106c3fb27SDimitry Andric if (I.isVolatile()) 12206c3fb27SDimitry Andric continue; 12306c3fb27SDimitry Andric 12406c3fb27SDimitry Andric BasicBlock *UsersDominator = usersDominator(ML.value(), &I, DT, MSSA); 12506c3fb27SDimitry Andric if (!UsersDominator) 12606c3fb27SDimitry Andric continue; 12706c3fb27SDimitry Andric 12806c3fb27SDimitry Andric if (UsersDominator == &EntryBB) 12906c3fb27SDimitry Andric continue; 13006c3fb27SDimitry Andric 13106c3fb27SDimitry Andric // Traverse the CFG to detect cycles `UsersDominator` would be part of. 13206c3fb27SDimitry Andric SmallPtrSet<BasicBlock *, 8> TransitiveSuccessors; 13306c3fb27SDimitry Andric SmallVector<BasicBlock *> WorkList(successors(UsersDominator)); 13406c3fb27SDimitry Andric bool HasCycle = false; 13506c3fb27SDimitry Andric while (!WorkList.empty()) { 13606c3fb27SDimitry Andric BasicBlock *CurrBB = WorkList.pop_back_val(); 13706c3fb27SDimitry Andric if (CurrBB == UsersDominator) 13806c3fb27SDimitry Andric // No early exit because we want to compute the full set of transitive 13906c3fb27SDimitry Andric // successors. 14006c3fb27SDimitry Andric HasCycle = true; 14106c3fb27SDimitry Andric for (BasicBlock *Successor : successors(CurrBB)) { 14206c3fb27SDimitry Andric if (!TransitiveSuccessors.insert(Successor).second) 14306c3fb27SDimitry Andric continue; 14406c3fb27SDimitry Andric WorkList.push_back(Successor); 14506c3fb27SDimitry Andric } 14606c3fb27SDimitry Andric } 14706c3fb27SDimitry Andric 14806c3fb27SDimitry Andric // Don't insert if that could create multiple execution of I, 14906c3fb27SDimitry Andric // but we can insert it in the non back-edge predecessors, if it exists. 15006c3fb27SDimitry Andric if (HasCycle) { 15106c3fb27SDimitry Andric BasicBlock *UsersDominatorHead = UsersDominator; 15206c3fb27SDimitry Andric while (BasicBlock *UniquePredecessor = 15306c3fb27SDimitry Andric UsersDominatorHead->getUniquePredecessor()) 15406c3fb27SDimitry Andric UsersDominatorHead = UniquePredecessor; 15506c3fb27SDimitry Andric 15606c3fb27SDimitry Andric if (UsersDominatorHead == &EntryBB) 15706c3fb27SDimitry Andric continue; 15806c3fb27SDimitry Andric 15906c3fb27SDimitry Andric BasicBlock *DominatingPredecessor = nullptr; 16006c3fb27SDimitry Andric for (BasicBlock *Pred : predecessors(UsersDominatorHead)) { 16106c3fb27SDimitry Andric // If one of the predecessor of the dominator also transitively is a 16206c3fb27SDimitry Andric // successor, moving to the dominator would do the inverse of loop 16306c3fb27SDimitry Andric // hoisting, and we don't want that. 16406c3fb27SDimitry Andric if (TransitiveSuccessors.count(Pred)) 16506c3fb27SDimitry Andric continue; 16606c3fb27SDimitry Andric 167*7a6dacacSDimitry Andric if (!DT.isReachableFromEntry(Pred)) 168*7a6dacacSDimitry Andric continue; 169*7a6dacacSDimitry Andric 17006c3fb27SDimitry Andric DominatingPredecessor = 17106c3fb27SDimitry Andric DominatingPredecessor 17206c3fb27SDimitry Andric ? DT.findNearestCommonDominator(DominatingPredecessor, Pred) 17306c3fb27SDimitry Andric : Pred; 17406c3fb27SDimitry Andric } 17506c3fb27SDimitry Andric 17606c3fb27SDimitry Andric if (!DominatingPredecessor || DominatingPredecessor == &EntryBB) 17706c3fb27SDimitry Andric continue; 17806c3fb27SDimitry Andric 17906c3fb27SDimitry Andric UsersDominator = DominatingPredecessor; 18006c3fb27SDimitry Andric } 18106c3fb27SDimitry Andric 18206c3fb27SDimitry Andric // CatchSwitchInst blocks can only have one instruction, so they are not 18306c3fb27SDimitry Andric // good candidates for insertion. 184*7a6dacacSDimitry Andric while (isa<CatchSwitchInst>(UsersDominator->getFirstNonPHI())) { 18506c3fb27SDimitry Andric for (BasicBlock *Pred : predecessors(UsersDominator)) 186*7a6dacacSDimitry Andric if (DT.isReachableFromEntry(Pred)) 18706c3fb27SDimitry Andric UsersDominator = DT.findNearestCommonDominator(UsersDominator, Pred); 18806c3fb27SDimitry Andric } 18906c3fb27SDimitry Andric 19006c3fb27SDimitry Andric // We finally found a place where I can be moved while not introducing extra 19106c3fb27SDimitry Andric // execution, and guarded by at least one condition. 19206c3fb27SDimitry Andric if (UsersDominator != &EntryBB) 19306c3fb27SDimitry Andric JobList.emplace_back(&I, UsersDominator); 19406c3fb27SDimitry Andric } 19506c3fb27SDimitry Andric 19606c3fb27SDimitry Andric // 19706c3fb27SDimitry Andric // Perform the actual substitution. 19806c3fb27SDimitry Andric // 19906c3fb27SDimitry Andric if (JobList.empty()) 20006c3fb27SDimitry Andric return false; 20106c3fb27SDimitry Andric 20206c3fb27SDimitry Andric MemorySSAUpdater MSSAU(&MSSA); 20306c3fb27SDimitry Andric 20406c3fb27SDimitry Andric // Reverse insertion to respect relative order between instructions: 20506c3fb27SDimitry Andric // if two instructions are moved from the same BB to the same BB, we insert 20606c3fb27SDimitry Andric // the second one in the front, then the first on top of it. 20706c3fb27SDimitry Andric for (auto &Job : reverse(JobList)) { 2085f757f3fSDimitry Andric Job.first->moveBefore(*Job.second, Job.second->getFirstInsertionPt()); 20906c3fb27SDimitry Andric MSSAU.moveToPlace(MSSA.getMemoryAccess(Job.first), Job.first->getParent(), 21006c3fb27SDimitry Andric MemorySSA::InsertionPlace::Beginning); 21106c3fb27SDimitry Andric } 21206c3fb27SDimitry Andric 21306c3fb27SDimitry Andric if (VerifyMemorySSA) 21406c3fb27SDimitry Andric MSSA.verifyMemorySSA(); 21506c3fb27SDimitry Andric 21606c3fb27SDimitry Andric NumMoved += JobList.size(); 21706c3fb27SDimitry Andric 21806c3fb27SDimitry Andric return true; 21906c3fb27SDimitry Andric } 22006c3fb27SDimitry Andric 22106c3fb27SDimitry Andric PreservedAnalyses MoveAutoInitPass::run(Function &F, 22206c3fb27SDimitry Andric FunctionAnalysisManager &AM) { 22306c3fb27SDimitry Andric 22406c3fb27SDimitry Andric auto &DT = AM.getResult<DominatorTreeAnalysis>(F); 22506c3fb27SDimitry Andric auto &MSSA = AM.getResult<MemorySSAAnalysis>(F).getMSSA(); 22606c3fb27SDimitry Andric if (!runMoveAutoInit(F, DT, MSSA)) 22706c3fb27SDimitry Andric return PreservedAnalyses::all(); 22806c3fb27SDimitry Andric 22906c3fb27SDimitry Andric PreservedAnalyses PA; 23006c3fb27SDimitry Andric PA.preserve<DominatorTreeAnalysis>(); 23106c3fb27SDimitry Andric PA.preserve<MemorySSAAnalysis>(); 23206c3fb27SDimitry Andric PA.preserveSet<CFGAnalyses>(); 23306c3fb27SDimitry Andric return PA; 23406c3fb27SDimitry Andric } 235