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