xref: /src/contrib/llvm-project/llvm/lib/Transforms/Utils/MoveAutoInit.cpp (revision 7a6dacaca14b62ca4b74406814becb87a3fefac0)
17fa27ce4SDimitry Andric //===-- MoveAutoInit.cpp - move auto-init inst closer to their use site----===//
27fa27ce4SDimitry Andric //
37fa27ce4SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
47fa27ce4SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
57fa27ce4SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
67fa27ce4SDimitry Andric //
77fa27ce4SDimitry Andric //===----------------------------------------------------------------------===//
87fa27ce4SDimitry Andric //
97fa27ce4SDimitry Andric // This pass moves instruction maked as auto-init closer to the basic block that
107fa27ce4SDimitry Andric // use it, eventually removing it from some control path of the function.
117fa27ce4SDimitry Andric //
127fa27ce4SDimitry Andric //===----------------------------------------------------------------------===//
137fa27ce4SDimitry Andric 
147fa27ce4SDimitry Andric #include "llvm/Transforms/Utils/MoveAutoInit.h"
157fa27ce4SDimitry Andric #include "llvm/ADT/STLExtras.h"
167fa27ce4SDimitry Andric #include "llvm/ADT/Statistic.h"
177fa27ce4SDimitry Andric #include "llvm/Analysis/MemorySSA.h"
187fa27ce4SDimitry Andric #include "llvm/Analysis/MemorySSAUpdater.h"
197fa27ce4SDimitry Andric #include "llvm/Analysis/ValueTracking.h"
207fa27ce4SDimitry Andric #include "llvm/IR/DebugInfo.h"
217fa27ce4SDimitry Andric #include "llvm/IR/Dominators.h"
227fa27ce4SDimitry Andric #include "llvm/IR/IRBuilder.h"
237fa27ce4SDimitry Andric #include "llvm/IR/Instructions.h"
247fa27ce4SDimitry Andric #include "llvm/IR/IntrinsicInst.h"
257fa27ce4SDimitry Andric #include "llvm/Support/CommandLine.h"
267fa27ce4SDimitry Andric #include "llvm/Transforms/Utils.h"
277fa27ce4SDimitry Andric #include "llvm/Transforms/Utils/LoopUtils.h"
287fa27ce4SDimitry Andric 
297fa27ce4SDimitry Andric using namespace llvm;
307fa27ce4SDimitry Andric 
317fa27ce4SDimitry Andric #define DEBUG_TYPE "move-auto-init"
327fa27ce4SDimitry Andric 
337fa27ce4SDimitry Andric STATISTIC(NumMoved, "Number of instructions moved");
347fa27ce4SDimitry Andric 
357fa27ce4SDimitry Andric static cl::opt<unsigned> MoveAutoInitThreshold(
367fa27ce4SDimitry Andric     "move-auto-init-threshold", cl::Hidden, cl::init(128),
377fa27ce4SDimitry Andric     cl::desc("Maximum instructions to analyze per moved initialization"));
387fa27ce4SDimitry Andric 
hasAutoInitMetadata(const Instruction & I)397fa27ce4SDimitry Andric static bool hasAutoInitMetadata(const Instruction &I) {
407fa27ce4SDimitry Andric   return I.hasMetadata(LLVMContext::MD_annotation) &&
417fa27ce4SDimitry Andric          any_of(I.getMetadata(LLVMContext::MD_annotation)->operands(),
427fa27ce4SDimitry Andric                 [](const MDOperand &Op) { return Op.equalsStr("auto-init"); });
437fa27ce4SDimitry Andric }
447fa27ce4SDimitry Andric 
writeToAlloca(const Instruction & I)457fa27ce4SDimitry Andric static std::optional<MemoryLocation> writeToAlloca(const Instruction &I) {
467fa27ce4SDimitry Andric   MemoryLocation ML;
477fa27ce4SDimitry Andric   if (auto *MI = dyn_cast<MemIntrinsic>(&I))
487fa27ce4SDimitry Andric     ML = MemoryLocation::getForDest(MI);
497fa27ce4SDimitry Andric   else if (auto *SI = dyn_cast<StoreInst>(&I))
507fa27ce4SDimitry Andric     ML = MemoryLocation::get(SI);
517fa27ce4SDimitry Andric   else
52b1c73532SDimitry Andric     return std::nullopt;
537fa27ce4SDimitry Andric 
547fa27ce4SDimitry Andric   if (isa<AllocaInst>(getUnderlyingObject(ML.Ptr)))
557fa27ce4SDimitry Andric     return ML;
567fa27ce4SDimitry Andric   else
577fa27ce4SDimitry Andric     return {};
587fa27ce4SDimitry Andric }
597fa27ce4SDimitry Andric 
607fa27ce4SDimitry Andric /// Finds a BasicBlock in the CFG where instruction `I` can be moved to while
617fa27ce4SDimitry Andric /// not changing the Memory SSA ordering and being guarded by at least one
627fa27ce4SDimitry Andric /// condition.
usersDominator(const MemoryLocation & ML,Instruction * I,DominatorTree & DT,MemorySSA & MSSA)637fa27ce4SDimitry Andric static BasicBlock *usersDominator(const MemoryLocation &ML, Instruction *I,
647fa27ce4SDimitry Andric                                   DominatorTree &DT, MemorySSA &MSSA) {
657fa27ce4SDimitry Andric   BasicBlock *CurrentDominator = nullptr;
667fa27ce4SDimitry Andric   MemoryUseOrDef &IMA = *MSSA.getMemoryAccess(I);
677fa27ce4SDimitry Andric   BatchAAResults AA(MSSA.getAA());
687fa27ce4SDimitry Andric 
697fa27ce4SDimitry Andric   SmallPtrSet<MemoryAccess *, 8> Visited;
707fa27ce4SDimitry Andric 
717fa27ce4SDimitry Andric   auto AsMemoryAccess = [](User *U) { return cast<MemoryAccess>(U); };
727fa27ce4SDimitry Andric   SmallVector<MemoryAccess *> WorkList(map_range(IMA.users(), AsMemoryAccess));
737fa27ce4SDimitry Andric 
747fa27ce4SDimitry Andric   while (!WorkList.empty()) {
757fa27ce4SDimitry Andric     MemoryAccess *MA = WorkList.pop_back_val();
767fa27ce4SDimitry Andric     if (!Visited.insert(MA).second)
777fa27ce4SDimitry Andric       continue;
787fa27ce4SDimitry Andric 
797fa27ce4SDimitry Andric     if (Visited.size() > MoveAutoInitThreshold)
807fa27ce4SDimitry Andric       return nullptr;
817fa27ce4SDimitry Andric 
827fa27ce4SDimitry Andric     bool FoundClobberingUser = false;
837fa27ce4SDimitry Andric     if (auto *M = dyn_cast<MemoryUseOrDef>(MA)) {
847fa27ce4SDimitry Andric       Instruction *MI = M->getMemoryInst();
857fa27ce4SDimitry Andric 
867fa27ce4SDimitry Andric       // If this memory instruction may not clobber `I`, we can skip it.
877fa27ce4SDimitry Andric       // LifetimeEnd is a valid user, but we do not want it in the user
887fa27ce4SDimitry Andric       // dominator.
897fa27ce4SDimitry Andric       if (AA.getModRefInfo(MI, ML) != ModRefInfo::NoModRef &&
907fa27ce4SDimitry Andric           !MI->isLifetimeStartOrEnd() && MI != I) {
917fa27ce4SDimitry Andric         FoundClobberingUser = true;
927fa27ce4SDimitry Andric         CurrentDominator = CurrentDominator
937fa27ce4SDimitry Andric                                ? DT.findNearestCommonDominator(CurrentDominator,
947fa27ce4SDimitry Andric                                                                MI->getParent())
957fa27ce4SDimitry Andric                                : MI->getParent();
967fa27ce4SDimitry Andric       }
977fa27ce4SDimitry Andric     }
987fa27ce4SDimitry Andric     if (!FoundClobberingUser) {
997fa27ce4SDimitry Andric       auto UsersAsMemoryAccesses = map_range(MA->users(), AsMemoryAccess);
1007fa27ce4SDimitry Andric       append_range(WorkList, UsersAsMemoryAccesses);
1017fa27ce4SDimitry Andric     }
1027fa27ce4SDimitry Andric   }
1037fa27ce4SDimitry Andric   return CurrentDominator;
1047fa27ce4SDimitry Andric }
1057fa27ce4SDimitry Andric 
runMoveAutoInit(Function & F,DominatorTree & DT,MemorySSA & MSSA)1067fa27ce4SDimitry Andric static bool runMoveAutoInit(Function &F, DominatorTree &DT, MemorySSA &MSSA) {
1077fa27ce4SDimitry Andric   BasicBlock &EntryBB = F.getEntryBlock();
1087fa27ce4SDimitry Andric   SmallVector<std::pair<Instruction *, BasicBlock *>> JobList;
1097fa27ce4SDimitry Andric 
1107fa27ce4SDimitry Andric   //
1117fa27ce4SDimitry Andric   // Compute movable instructions.
1127fa27ce4SDimitry Andric   //
1137fa27ce4SDimitry Andric   for (Instruction &I : EntryBB) {
1147fa27ce4SDimitry Andric     if (!hasAutoInitMetadata(I))
1157fa27ce4SDimitry Andric       continue;
1167fa27ce4SDimitry Andric 
1177fa27ce4SDimitry Andric     std::optional<MemoryLocation> ML = writeToAlloca(I);
1187fa27ce4SDimitry Andric     if (!ML)
1197fa27ce4SDimitry Andric       continue;
1207fa27ce4SDimitry Andric 
1217fa27ce4SDimitry Andric     if (I.isVolatile())
1227fa27ce4SDimitry Andric       continue;
1237fa27ce4SDimitry Andric 
1247fa27ce4SDimitry Andric     BasicBlock *UsersDominator = usersDominator(ML.value(), &I, DT, MSSA);
1257fa27ce4SDimitry Andric     if (!UsersDominator)
1267fa27ce4SDimitry Andric       continue;
1277fa27ce4SDimitry Andric 
1287fa27ce4SDimitry Andric     if (UsersDominator == &EntryBB)
1297fa27ce4SDimitry Andric       continue;
1307fa27ce4SDimitry Andric 
1317fa27ce4SDimitry Andric     // Traverse the CFG to detect cycles `UsersDominator` would be part of.
1327fa27ce4SDimitry Andric     SmallPtrSet<BasicBlock *, 8> TransitiveSuccessors;
1337fa27ce4SDimitry Andric     SmallVector<BasicBlock *> WorkList(successors(UsersDominator));
1347fa27ce4SDimitry Andric     bool HasCycle = false;
1357fa27ce4SDimitry Andric     while (!WorkList.empty()) {
1367fa27ce4SDimitry Andric       BasicBlock *CurrBB = WorkList.pop_back_val();
1377fa27ce4SDimitry Andric       if (CurrBB == UsersDominator)
1387fa27ce4SDimitry Andric         // No early exit because we want to compute the full set of transitive
1397fa27ce4SDimitry Andric         // successors.
1407fa27ce4SDimitry Andric         HasCycle = true;
1417fa27ce4SDimitry Andric       for (BasicBlock *Successor : successors(CurrBB)) {
1427fa27ce4SDimitry Andric         if (!TransitiveSuccessors.insert(Successor).second)
1437fa27ce4SDimitry Andric           continue;
1447fa27ce4SDimitry Andric         WorkList.push_back(Successor);
1457fa27ce4SDimitry Andric       }
1467fa27ce4SDimitry Andric     }
1477fa27ce4SDimitry Andric 
1487fa27ce4SDimitry Andric     // Don't insert if that could create multiple execution of I,
1497fa27ce4SDimitry Andric     // but we can insert it in the non back-edge predecessors, if it exists.
1507fa27ce4SDimitry Andric     if (HasCycle) {
1517fa27ce4SDimitry Andric       BasicBlock *UsersDominatorHead = UsersDominator;
1527fa27ce4SDimitry Andric       while (BasicBlock *UniquePredecessor =
1537fa27ce4SDimitry Andric                  UsersDominatorHead->getUniquePredecessor())
1547fa27ce4SDimitry Andric         UsersDominatorHead = UniquePredecessor;
1557fa27ce4SDimitry Andric 
1567fa27ce4SDimitry Andric       if (UsersDominatorHead == &EntryBB)
1577fa27ce4SDimitry Andric         continue;
1587fa27ce4SDimitry Andric 
1597fa27ce4SDimitry Andric       BasicBlock *DominatingPredecessor = nullptr;
1607fa27ce4SDimitry Andric       for (BasicBlock *Pred : predecessors(UsersDominatorHead)) {
1617fa27ce4SDimitry Andric         // If one of the predecessor of the dominator also transitively is a
1627fa27ce4SDimitry Andric         // successor, moving to the dominator would do the inverse of loop
1637fa27ce4SDimitry Andric         // hoisting, and we don't want that.
1647fa27ce4SDimitry Andric         if (TransitiveSuccessors.count(Pred))
1657fa27ce4SDimitry Andric           continue;
1667fa27ce4SDimitry Andric 
1674df029ccSDimitry Andric         if (!DT.isReachableFromEntry(Pred))
1684df029ccSDimitry Andric           continue;
1694df029ccSDimitry Andric 
1707fa27ce4SDimitry Andric         DominatingPredecessor =
1717fa27ce4SDimitry Andric             DominatingPredecessor
1727fa27ce4SDimitry Andric                 ? DT.findNearestCommonDominator(DominatingPredecessor, Pred)
1737fa27ce4SDimitry Andric                 : Pred;
1747fa27ce4SDimitry Andric       }
1757fa27ce4SDimitry Andric 
1767fa27ce4SDimitry Andric       if (!DominatingPredecessor || DominatingPredecessor == &EntryBB)
1777fa27ce4SDimitry Andric         continue;
1787fa27ce4SDimitry Andric 
1797fa27ce4SDimitry Andric       UsersDominator = DominatingPredecessor;
1807fa27ce4SDimitry Andric     }
1817fa27ce4SDimitry Andric 
1827fa27ce4SDimitry Andric     // CatchSwitchInst blocks can only have one instruction, so they are not
1837fa27ce4SDimitry Andric     // good candidates for insertion.
1844df029ccSDimitry Andric     while (isa<CatchSwitchInst>(UsersDominator->getFirstNonPHI())) {
1857fa27ce4SDimitry Andric       for (BasicBlock *Pred : predecessors(UsersDominator))
1864df029ccSDimitry Andric         if (DT.isReachableFromEntry(Pred))
1877fa27ce4SDimitry Andric           UsersDominator = DT.findNearestCommonDominator(UsersDominator, Pred);
1887fa27ce4SDimitry Andric     }
1897fa27ce4SDimitry Andric 
1907fa27ce4SDimitry Andric     // We finally found a place where I can be moved while not introducing extra
1917fa27ce4SDimitry Andric     // execution, and guarded by at least one condition.
1927fa27ce4SDimitry Andric     if (UsersDominator != &EntryBB)
1937fa27ce4SDimitry Andric       JobList.emplace_back(&I, UsersDominator);
1947fa27ce4SDimitry Andric   }
1957fa27ce4SDimitry Andric 
1967fa27ce4SDimitry Andric   //
1977fa27ce4SDimitry Andric   // Perform the actual substitution.
1987fa27ce4SDimitry Andric   //
1997fa27ce4SDimitry Andric   if (JobList.empty())
2007fa27ce4SDimitry Andric     return false;
2017fa27ce4SDimitry Andric 
2027fa27ce4SDimitry Andric   MemorySSAUpdater MSSAU(&MSSA);
2037fa27ce4SDimitry Andric 
2047fa27ce4SDimitry Andric   // Reverse insertion to respect relative order between instructions:
2057fa27ce4SDimitry Andric   // if two instructions are moved from the same BB to the same BB, we insert
2067fa27ce4SDimitry Andric   // the second one in the front, then the first on top of it.
2077fa27ce4SDimitry Andric   for (auto &Job : reverse(JobList)) {
208b1c73532SDimitry Andric     Job.first->moveBefore(*Job.second, Job.second->getFirstInsertionPt());
2097fa27ce4SDimitry Andric     MSSAU.moveToPlace(MSSA.getMemoryAccess(Job.first), Job.first->getParent(),
2107fa27ce4SDimitry Andric                       MemorySSA::InsertionPlace::Beginning);
2117fa27ce4SDimitry Andric   }
2127fa27ce4SDimitry Andric 
2137fa27ce4SDimitry Andric   if (VerifyMemorySSA)
2147fa27ce4SDimitry Andric     MSSA.verifyMemorySSA();
2157fa27ce4SDimitry Andric 
2167fa27ce4SDimitry Andric   NumMoved += JobList.size();
2177fa27ce4SDimitry Andric 
2187fa27ce4SDimitry Andric   return true;
2197fa27ce4SDimitry Andric }
2207fa27ce4SDimitry Andric 
run(Function & F,FunctionAnalysisManager & AM)2217fa27ce4SDimitry Andric PreservedAnalyses MoveAutoInitPass::run(Function &F,
2227fa27ce4SDimitry Andric                                         FunctionAnalysisManager &AM) {
2237fa27ce4SDimitry Andric 
2247fa27ce4SDimitry Andric   auto &DT = AM.getResult<DominatorTreeAnalysis>(F);
2257fa27ce4SDimitry Andric   auto &MSSA = AM.getResult<MemorySSAAnalysis>(F).getMSSA();
2267fa27ce4SDimitry Andric   if (!runMoveAutoInit(F, DT, MSSA))
2277fa27ce4SDimitry Andric     return PreservedAnalyses::all();
2287fa27ce4SDimitry Andric 
2297fa27ce4SDimitry Andric   PreservedAnalyses PA;
2307fa27ce4SDimitry Andric   PA.preserve<DominatorTreeAnalysis>();
2317fa27ce4SDimitry Andric   PA.preserve<MemorySSAAnalysis>();
2327fa27ce4SDimitry Andric   PA.preserveSet<CFGAnalyses>();
2337fa27ce4SDimitry Andric   return PA;
2347fa27ce4SDimitry Andric }
235