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