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