xref: /freebsd-src/contrib/llvm-project/llvm/lib/Transforms/Utils/MoveAutoInit.cpp (revision 7a6dacaca14b62ca4b74406814becb87a3fefac0)
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 
hasAutoInitMetadata(const Instruction & I)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 
writeToAlloca(const Instruction & I)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.
usersDominator(const MemoryLocation & ML,Instruction * I,DominatorTree & DT,MemorySSA & MSSA)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 
runMoveAutoInit(Function & F,DominatorTree & DT,MemorySSA & MSSA)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 
run(Function & F,FunctionAnalysisManager & AM)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