1fe013be4SDimitry Andric //===-- MoveAutoInit.cpp - move auto-init inst closer to their use site----===//
2fe013be4SDimitry Andric //
3fe013be4SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4fe013be4SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5fe013be4SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6fe013be4SDimitry Andric //
7fe013be4SDimitry Andric //===----------------------------------------------------------------------===//
8fe013be4SDimitry Andric //
9fe013be4SDimitry Andric // This pass moves instruction maked as auto-init closer to the basic block that
10fe013be4SDimitry Andric // use it, eventually removing it from some control path of the function.
11fe013be4SDimitry Andric //
12fe013be4SDimitry Andric //===----------------------------------------------------------------------===//
13fe013be4SDimitry Andric 
14fe013be4SDimitry Andric #include "llvm/Transforms/Utils/MoveAutoInit.h"
15fe013be4SDimitry Andric #include "llvm/ADT/STLExtras.h"
16fe013be4SDimitry Andric #include "llvm/ADT/Statistic.h"
17fe013be4SDimitry Andric #include "llvm/Analysis/MemorySSA.h"
18fe013be4SDimitry Andric #include "llvm/Analysis/MemorySSAUpdater.h"
19fe013be4SDimitry Andric #include "llvm/Analysis/ValueTracking.h"
20fe013be4SDimitry Andric #include "llvm/IR/DebugInfo.h"
21fe013be4SDimitry Andric #include "llvm/IR/Dominators.h"
22fe013be4SDimitry Andric #include "llvm/IR/IRBuilder.h"
23fe013be4SDimitry Andric #include "llvm/IR/Instructions.h"
24fe013be4SDimitry Andric #include "llvm/IR/IntrinsicInst.h"
25fe013be4SDimitry Andric #include "llvm/Support/CommandLine.h"
26fe013be4SDimitry Andric #include "llvm/Transforms/Utils.h"
27fe013be4SDimitry Andric #include "llvm/Transforms/Utils/LoopUtils.h"
28fe013be4SDimitry Andric 
29fe013be4SDimitry Andric using namespace llvm;
30fe013be4SDimitry Andric 
31fe013be4SDimitry Andric #define DEBUG_TYPE "move-auto-init"
32fe013be4SDimitry Andric 
33fe013be4SDimitry Andric STATISTIC(NumMoved, "Number of instructions moved");
34fe013be4SDimitry Andric 
35fe013be4SDimitry Andric static cl::opt<unsigned> MoveAutoInitThreshold(
36fe013be4SDimitry Andric     "move-auto-init-threshold", cl::Hidden, cl::init(128),
37fe013be4SDimitry Andric     cl::desc("Maximum instructions to analyze per moved initialization"));
38fe013be4SDimitry Andric 
hasAutoInitMetadata(const Instruction & I)39fe013be4SDimitry Andric static bool hasAutoInitMetadata(const Instruction &I) {
40fe013be4SDimitry Andric   return I.hasMetadata(LLVMContext::MD_annotation) &&
41fe013be4SDimitry Andric          any_of(I.getMetadata(LLVMContext::MD_annotation)->operands(),
42fe013be4SDimitry Andric                 [](const MDOperand &Op) { return Op.equalsStr("auto-init"); });
43fe013be4SDimitry Andric }
44fe013be4SDimitry Andric 
writeToAlloca(const Instruction & I)45fe013be4SDimitry Andric static std::optional<MemoryLocation> writeToAlloca(const Instruction &I) {
46fe013be4SDimitry Andric   MemoryLocation ML;
47fe013be4SDimitry Andric   if (auto *MI = dyn_cast<MemIntrinsic>(&I))
48fe013be4SDimitry Andric     ML = MemoryLocation::getForDest(MI);
49fe013be4SDimitry Andric   else if (auto *SI = dyn_cast<StoreInst>(&I))
50fe013be4SDimitry Andric     ML = MemoryLocation::get(SI);
51fe013be4SDimitry Andric   else
52c9157d92SDimitry Andric     return std::nullopt;
53fe013be4SDimitry Andric 
54fe013be4SDimitry Andric   if (isa<AllocaInst>(getUnderlyingObject(ML.Ptr)))
55fe013be4SDimitry Andric     return ML;
56fe013be4SDimitry Andric   else
57fe013be4SDimitry Andric     return {};
58fe013be4SDimitry Andric }
59fe013be4SDimitry Andric 
60fe013be4SDimitry Andric /// Finds a BasicBlock in the CFG where instruction `I` can be moved to while
61fe013be4SDimitry Andric /// not changing the Memory SSA ordering and being guarded by at least one
62fe013be4SDimitry Andric /// condition.
usersDominator(const MemoryLocation & ML,Instruction * I,DominatorTree & DT,MemorySSA & MSSA)63fe013be4SDimitry Andric static BasicBlock *usersDominator(const MemoryLocation &ML, Instruction *I,
64fe013be4SDimitry Andric                                   DominatorTree &DT, MemorySSA &MSSA) {
65fe013be4SDimitry Andric   BasicBlock *CurrentDominator = nullptr;
66fe013be4SDimitry Andric   MemoryUseOrDef &IMA = *MSSA.getMemoryAccess(I);
67fe013be4SDimitry Andric   BatchAAResults AA(MSSA.getAA());
68fe013be4SDimitry Andric 
69fe013be4SDimitry Andric   SmallPtrSet<MemoryAccess *, 8> Visited;
70fe013be4SDimitry Andric 
71fe013be4SDimitry Andric   auto AsMemoryAccess = [](User *U) { return cast<MemoryAccess>(U); };
72fe013be4SDimitry Andric   SmallVector<MemoryAccess *> WorkList(map_range(IMA.users(), AsMemoryAccess));
73fe013be4SDimitry Andric 
74fe013be4SDimitry Andric   while (!WorkList.empty()) {
75fe013be4SDimitry Andric     MemoryAccess *MA = WorkList.pop_back_val();
76fe013be4SDimitry Andric     if (!Visited.insert(MA).second)
77fe013be4SDimitry Andric       continue;
78fe013be4SDimitry Andric 
79fe013be4SDimitry Andric     if (Visited.size() > MoveAutoInitThreshold)
80fe013be4SDimitry Andric       return nullptr;
81fe013be4SDimitry Andric 
82fe013be4SDimitry Andric     bool FoundClobberingUser = false;
83fe013be4SDimitry Andric     if (auto *M = dyn_cast<MemoryUseOrDef>(MA)) {
84fe013be4SDimitry Andric       Instruction *MI = M->getMemoryInst();
85fe013be4SDimitry Andric 
86fe013be4SDimitry Andric       // If this memory instruction may not clobber `I`, we can skip it.
87fe013be4SDimitry Andric       // LifetimeEnd is a valid user, but we do not want it in the user
88fe013be4SDimitry Andric       // dominator.
89fe013be4SDimitry Andric       if (AA.getModRefInfo(MI, ML) != ModRefInfo::NoModRef &&
90fe013be4SDimitry Andric           !MI->isLifetimeStartOrEnd() && MI != I) {
91fe013be4SDimitry Andric         FoundClobberingUser = true;
92fe013be4SDimitry Andric         CurrentDominator = CurrentDominator
93fe013be4SDimitry Andric                                ? DT.findNearestCommonDominator(CurrentDominator,
94fe013be4SDimitry Andric                                                                MI->getParent())
95fe013be4SDimitry Andric                                : MI->getParent();
96fe013be4SDimitry Andric       }
97fe013be4SDimitry Andric     }
98fe013be4SDimitry Andric     if (!FoundClobberingUser) {
99fe013be4SDimitry Andric       auto UsersAsMemoryAccesses = map_range(MA->users(), AsMemoryAccess);
100fe013be4SDimitry Andric       append_range(WorkList, UsersAsMemoryAccesses);
101fe013be4SDimitry Andric     }
102fe013be4SDimitry Andric   }
103fe013be4SDimitry Andric   return CurrentDominator;
104fe013be4SDimitry Andric }
105fe013be4SDimitry Andric 
runMoveAutoInit(Function & F,DominatorTree & DT,MemorySSA & MSSA)106fe013be4SDimitry Andric static bool runMoveAutoInit(Function &F, DominatorTree &DT, MemorySSA &MSSA) {
107fe013be4SDimitry Andric   BasicBlock &EntryBB = F.getEntryBlock();
108fe013be4SDimitry Andric   SmallVector<std::pair<Instruction *, BasicBlock *>> JobList;
109fe013be4SDimitry Andric 
110fe013be4SDimitry Andric   //
111fe013be4SDimitry Andric   // Compute movable instructions.
112fe013be4SDimitry Andric   //
113fe013be4SDimitry Andric   for (Instruction &I : EntryBB) {
114fe013be4SDimitry Andric     if (!hasAutoInitMetadata(I))
115fe013be4SDimitry Andric       continue;
116fe013be4SDimitry Andric 
117fe013be4SDimitry Andric     std::optional<MemoryLocation> ML = writeToAlloca(I);
118fe013be4SDimitry Andric     if (!ML)
119fe013be4SDimitry Andric       continue;
120fe013be4SDimitry Andric 
121fe013be4SDimitry Andric     if (I.isVolatile())
122fe013be4SDimitry Andric       continue;
123fe013be4SDimitry Andric 
124fe013be4SDimitry Andric     BasicBlock *UsersDominator = usersDominator(ML.value(), &I, DT, MSSA);
125fe013be4SDimitry Andric     if (!UsersDominator)
126fe013be4SDimitry Andric       continue;
127fe013be4SDimitry Andric 
128fe013be4SDimitry Andric     if (UsersDominator == &EntryBB)
129fe013be4SDimitry Andric       continue;
130fe013be4SDimitry Andric 
131fe013be4SDimitry Andric     // Traverse the CFG to detect cycles `UsersDominator` would be part of.
132fe013be4SDimitry Andric     SmallPtrSet<BasicBlock *, 8> TransitiveSuccessors;
133fe013be4SDimitry Andric     SmallVector<BasicBlock *> WorkList(successors(UsersDominator));
134fe013be4SDimitry Andric     bool HasCycle = false;
135fe013be4SDimitry Andric     while (!WorkList.empty()) {
136fe013be4SDimitry Andric       BasicBlock *CurrBB = WorkList.pop_back_val();
137fe013be4SDimitry Andric       if (CurrBB == UsersDominator)
138fe013be4SDimitry Andric         // No early exit because we want to compute the full set of transitive
139fe013be4SDimitry Andric         // successors.
140fe013be4SDimitry Andric         HasCycle = true;
141fe013be4SDimitry Andric       for (BasicBlock *Successor : successors(CurrBB)) {
142fe013be4SDimitry Andric         if (!TransitiveSuccessors.insert(Successor).second)
143fe013be4SDimitry Andric           continue;
144fe013be4SDimitry Andric         WorkList.push_back(Successor);
145fe013be4SDimitry Andric       }
146fe013be4SDimitry Andric     }
147fe013be4SDimitry Andric 
148fe013be4SDimitry Andric     // Don't insert if that could create multiple execution of I,
149fe013be4SDimitry Andric     // but we can insert it in the non back-edge predecessors, if it exists.
150fe013be4SDimitry Andric     if (HasCycle) {
151fe013be4SDimitry Andric       BasicBlock *UsersDominatorHead = UsersDominator;
152fe013be4SDimitry Andric       while (BasicBlock *UniquePredecessor =
153fe013be4SDimitry Andric                  UsersDominatorHead->getUniquePredecessor())
154fe013be4SDimitry Andric         UsersDominatorHead = UniquePredecessor;
155fe013be4SDimitry Andric 
156fe013be4SDimitry Andric       if (UsersDominatorHead == &EntryBB)
157fe013be4SDimitry Andric         continue;
158fe013be4SDimitry Andric 
159fe013be4SDimitry Andric       BasicBlock *DominatingPredecessor = nullptr;
160fe013be4SDimitry Andric       for (BasicBlock *Pred : predecessors(UsersDominatorHead)) {
161fe013be4SDimitry Andric         // If one of the predecessor of the dominator also transitively is a
162fe013be4SDimitry Andric         // successor, moving to the dominator would do the inverse of loop
163fe013be4SDimitry Andric         // hoisting, and we don't want that.
164fe013be4SDimitry Andric         if (TransitiveSuccessors.count(Pred))
165fe013be4SDimitry Andric           continue;
166fe013be4SDimitry Andric 
167*a58f00eaSDimitry Andric         if (!DT.isReachableFromEntry(Pred))
168*a58f00eaSDimitry Andric           continue;
169*a58f00eaSDimitry Andric 
170fe013be4SDimitry Andric         DominatingPredecessor =
171fe013be4SDimitry Andric             DominatingPredecessor
172fe013be4SDimitry Andric                 ? DT.findNearestCommonDominator(DominatingPredecessor, Pred)
173fe013be4SDimitry Andric                 : Pred;
174fe013be4SDimitry Andric       }
175fe013be4SDimitry Andric 
176fe013be4SDimitry Andric       if (!DominatingPredecessor || DominatingPredecessor == &EntryBB)
177fe013be4SDimitry Andric         continue;
178fe013be4SDimitry Andric 
179fe013be4SDimitry Andric       UsersDominator = DominatingPredecessor;
180fe013be4SDimitry Andric     }
181fe013be4SDimitry Andric 
182fe013be4SDimitry Andric     // CatchSwitchInst blocks can only have one instruction, so they are not
183fe013be4SDimitry Andric     // good candidates for insertion.
184*a58f00eaSDimitry Andric     while (isa<CatchSwitchInst>(UsersDominator->getFirstNonPHI())) {
185fe013be4SDimitry Andric       for (BasicBlock *Pred : predecessors(UsersDominator))
186*a58f00eaSDimitry Andric         if (DT.isReachableFromEntry(Pred))
187fe013be4SDimitry Andric           UsersDominator = DT.findNearestCommonDominator(UsersDominator, Pred);
188fe013be4SDimitry Andric     }
189fe013be4SDimitry Andric 
190fe013be4SDimitry Andric     // We finally found a place where I can be moved while not introducing extra
191fe013be4SDimitry Andric     // execution, and guarded by at least one condition.
192fe013be4SDimitry Andric     if (UsersDominator != &EntryBB)
193fe013be4SDimitry Andric       JobList.emplace_back(&I, UsersDominator);
194fe013be4SDimitry Andric   }
195fe013be4SDimitry Andric 
196fe013be4SDimitry Andric   //
197fe013be4SDimitry Andric   // Perform the actual substitution.
198fe013be4SDimitry Andric   //
199fe013be4SDimitry Andric   if (JobList.empty())
200fe013be4SDimitry Andric     return false;
201fe013be4SDimitry Andric 
202fe013be4SDimitry Andric   MemorySSAUpdater MSSAU(&MSSA);
203fe013be4SDimitry Andric 
204fe013be4SDimitry Andric   // Reverse insertion to respect relative order between instructions:
205fe013be4SDimitry Andric   // if two instructions are moved from the same BB to the same BB, we insert
206fe013be4SDimitry Andric   // the second one in the front, then the first on top of it.
207fe013be4SDimitry Andric   for (auto &Job : reverse(JobList)) {
208c9157d92SDimitry Andric     Job.first->moveBefore(*Job.second, Job.second->getFirstInsertionPt());
209fe013be4SDimitry Andric     MSSAU.moveToPlace(MSSA.getMemoryAccess(Job.first), Job.first->getParent(),
210fe013be4SDimitry Andric                       MemorySSA::InsertionPlace::Beginning);
211fe013be4SDimitry Andric   }
212fe013be4SDimitry Andric 
213fe013be4SDimitry Andric   if (VerifyMemorySSA)
214fe013be4SDimitry Andric     MSSA.verifyMemorySSA();
215fe013be4SDimitry Andric 
216fe013be4SDimitry Andric   NumMoved += JobList.size();
217fe013be4SDimitry Andric 
218fe013be4SDimitry Andric   return true;
219fe013be4SDimitry Andric }
220fe013be4SDimitry Andric 
run(Function & F,FunctionAnalysisManager & AM)221fe013be4SDimitry Andric PreservedAnalyses MoveAutoInitPass::run(Function &F,
222fe013be4SDimitry Andric                                         FunctionAnalysisManager &AM) {
223fe013be4SDimitry Andric 
224fe013be4SDimitry Andric   auto &DT = AM.getResult<DominatorTreeAnalysis>(F);
225fe013be4SDimitry Andric   auto &MSSA = AM.getResult<MemorySSAAnalysis>(F).getMSSA();
226fe013be4SDimitry Andric   if (!runMoveAutoInit(F, DT, MSSA))
227fe013be4SDimitry Andric     return PreservedAnalyses::all();
228fe013be4SDimitry Andric 
229fe013be4SDimitry Andric   PreservedAnalyses PA;
230fe013be4SDimitry Andric   PA.preserve<DominatorTreeAnalysis>();
231fe013be4SDimitry Andric   PA.preserve<MemorySSAAnalysis>();
232fe013be4SDimitry Andric   PA.preserveSet<CFGAnalyses>();
233fe013be4SDimitry Andric   return PA;
234fe013be4SDimitry Andric }
235