1e8d8bef9SDimitry Andric //===- MemProfiler.cpp - memory allocation and access profiler ------------===// 2e8d8bef9SDimitry Andric // 3e8d8bef9SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4e8d8bef9SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5e8d8bef9SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6e8d8bef9SDimitry Andric // 7e8d8bef9SDimitry Andric //===----------------------------------------------------------------------===// 8e8d8bef9SDimitry Andric // 9e8d8bef9SDimitry Andric // This file is a part of MemProfiler. Memory accesses are instrumented 10e8d8bef9SDimitry Andric // to increment the access count held in a shadow memory location, or 11e8d8bef9SDimitry Andric // alternatively to call into the runtime. Memory intrinsic calls (memmove, 12e8d8bef9SDimitry Andric // memcpy, memset) are changed to call the memory profiling runtime version 13e8d8bef9SDimitry Andric // instead. 14e8d8bef9SDimitry Andric // 15e8d8bef9SDimitry Andric //===----------------------------------------------------------------------===// 16e8d8bef9SDimitry Andric 17e8d8bef9SDimitry Andric #include "llvm/Transforms/Instrumentation/MemProfiler.h" 18e8d8bef9SDimitry Andric #include "llvm/ADT/SmallVector.h" 19e8d8bef9SDimitry Andric #include "llvm/ADT/Statistic.h" 20e8d8bef9SDimitry Andric #include "llvm/ADT/StringRef.h" 2106c3fb27SDimitry Andric #include "llvm/Analysis/MemoryBuiltins.h" 2206c3fb27SDimitry Andric #include "llvm/Analysis/MemoryProfileInfo.h" 23349cc55cSDimitry Andric #include "llvm/Analysis/ValueTracking.h" 24e8d8bef9SDimitry Andric #include "llvm/IR/Constant.h" 25e8d8bef9SDimitry Andric #include "llvm/IR/DataLayout.h" 2606c3fb27SDimitry Andric #include "llvm/IR/DiagnosticInfo.h" 27e8d8bef9SDimitry Andric #include "llvm/IR/Function.h" 28e8d8bef9SDimitry Andric #include "llvm/IR/GlobalValue.h" 29e8d8bef9SDimitry Andric #include "llvm/IR/IRBuilder.h" 30e8d8bef9SDimitry Andric #include "llvm/IR/Instruction.h" 311fd87a68SDimitry Andric #include "llvm/IR/IntrinsicInst.h" 32e8d8bef9SDimitry Andric #include "llvm/IR/Module.h" 33e8d8bef9SDimitry Andric #include "llvm/IR/Type.h" 34e8d8bef9SDimitry Andric #include "llvm/IR/Value.h" 3581ad6265SDimitry Andric #include "llvm/ProfileData/InstrProf.h" 3606c3fb27SDimitry Andric #include "llvm/ProfileData/InstrProfReader.h" 3706c3fb27SDimitry Andric #include "llvm/Support/BLAKE3.h" 38e8d8bef9SDimitry Andric #include "llvm/Support/CommandLine.h" 39e8d8bef9SDimitry Andric #include "llvm/Support/Debug.h" 4006c3fb27SDimitry Andric #include "llvm/Support/HashBuilder.h" 4106c3fb27SDimitry Andric #include "llvm/Support/VirtualFileSystem.h" 4206c3fb27SDimitry Andric #include "llvm/TargetParser/Triple.h" 43e8d8bef9SDimitry Andric #include "llvm/Transforms/Utils/BasicBlockUtils.h" 44e8d8bef9SDimitry Andric #include "llvm/Transforms/Utils/ModuleUtils.h" 4506c3fb27SDimitry Andric #include <map> 4606c3fb27SDimitry Andric #include <set> 47e8d8bef9SDimitry Andric 48e8d8bef9SDimitry Andric using namespace llvm; 4906c3fb27SDimitry Andric using namespace llvm::memprof; 50e8d8bef9SDimitry Andric 51e8d8bef9SDimitry Andric #define DEBUG_TYPE "memprof" 52e8d8bef9SDimitry Andric 5306c3fb27SDimitry Andric namespace llvm { 5406c3fb27SDimitry Andric extern cl::opt<bool> PGOWarnMissing; 5506c3fb27SDimitry Andric extern cl::opt<bool> NoPGOWarnMismatch; 5606c3fb27SDimitry Andric extern cl::opt<bool> NoPGOWarnMismatchComdatWeak; 5706c3fb27SDimitry Andric } // namespace llvm 5806c3fb27SDimitry Andric 59e8d8bef9SDimitry Andric constexpr int LLVM_MEM_PROFILER_VERSION = 1; 60e8d8bef9SDimitry Andric 61e8d8bef9SDimitry Andric // Size of memory mapped to a single shadow location. 62*0fca6ea1SDimitry Andric constexpr uint64_t DefaultMemGranularity = 64; 63e8d8bef9SDimitry Andric 64e8d8bef9SDimitry Andric // Scale from granularity down to shadow size. 65e8d8bef9SDimitry Andric constexpr uint64_t DefaultShadowScale = 3; 66e8d8bef9SDimitry Andric 67e8d8bef9SDimitry Andric constexpr char MemProfModuleCtorName[] = "memprof.module_ctor"; 68e8d8bef9SDimitry Andric constexpr uint64_t MemProfCtorAndDtorPriority = 1; 69e8d8bef9SDimitry Andric // On Emscripten, the system needs more than one priorities for constructors. 70e8d8bef9SDimitry Andric constexpr uint64_t MemProfEmscriptenCtorAndDtorPriority = 50; 71e8d8bef9SDimitry Andric constexpr char MemProfInitName[] = "__memprof_init"; 72e8d8bef9SDimitry Andric constexpr char MemProfVersionCheckNamePrefix[] = 73e8d8bef9SDimitry Andric "__memprof_version_mismatch_check_v"; 74e8d8bef9SDimitry Andric 75e8d8bef9SDimitry Andric constexpr char MemProfShadowMemoryDynamicAddress[] = 76e8d8bef9SDimitry Andric "__memprof_shadow_memory_dynamic_address"; 77e8d8bef9SDimitry Andric 78e8d8bef9SDimitry Andric constexpr char MemProfFilenameVar[] = "__memprof_profile_filename"; 79e8d8bef9SDimitry Andric 80*0fca6ea1SDimitry Andric constexpr char MemProfHistogramFlagVar[] = "__memprof_histogram"; 81*0fca6ea1SDimitry Andric 82e8d8bef9SDimitry Andric // Command-line flags. 83e8d8bef9SDimitry Andric 84e8d8bef9SDimitry Andric static cl::opt<bool> ClInsertVersionCheck( 85e8d8bef9SDimitry Andric "memprof-guard-against-version-mismatch", 86e8d8bef9SDimitry Andric cl::desc("Guard against compiler/runtime version mismatch."), cl::Hidden, 87e8d8bef9SDimitry Andric cl::init(true)); 88e8d8bef9SDimitry Andric 89e8d8bef9SDimitry Andric // This flag may need to be replaced with -f[no-]memprof-reads. 90e8d8bef9SDimitry Andric static cl::opt<bool> ClInstrumentReads("memprof-instrument-reads", 91e8d8bef9SDimitry Andric cl::desc("instrument read instructions"), 92e8d8bef9SDimitry Andric cl::Hidden, cl::init(true)); 93e8d8bef9SDimitry Andric 94e8d8bef9SDimitry Andric static cl::opt<bool> 95e8d8bef9SDimitry Andric ClInstrumentWrites("memprof-instrument-writes", 96e8d8bef9SDimitry Andric cl::desc("instrument write instructions"), cl::Hidden, 97e8d8bef9SDimitry Andric cl::init(true)); 98e8d8bef9SDimitry Andric 99e8d8bef9SDimitry Andric static cl::opt<bool> ClInstrumentAtomics( 100e8d8bef9SDimitry Andric "memprof-instrument-atomics", 101e8d8bef9SDimitry Andric cl::desc("instrument atomic instructions (rmw, cmpxchg)"), cl::Hidden, 102e8d8bef9SDimitry Andric cl::init(true)); 103e8d8bef9SDimitry Andric 104e8d8bef9SDimitry Andric static cl::opt<bool> ClUseCalls( 105e8d8bef9SDimitry Andric "memprof-use-callbacks", 106e8d8bef9SDimitry Andric cl::desc("Use callbacks instead of inline instrumentation sequences."), 107e8d8bef9SDimitry Andric cl::Hidden, cl::init(false)); 108e8d8bef9SDimitry Andric 109e8d8bef9SDimitry Andric static cl::opt<std::string> 110e8d8bef9SDimitry Andric ClMemoryAccessCallbackPrefix("memprof-memory-access-callback-prefix", 111e8d8bef9SDimitry Andric cl::desc("Prefix for memory access callbacks"), 112e8d8bef9SDimitry Andric cl::Hidden, cl::init("__memprof_")); 113e8d8bef9SDimitry Andric 114e8d8bef9SDimitry Andric // These flags allow to change the shadow mapping. 115e8d8bef9SDimitry Andric // The shadow mapping looks like 116e8d8bef9SDimitry Andric // Shadow = ((Mem & mask) >> scale) + offset 117e8d8bef9SDimitry Andric 118e8d8bef9SDimitry Andric static cl::opt<int> ClMappingScale("memprof-mapping-scale", 119e8d8bef9SDimitry Andric cl::desc("scale of memprof shadow mapping"), 120e8d8bef9SDimitry Andric cl::Hidden, cl::init(DefaultShadowScale)); 121e8d8bef9SDimitry Andric 122e8d8bef9SDimitry Andric static cl::opt<int> 123e8d8bef9SDimitry Andric ClMappingGranularity("memprof-mapping-granularity", 124e8d8bef9SDimitry Andric cl::desc("granularity of memprof shadow mapping"), 125*0fca6ea1SDimitry Andric cl::Hidden, cl::init(DefaultMemGranularity)); 126e8d8bef9SDimitry Andric 127349cc55cSDimitry Andric static cl::opt<bool> ClStack("memprof-instrument-stack", 128349cc55cSDimitry Andric cl::desc("Instrument scalar stack variables"), 129349cc55cSDimitry Andric cl::Hidden, cl::init(false)); 130349cc55cSDimitry Andric 131e8d8bef9SDimitry Andric // Debug flags. 132e8d8bef9SDimitry Andric 133e8d8bef9SDimitry Andric static cl::opt<int> ClDebug("memprof-debug", cl::desc("debug"), cl::Hidden, 134e8d8bef9SDimitry Andric cl::init(0)); 135e8d8bef9SDimitry Andric 136e8d8bef9SDimitry Andric static cl::opt<std::string> ClDebugFunc("memprof-debug-func", cl::Hidden, 137e8d8bef9SDimitry Andric cl::desc("Debug func")); 138e8d8bef9SDimitry Andric 139e8d8bef9SDimitry Andric static cl::opt<int> ClDebugMin("memprof-debug-min", cl::desc("Debug min inst"), 140e8d8bef9SDimitry Andric cl::Hidden, cl::init(-1)); 141e8d8bef9SDimitry Andric 142e8d8bef9SDimitry Andric static cl::opt<int> ClDebugMax("memprof-debug-max", cl::desc("Debug max inst"), 143e8d8bef9SDimitry Andric cl::Hidden, cl::init(-1)); 144e8d8bef9SDimitry Andric 145*0fca6ea1SDimitry Andric // By default disable matching of allocation profiles onto operator new that 146*0fca6ea1SDimitry Andric // already explicitly pass a hot/cold hint, since we don't currently 147*0fca6ea1SDimitry Andric // override these hints anyway. 148*0fca6ea1SDimitry Andric static cl::opt<bool> ClMemProfMatchHotColdNew( 149*0fca6ea1SDimitry Andric "memprof-match-hot-cold-new", 150*0fca6ea1SDimitry Andric cl::desc( 151*0fca6ea1SDimitry Andric "Match allocation profiles onto existing hot/cold operator new calls"), 152*0fca6ea1SDimitry Andric cl::Hidden, cl::init(false)); 153*0fca6ea1SDimitry Andric 154*0fca6ea1SDimitry Andric static cl::opt<bool> ClHistogram("memprof-histogram", 155*0fca6ea1SDimitry Andric cl::desc("Collect access count histograms"), 156*0fca6ea1SDimitry Andric cl::Hidden, cl::init(false)); 157*0fca6ea1SDimitry Andric 158*0fca6ea1SDimitry Andric static cl::opt<bool> 159*0fca6ea1SDimitry Andric ClPrintMemProfMatchInfo("memprof-print-match-info", 160*0fca6ea1SDimitry Andric cl::desc("Print matching stats for each allocation " 161*0fca6ea1SDimitry Andric "context in this module's profiles"), 162*0fca6ea1SDimitry Andric cl::Hidden, cl::init(false)); 163*0fca6ea1SDimitry Andric 164*0fca6ea1SDimitry Andric extern cl::opt<bool> MemProfReportHintedSizes; 165*0fca6ea1SDimitry Andric 166*0fca6ea1SDimitry Andric // Instrumentation statistics 167e8d8bef9SDimitry Andric STATISTIC(NumInstrumentedReads, "Number of instrumented reads"); 168e8d8bef9SDimitry Andric STATISTIC(NumInstrumentedWrites, "Number of instrumented writes"); 169349cc55cSDimitry Andric STATISTIC(NumSkippedStackReads, "Number of non-instrumented stack reads"); 170349cc55cSDimitry Andric STATISTIC(NumSkippedStackWrites, "Number of non-instrumented stack writes"); 171*0fca6ea1SDimitry Andric 172*0fca6ea1SDimitry Andric // Matching statistics 17306c3fb27SDimitry Andric STATISTIC(NumOfMemProfMissing, "Number of functions without memory profile."); 174*0fca6ea1SDimitry Andric STATISTIC(NumOfMemProfMismatch, 175*0fca6ea1SDimitry Andric "Number of functions having mismatched memory profile hash."); 176*0fca6ea1SDimitry Andric STATISTIC(NumOfMemProfFunc, "Number of functions having valid memory profile."); 177*0fca6ea1SDimitry Andric STATISTIC(NumOfMemProfAllocContextProfiles, 178*0fca6ea1SDimitry Andric "Number of alloc contexts in memory profile."); 179*0fca6ea1SDimitry Andric STATISTIC(NumOfMemProfCallSiteProfiles, 180*0fca6ea1SDimitry Andric "Number of callsites in memory profile."); 181*0fca6ea1SDimitry Andric STATISTIC(NumOfMemProfMatchedAllocContexts, 182*0fca6ea1SDimitry Andric "Number of matched memory profile alloc contexts."); 183*0fca6ea1SDimitry Andric STATISTIC(NumOfMemProfMatchedAllocs, 184*0fca6ea1SDimitry Andric "Number of matched memory profile allocs."); 185*0fca6ea1SDimitry Andric STATISTIC(NumOfMemProfMatchedCallSites, 186*0fca6ea1SDimitry Andric "Number of matched memory profile callsites."); 187e8d8bef9SDimitry Andric 188e8d8bef9SDimitry Andric namespace { 189e8d8bef9SDimitry Andric 190e8d8bef9SDimitry Andric /// This struct defines the shadow mapping using the rule: 191e8d8bef9SDimitry Andric /// shadow = ((mem & mask) >> Scale) ADD DynamicShadowOffset. 192e8d8bef9SDimitry Andric struct ShadowMapping { 193e8d8bef9SDimitry Andric ShadowMapping() { 194e8d8bef9SDimitry Andric Scale = ClMappingScale; 195e8d8bef9SDimitry Andric Granularity = ClMappingGranularity; 196e8d8bef9SDimitry Andric Mask = ~(Granularity - 1); 197e8d8bef9SDimitry Andric } 198e8d8bef9SDimitry Andric 199e8d8bef9SDimitry Andric int Scale; 200e8d8bef9SDimitry Andric int Granularity; 201e8d8bef9SDimitry Andric uint64_t Mask; // Computed as ~(Granularity-1) 202e8d8bef9SDimitry Andric }; 203e8d8bef9SDimitry Andric 204e8d8bef9SDimitry Andric static uint64_t getCtorAndDtorPriority(Triple &TargetTriple) { 205e8d8bef9SDimitry Andric return TargetTriple.isOSEmscripten() ? MemProfEmscriptenCtorAndDtorPriority 206e8d8bef9SDimitry Andric : MemProfCtorAndDtorPriority; 207e8d8bef9SDimitry Andric } 208e8d8bef9SDimitry Andric 209e8d8bef9SDimitry Andric struct InterestingMemoryAccess { 210e8d8bef9SDimitry Andric Value *Addr = nullptr; 211e8d8bef9SDimitry Andric bool IsWrite; 21204eeddc0SDimitry Andric Type *AccessTy; 213e8d8bef9SDimitry Andric Value *MaybeMask = nullptr; 214e8d8bef9SDimitry Andric }; 215e8d8bef9SDimitry Andric 216e8d8bef9SDimitry Andric /// Instrument the code in module to profile memory accesses. 217e8d8bef9SDimitry Andric class MemProfiler { 218e8d8bef9SDimitry Andric public: 219e8d8bef9SDimitry Andric MemProfiler(Module &M) { 220e8d8bef9SDimitry Andric C = &(M.getContext()); 221e8d8bef9SDimitry Andric LongSize = M.getDataLayout().getPointerSizeInBits(); 222e8d8bef9SDimitry Andric IntptrTy = Type::getIntNTy(*C, LongSize); 2235f757f3fSDimitry Andric PtrTy = PointerType::getUnqual(*C); 224e8d8bef9SDimitry Andric } 225e8d8bef9SDimitry Andric 226e8d8bef9SDimitry Andric /// If it is an interesting memory access, populate information 227e8d8bef9SDimitry Andric /// about the access and return a InterestingMemoryAccess struct. 228bdd1243dSDimitry Andric /// Otherwise return std::nullopt. 229bdd1243dSDimitry Andric std::optional<InterestingMemoryAccess> 230e8d8bef9SDimitry Andric isInterestingMemoryAccess(Instruction *I) const; 231e8d8bef9SDimitry Andric 232e8d8bef9SDimitry Andric void instrumentMop(Instruction *I, const DataLayout &DL, 233e8d8bef9SDimitry Andric InterestingMemoryAccess &Access); 234e8d8bef9SDimitry Andric void instrumentAddress(Instruction *OrigIns, Instruction *InsertBefore, 235*0fca6ea1SDimitry Andric Value *Addr, bool IsWrite); 236e8d8bef9SDimitry Andric void instrumentMaskedLoadOrStore(const DataLayout &DL, Value *Mask, 23781ad6265SDimitry Andric Instruction *I, Value *Addr, Type *AccessTy, 238e8d8bef9SDimitry Andric bool IsWrite); 239e8d8bef9SDimitry Andric void instrumentMemIntrinsic(MemIntrinsic *MI); 240e8d8bef9SDimitry Andric Value *memToShadow(Value *Shadow, IRBuilder<> &IRB); 241e8d8bef9SDimitry Andric bool instrumentFunction(Function &F); 242e8d8bef9SDimitry Andric bool maybeInsertMemProfInitAtFunctionEntry(Function &F); 243e8d8bef9SDimitry Andric bool insertDynamicShadowAtFunctionEntry(Function &F); 244e8d8bef9SDimitry Andric 245e8d8bef9SDimitry Andric private: 246e8d8bef9SDimitry Andric void initializeCallbacks(Module &M); 247e8d8bef9SDimitry Andric 248e8d8bef9SDimitry Andric LLVMContext *C; 249e8d8bef9SDimitry Andric int LongSize; 250e8d8bef9SDimitry Andric Type *IntptrTy; 2515f757f3fSDimitry Andric PointerType *PtrTy; 252e8d8bef9SDimitry Andric ShadowMapping Mapping; 253e8d8bef9SDimitry Andric 254e8d8bef9SDimitry Andric // These arrays is indexed by AccessIsWrite 255e8d8bef9SDimitry Andric FunctionCallee MemProfMemoryAccessCallback[2]; 256e8d8bef9SDimitry Andric 257e8d8bef9SDimitry Andric FunctionCallee MemProfMemmove, MemProfMemcpy, MemProfMemset; 258e8d8bef9SDimitry Andric Value *DynamicShadowOffset = nullptr; 259e8d8bef9SDimitry Andric }; 260e8d8bef9SDimitry Andric 261e8d8bef9SDimitry Andric class ModuleMemProfiler { 262e8d8bef9SDimitry Andric public: 263e8d8bef9SDimitry Andric ModuleMemProfiler(Module &M) { TargetTriple = Triple(M.getTargetTriple()); } 264e8d8bef9SDimitry Andric 265e8d8bef9SDimitry Andric bool instrumentModule(Module &); 266e8d8bef9SDimitry Andric 267e8d8bef9SDimitry Andric private: 268e8d8bef9SDimitry Andric Triple TargetTriple; 269e8d8bef9SDimitry Andric ShadowMapping Mapping; 270e8d8bef9SDimitry Andric Function *MemProfCtorFunction = nullptr; 271e8d8bef9SDimitry Andric }; 272e8d8bef9SDimitry Andric 273e8d8bef9SDimitry Andric } // end anonymous namespace 274e8d8bef9SDimitry Andric 27581ad6265SDimitry Andric MemProfilerPass::MemProfilerPass() = default; 276e8d8bef9SDimitry Andric 277e8d8bef9SDimitry Andric PreservedAnalyses MemProfilerPass::run(Function &F, 278e8d8bef9SDimitry Andric AnalysisManager<Function> &AM) { 279e8d8bef9SDimitry Andric Module &M = *F.getParent(); 280e8d8bef9SDimitry Andric MemProfiler Profiler(M); 281e8d8bef9SDimitry Andric if (Profiler.instrumentFunction(F)) 282e8d8bef9SDimitry Andric return PreservedAnalyses::none(); 283e8d8bef9SDimitry Andric return PreservedAnalyses::all(); 284e8d8bef9SDimitry Andric } 285e8d8bef9SDimitry Andric 28681ad6265SDimitry Andric ModuleMemProfilerPass::ModuleMemProfilerPass() = default; 287e8d8bef9SDimitry Andric 288e8d8bef9SDimitry Andric PreservedAnalyses ModuleMemProfilerPass::run(Module &M, 289e8d8bef9SDimitry Andric AnalysisManager<Module> &AM) { 290*0fca6ea1SDimitry Andric 291*0fca6ea1SDimitry Andric assert((!ClHistogram || (ClHistogram && ClUseCalls)) && 292*0fca6ea1SDimitry Andric "Cannot use -memprof-histogram without Callbacks. Set " 293*0fca6ea1SDimitry Andric "memprof-use-callbacks"); 294*0fca6ea1SDimitry Andric 295e8d8bef9SDimitry Andric ModuleMemProfiler Profiler(M); 296e8d8bef9SDimitry Andric if (Profiler.instrumentModule(M)) 297e8d8bef9SDimitry Andric return PreservedAnalyses::none(); 298e8d8bef9SDimitry Andric return PreservedAnalyses::all(); 299e8d8bef9SDimitry Andric } 300e8d8bef9SDimitry Andric 301e8d8bef9SDimitry Andric Value *MemProfiler::memToShadow(Value *Shadow, IRBuilder<> &IRB) { 302e8d8bef9SDimitry Andric // (Shadow & mask) >> scale 303e8d8bef9SDimitry Andric Shadow = IRB.CreateAnd(Shadow, Mapping.Mask); 304e8d8bef9SDimitry Andric Shadow = IRB.CreateLShr(Shadow, Mapping.Scale); 305e8d8bef9SDimitry Andric // (Shadow >> scale) | offset 306e8d8bef9SDimitry Andric assert(DynamicShadowOffset); 307e8d8bef9SDimitry Andric return IRB.CreateAdd(Shadow, DynamicShadowOffset); 308e8d8bef9SDimitry Andric } 309e8d8bef9SDimitry Andric 310e8d8bef9SDimitry Andric // Instrument memset/memmove/memcpy 311e8d8bef9SDimitry Andric void MemProfiler::instrumentMemIntrinsic(MemIntrinsic *MI) { 312e8d8bef9SDimitry Andric IRBuilder<> IRB(MI); 313e8d8bef9SDimitry Andric if (isa<MemTransferInst>(MI)) { 3145f757f3fSDimitry Andric IRB.CreateCall(isa<MemMoveInst>(MI) ? MemProfMemmove : MemProfMemcpy, 3155f757f3fSDimitry Andric {MI->getOperand(0), MI->getOperand(1), 316e8d8bef9SDimitry Andric IRB.CreateIntCast(MI->getOperand(2), IntptrTy, false)}); 317e8d8bef9SDimitry Andric } else if (isa<MemSetInst>(MI)) { 318e8d8bef9SDimitry Andric IRB.CreateCall( 319e8d8bef9SDimitry Andric MemProfMemset, 3205f757f3fSDimitry Andric {MI->getOperand(0), 321e8d8bef9SDimitry Andric IRB.CreateIntCast(MI->getOperand(1), IRB.getInt32Ty(), false), 322e8d8bef9SDimitry Andric IRB.CreateIntCast(MI->getOperand(2), IntptrTy, false)}); 323e8d8bef9SDimitry Andric } 324e8d8bef9SDimitry Andric MI->eraseFromParent(); 325e8d8bef9SDimitry Andric } 326e8d8bef9SDimitry Andric 327bdd1243dSDimitry Andric std::optional<InterestingMemoryAccess> 328e8d8bef9SDimitry Andric MemProfiler::isInterestingMemoryAccess(Instruction *I) const { 329e8d8bef9SDimitry Andric // Do not instrument the load fetching the dynamic shadow address. 330e8d8bef9SDimitry Andric if (DynamicShadowOffset == I) 331bdd1243dSDimitry Andric return std::nullopt; 332e8d8bef9SDimitry Andric 333e8d8bef9SDimitry Andric InterestingMemoryAccess Access; 334e8d8bef9SDimitry Andric 335e8d8bef9SDimitry Andric if (LoadInst *LI = dyn_cast<LoadInst>(I)) { 336e8d8bef9SDimitry Andric if (!ClInstrumentReads) 337bdd1243dSDimitry Andric return std::nullopt; 338e8d8bef9SDimitry Andric Access.IsWrite = false; 33904eeddc0SDimitry Andric Access.AccessTy = LI->getType(); 340e8d8bef9SDimitry Andric Access.Addr = LI->getPointerOperand(); 341e8d8bef9SDimitry Andric } else if (StoreInst *SI = dyn_cast<StoreInst>(I)) { 342e8d8bef9SDimitry Andric if (!ClInstrumentWrites) 343bdd1243dSDimitry Andric return std::nullopt; 344e8d8bef9SDimitry Andric Access.IsWrite = true; 34504eeddc0SDimitry Andric Access.AccessTy = SI->getValueOperand()->getType(); 346e8d8bef9SDimitry Andric Access.Addr = SI->getPointerOperand(); 347e8d8bef9SDimitry Andric } else if (AtomicRMWInst *RMW = dyn_cast<AtomicRMWInst>(I)) { 348e8d8bef9SDimitry Andric if (!ClInstrumentAtomics) 349bdd1243dSDimitry Andric return std::nullopt; 350e8d8bef9SDimitry Andric Access.IsWrite = true; 35104eeddc0SDimitry Andric Access.AccessTy = RMW->getValOperand()->getType(); 352e8d8bef9SDimitry Andric Access.Addr = RMW->getPointerOperand(); 353e8d8bef9SDimitry Andric } else if (AtomicCmpXchgInst *XCHG = dyn_cast<AtomicCmpXchgInst>(I)) { 354e8d8bef9SDimitry Andric if (!ClInstrumentAtomics) 355bdd1243dSDimitry Andric return std::nullopt; 356e8d8bef9SDimitry Andric Access.IsWrite = true; 35704eeddc0SDimitry Andric Access.AccessTy = XCHG->getCompareOperand()->getType(); 358e8d8bef9SDimitry Andric Access.Addr = XCHG->getPointerOperand(); 359e8d8bef9SDimitry Andric } else if (auto *CI = dyn_cast<CallInst>(I)) { 360e8d8bef9SDimitry Andric auto *F = CI->getCalledFunction(); 361e8d8bef9SDimitry Andric if (F && (F->getIntrinsicID() == Intrinsic::masked_load || 362e8d8bef9SDimitry Andric F->getIntrinsicID() == Intrinsic::masked_store)) { 363e8d8bef9SDimitry Andric unsigned OpOffset = 0; 364e8d8bef9SDimitry Andric if (F->getIntrinsicID() == Intrinsic::masked_store) { 365e8d8bef9SDimitry Andric if (!ClInstrumentWrites) 366bdd1243dSDimitry Andric return std::nullopt; 367e8d8bef9SDimitry Andric // Masked store has an initial operand for the value. 368e8d8bef9SDimitry Andric OpOffset = 1; 36904eeddc0SDimitry Andric Access.AccessTy = CI->getArgOperand(0)->getType(); 370e8d8bef9SDimitry Andric Access.IsWrite = true; 371e8d8bef9SDimitry Andric } else { 372e8d8bef9SDimitry Andric if (!ClInstrumentReads) 373bdd1243dSDimitry Andric return std::nullopt; 37404eeddc0SDimitry Andric Access.AccessTy = CI->getType(); 375e8d8bef9SDimitry Andric Access.IsWrite = false; 376e8d8bef9SDimitry Andric } 377e8d8bef9SDimitry Andric 378e8d8bef9SDimitry Andric auto *BasePtr = CI->getOperand(0 + OpOffset); 379e8d8bef9SDimitry Andric Access.MaybeMask = CI->getOperand(2 + OpOffset); 380e8d8bef9SDimitry Andric Access.Addr = BasePtr; 381e8d8bef9SDimitry Andric } 382e8d8bef9SDimitry Andric } 383e8d8bef9SDimitry Andric 384e8d8bef9SDimitry Andric if (!Access.Addr) 385bdd1243dSDimitry Andric return std::nullopt; 386e8d8bef9SDimitry Andric 387bdd1243dSDimitry Andric // Do not instrument accesses from different address spaces; we cannot deal 388e8d8bef9SDimitry Andric // with them. 389e8d8bef9SDimitry Andric Type *PtrTy = cast<PointerType>(Access.Addr->getType()->getScalarType()); 390e8d8bef9SDimitry Andric if (PtrTy->getPointerAddressSpace() != 0) 391bdd1243dSDimitry Andric return std::nullopt; 392e8d8bef9SDimitry Andric 393e8d8bef9SDimitry Andric // Ignore swifterror addresses. 394e8d8bef9SDimitry Andric // swifterror memory addresses are mem2reg promoted by instruction 395e8d8bef9SDimitry Andric // selection. As such they cannot have regular uses like an instrumentation 396e8d8bef9SDimitry Andric // function and it makes no sense to track them as memory. 397e8d8bef9SDimitry Andric if (Access.Addr->isSwiftError()) 398bdd1243dSDimitry Andric return std::nullopt; 399e8d8bef9SDimitry Andric 40081ad6265SDimitry Andric // Peel off GEPs and BitCasts. 40181ad6265SDimitry Andric auto *Addr = Access.Addr->stripInBoundsOffsets(); 40281ad6265SDimitry Andric 40381ad6265SDimitry Andric if (GlobalVariable *GV = dyn_cast<GlobalVariable>(Addr)) { 40481ad6265SDimitry Andric // Do not instrument PGO counter updates. 40581ad6265SDimitry Andric if (GV->hasSection()) { 40681ad6265SDimitry Andric StringRef SectionName = GV->getSection(); 40781ad6265SDimitry Andric // Check if the global is in the PGO counters section. 40881ad6265SDimitry Andric auto OF = Triple(I->getModule()->getTargetTriple()).getObjectFormat(); 4095f757f3fSDimitry Andric if (SectionName.ends_with( 41081ad6265SDimitry Andric getInstrProfSectionName(IPSK_cnts, OF, /*AddSegmentInfo=*/false))) 411bdd1243dSDimitry Andric return std::nullopt; 41281ad6265SDimitry Andric } 41381ad6265SDimitry Andric 41481ad6265SDimitry Andric // Do not instrument accesses to LLVM internal variables. 4155f757f3fSDimitry Andric if (GV->getName().starts_with("__llvm")) 416bdd1243dSDimitry Andric return std::nullopt; 41781ad6265SDimitry Andric } 41881ad6265SDimitry Andric 419e8d8bef9SDimitry Andric return Access; 420e8d8bef9SDimitry Andric } 421e8d8bef9SDimitry Andric 422e8d8bef9SDimitry Andric void MemProfiler::instrumentMaskedLoadOrStore(const DataLayout &DL, Value *Mask, 423e8d8bef9SDimitry Andric Instruction *I, Value *Addr, 42404eeddc0SDimitry Andric Type *AccessTy, bool IsWrite) { 42504eeddc0SDimitry Andric auto *VTy = cast<FixedVectorType>(AccessTy); 426e8d8bef9SDimitry Andric unsigned Num = VTy->getNumElements(); 427e8d8bef9SDimitry Andric auto *Zero = ConstantInt::get(IntptrTy, 0); 428e8d8bef9SDimitry Andric for (unsigned Idx = 0; Idx < Num; ++Idx) { 429e8d8bef9SDimitry Andric Value *InstrumentedAddress = nullptr; 430e8d8bef9SDimitry Andric Instruction *InsertBefore = I; 431e8d8bef9SDimitry Andric if (auto *Vector = dyn_cast<ConstantVector>(Mask)) { 432e8d8bef9SDimitry Andric // dyn_cast as we might get UndefValue 433e8d8bef9SDimitry Andric if (auto *Masked = dyn_cast<ConstantInt>(Vector->getOperand(Idx))) { 434e8d8bef9SDimitry Andric if (Masked->isZero()) 435e8d8bef9SDimitry Andric // Mask is constant false, so no instrumentation needed. 436e8d8bef9SDimitry Andric continue; 437e8d8bef9SDimitry Andric // If we have a true or undef value, fall through to instrumentAddress. 438e8d8bef9SDimitry Andric // with InsertBefore == I 439e8d8bef9SDimitry Andric } 440e8d8bef9SDimitry Andric } else { 441e8d8bef9SDimitry Andric IRBuilder<> IRB(I); 442e8d8bef9SDimitry Andric Value *MaskElem = IRB.CreateExtractElement(Mask, Idx); 443e8d8bef9SDimitry Andric Instruction *ThenTerm = SplitBlockAndInsertIfThen(MaskElem, I, false); 444e8d8bef9SDimitry Andric InsertBefore = ThenTerm; 445e8d8bef9SDimitry Andric } 446e8d8bef9SDimitry Andric 447e8d8bef9SDimitry Andric IRBuilder<> IRB(InsertBefore); 448e8d8bef9SDimitry Andric InstrumentedAddress = 449e8d8bef9SDimitry Andric IRB.CreateGEP(VTy, Addr, {Zero, ConstantInt::get(IntptrTy, Idx)}); 450*0fca6ea1SDimitry Andric instrumentAddress(I, InsertBefore, InstrumentedAddress, IsWrite); 451e8d8bef9SDimitry Andric } 452e8d8bef9SDimitry Andric } 453e8d8bef9SDimitry Andric 454e8d8bef9SDimitry Andric void MemProfiler::instrumentMop(Instruction *I, const DataLayout &DL, 455e8d8bef9SDimitry Andric InterestingMemoryAccess &Access) { 456349cc55cSDimitry Andric // Skip instrumentation of stack accesses unless requested. 457349cc55cSDimitry Andric if (!ClStack && isa<AllocaInst>(getUnderlyingObject(Access.Addr))) { 458349cc55cSDimitry Andric if (Access.IsWrite) 459349cc55cSDimitry Andric ++NumSkippedStackWrites; 460349cc55cSDimitry Andric else 461349cc55cSDimitry Andric ++NumSkippedStackReads; 462349cc55cSDimitry Andric return; 463349cc55cSDimitry Andric } 464349cc55cSDimitry Andric 465e8d8bef9SDimitry Andric if (Access.IsWrite) 466e8d8bef9SDimitry Andric NumInstrumentedWrites++; 467e8d8bef9SDimitry Andric else 468e8d8bef9SDimitry Andric NumInstrumentedReads++; 469e8d8bef9SDimitry Andric 470e8d8bef9SDimitry Andric if (Access.MaybeMask) { 471e8d8bef9SDimitry Andric instrumentMaskedLoadOrStore(DL, Access.MaybeMask, I, Access.Addr, 47281ad6265SDimitry Andric Access.AccessTy, Access.IsWrite); 473e8d8bef9SDimitry Andric } else { 474e8d8bef9SDimitry Andric // Since the access counts will be accumulated across the entire allocation, 475e8d8bef9SDimitry Andric // we only update the shadow access count for the first location and thus 476e8d8bef9SDimitry Andric // don't need to worry about alignment and type size. 477*0fca6ea1SDimitry Andric instrumentAddress(I, I, Access.Addr, Access.IsWrite); 478e8d8bef9SDimitry Andric } 479e8d8bef9SDimitry Andric } 480e8d8bef9SDimitry Andric 481e8d8bef9SDimitry Andric void MemProfiler::instrumentAddress(Instruction *OrigIns, 482e8d8bef9SDimitry Andric Instruction *InsertBefore, Value *Addr, 483*0fca6ea1SDimitry Andric bool IsWrite) { 484e8d8bef9SDimitry Andric IRBuilder<> IRB(InsertBefore); 485e8d8bef9SDimitry Andric Value *AddrLong = IRB.CreatePointerCast(Addr, IntptrTy); 486e8d8bef9SDimitry Andric 487e8d8bef9SDimitry Andric if (ClUseCalls) { 488e8d8bef9SDimitry Andric IRB.CreateCall(MemProfMemoryAccessCallback[IsWrite], AddrLong); 489e8d8bef9SDimitry Andric return; 490e8d8bef9SDimitry Andric } 491e8d8bef9SDimitry Andric 492e8d8bef9SDimitry Andric // Create an inline sequence to compute shadow location, and increment the 493e8d8bef9SDimitry Andric // value by one. 494e8d8bef9SDimitry Andric Type *ShadowTy = Type::getInt64Ty(*C); 495e8d8bef9SDimitry Andric Type *ShadowPtrTy = PointerType::get(ShadowTy, 0); 496e8d8bef9SDimitry Andric Value *ShadowPtr = memToShadow(AddrLong, IRB); 497e8d8bef9SDimitry Andric Value *ShadowAddr = IRB.CreateIntToPtr(ShadowPtr, ShadowPtrTy); 498e8d8bef9SDimitry Andric Value *ShadowValue = IRB.CreateLoad(ShadowTy, ShadowAddr); 499e8d8bef9SDimitry Andric Value *Inc = ConstantInt::get(Type::getInt64Ty(*C), 1); 500e8d8bef9SDimitry Andric ShadowValue = IRB.CreateAdd(ShadowValue, Inc); 501e8d8bef9SDimitry Andric IRB.CreateStore(ShadowValue, ShadowAddr); 502e8d8bef9SDimitry Andric } 503e8d8bef9SDimitry Andric 504e8d8bef9SDimitry Andric // Create the variable for the profile file name. 505e8d8bef9SDimitry Andric void createProfileFileNameVar(Module &M) { 506e8d8bef9SDimitry Andric const MDString *MemProfFilename = 507e8d8bef9SDimitry Andric dyn_cast_or_null<MDString>(M.getModuleFlag("MemProfProfileFilename")); 508e8d8bef9SDimitry Andric if (!MemProfFilename) 509e8d8bef9SDimitry Andric return; 510e8d8bef9SDimitry Andric assert(!MemProfFilename->getString().empty() && 511e8d8bef9SDimitry Andric "Unexpected MemProfProfileFilename metadata with empty string"); 512e8d8bef9SDimitry Andric Constant *ProfileNameConst = ConstantDataArray::getString( 513e8d8bef9SDimitry Andric M.getContext(), MemProfFilename->getString(), true); 514e8d8bef9SDimitry Andric GlobalVariable *ProfileNameVar = new GlobalVariable( 515e8d8bef9SDimitry Andric M, ProfileNameConst->getType(), /*isConstant=*/true, 516e8d8bef9SDimitry Andric GlobalValue::WeakAnyLinkage, ProfileNameConst, MemProfFilenameVar); 517e8d8bef9SDimitry Andric Triple TT(M.getTargetTriple()); 518e8d8bef9SDimitry Andric if (TT.supportsCOMDAT()) { 519e8d8bef9SDimitry Andric ProfileNameVar->setLinkage(GlobalValue::ExternalLinkage); 520e8d8bef9SDimitry Andric ProfileNameVar->setComdat(M.getOrInsertComdat(MemProfFilenameVar)); 521e8d8bef9SDimitry Andric } 522e8d8bef9SDimitry Andric } 523e8d8bef9SDimitry Andric 524*0fca6ea1SDimitry Andric // Set MemprofHistogramFlag as a Global veriable in IR. This makes it accessible 525*0fca6ea1SDimitry Andric // to the runtime, changing shadow count behavior. 526*0fca6ea1SDimitry Andric void createMemprofHistogramFlagVar(Module &M) { 527*0fca6ea1SDimitry Andric const StringRef VarName(MemProfHistogramFlagVar); 528*0fca6ea1SDimitry Andric Type *IntTy1 = Type::getInt1Ty(M.getContext()); 529*0fca6ea1SDimitry Andric auto MemprofHistogramFlag = new GlobalVariable( 530*0fca6ea1SDimitry Andric M, IntTy1, true, GlobalValue::WeakAnyLinkage, 531*0fca6ea1SDimitry Andric Constant::getIntegerValue(IntTy1, APInt(1, ClHistogram)), VarName); 532*0fca6ea1SDimitry Andric Triple TT(M.getTargetTriple()); 533*0fca6ea1SDimitry Andric if (TT.supportsCOMDAT()) { 534*0fca6ea1SDimitry Andric MemprofHistogramFlag->setLinkage(GlobalValue::ExternalLinkage); 535*0fca6ea1SDimitry Andric MemprofHistogramFlag->setComdat(M.getOrInsertComdat(VarName)); 536*0fca6ea1SDimitry Andric } 537*0fca6ea1SDimitry Andric appendToCompilerUsed(M, MemprofHistogramFlag); 538*0fca6ea1SDimitry Andric } 539*0fca6ea1SDimitry Andric 540e8d8bef9SDimitry Andric bool ModuleMemProfiler::instrumentModule(Module &M) { 541*0fca6ea1SDimitry Andric 542e8d8bef9SDimitry Andric // Create a module constructor. 543e8d8bef9SDimitry Andric std::string MemProfVersion = std::to_string(LLVM_MEM_PROFILER_VERSION); 544e8d8bef9SDimitry Andric std::string VersionCheckName = 545e8d8bef9SDimitry Andric ClInsertVersionCheck ? (MemProfVersionCheckNamePrefix + MemProfVersion) 546e8d8bef9SDimitry Andric : ""; 547e8d8bef9SDimitry Andric std::tie(MemProfCtorFunction, std::ignore) = 548e8d8bef9SDimitry Andric createSanitizerCtorAndInitFunctions(M, MemProfModuleCtorName, 549e8d8bef9SDimitry Andric MemProfInitName, /*InitArgTypes=*/{}, 550e8d8bef9SDimitry Andric /*InitArgs=*/{}, VersionCheckName); 551e8d8bef9SDimitry Andric 552e8d8bef9SDimitry Andric const uint64_t Priority = getCtorAndDtorPriority(TargetTriple); 553e8d8bef9SDimitry Andric appendToGlobalCtors(M, MemProfCtorFunction, Priority); 554e8d8bef9SDimitry Andric 555e8d8bef9SDimitry Andric createProfileFileNameVar(M); 556e8d8bef9SDimitry Andric 557*0fca6ea1SDimitry Andric createMemprofHistogramFlagVar(M); 558*0fca6ea1SDimitry Andric 559e8d8bef9SDimitry Andric return true; 560e8d8bef9SDimitry Andric } 561e8d8bef9SDimitry Andric 562e8d8bef9SDimitry Andric void MemProfiler::initializeCallbacks(Module &M) { 563e8d8bef9SDimitry Andric IRBuilder<> IRB(*C); 564e8d8bef9SDimitry Andric 565e8d8bef9SDimitry Andric for (size_t AccessIsWrite = 0; AccessIsWrite <= 1; AccessIsWrite++) { 566e8d8bef9SDimitry Andric const std::string TypeStr = AccessIsWrite ? "store" : "load"; 567*0fca6ea1SDimitry Andric const std::string HistPrefix = ClHistogram ? "hist_" : ""; 568e8d8bef9SDimitry Andric 569e8d8bef9SDimitry Andric SmallVector<Type *, 2> Args1{1, IntptrTy}; 570*0fca6ea1SDimitry Andric MemProfMemoryAccessCallback[AccessIsWrite] = M.getOrInsertFunction( 571*0fca6ea1SDimitry Andric ClMemoryAccessCallbackPrefix + HistPrefix + TypeStr, 572e8d8bef9SDimitry Andric FunctionType::get(IRB.getVoidTy(), Args1, false)); 573e8d8bef9SDimitry Andric } 574e8d8bef9SDimitry Andric MemProfMemmove = M.getOrInsertFunction( 5755f757f3fSDimitry Andric ClMemoryAccessCallbackPrefix + "memmove", PtrTy, PtrTy, PtrTy, IntptrTy); 576e8d8bef9SDimitry Andric MemProfMemcpy = M.getOrInsertFunction(ClMemoryAccessCallbackPrefix + "memcpy", 5775f757f3fSDimitry Andric PtrTy, PtrTy, PtrTy, IntptrTy); 5785f757f3fSDimitry Andric MemProfMemset = 5795f757f3fSDimitry Andric M.getOrInsertFunction(ClMemoryAccessCallbackPrefix + "memset", PtrTy, 5805f757f3fSDimitry Andric PtrTy, IRB.getInt32Ty(), IntptrTy); 581e8d8bef9SDimitry Andric } 582e8d8bef9SDimitry Andric 583e8d8bef9SDimitry Andric bool MemProfiler::maybeInsertMemProfInitAtFunctionEntry(Function &F) { 584e8d8bef9SDimitry Andric // For each NSObject descendant having a +load method, this method is invoked 585e8d8bef9SDimitry Andric // by the ObjC runtime before any of the static constructors is called. 586e8d8bef9SDimitry Andric // Therefore we need to instrument such methods with a call to __memprof_init 587e8d8bef9SDimitry Andric // at the beginning in order to initialize our runtime before any access to 588e8d8bef9SDimitry Andric // the shadow memory. 589e8d8bef9SDimitry Andric // We cannot just ignore these methods, because they may call other 590e8d8bef9SDimitry Andric // instrumented functions. 591cb14a3feSDimitry Andric if (F.getName().contains(" load]")) { 592e8d8bef9SDimitry Andric FunctionCallee MemProfInitFunction = 593e8d8bef9SDimitry Andric declareSanitizerInitFunction(*F.getParent(), MemProfInitName, {}); 594e8d8bef9SDimitry Andric IRBuilder<> IRB(&F.front(), F.front().begin()); 595e8d8bef9SDimitry Andric IRB.CreateCall(MemProfInitFunction, {}); 596e8d8bef9SDimitry Andric return true; 597e8d8bef9SDimitry Andric } 598e8d8bef9SDimitry Andric return false; 599e8d8bef9SDimitry Andric } 600e8d8bef9SDimitry Andric 601e8d8bef9SDimitry Andric bool MemProfiler::insertDynamicShadowAtFunctionEntry(Function &F) { 602e8d8bef9SDimitry Andric IRBuilder<> IRB(&F.front().front()); 603e8d8bef9SDimitry Andric Value *GlobalDynamicAddress = F.getParent()->getOrInsertGlobal( 604e8d8bef9SDimitry Andric MemProfShadowMemoryDynamicAddress, IntptrTy); 605e8d8bef9SDimitry Andric if (F.getParent()->getPICLevel() == PICLevel::NotPIC) 606e8d8bef9SDimitry Andric cast<GlobalVariable>(GlobalDynamicAddress)->setDSOLocal(true); 607e8d8bef9SDimitry Andric DynamicShadowOffset = IRB.CreateLoad(IntptrTy, GlobalDynamicAddress); 608e8d8bef9SDimitry Andric return true; 609e8d8bef9SDimitry Andric } 610e8d8bef9SDimitry Andric 611e8d8bef9SDimitry Andric bool MemProfiler::instrumentFunction(Function &F) { 612e8d8bef9SDimitry Andric if (F.getLinkage() == GlobalValue::AvailableExternallyLinkage) 613e8d8bef9SDimitry Andric return false; 614e8d8bef9SDimitry Andric if (ClDebugFunc == F.getName()) 615e8d8bef9SDimitry Andric return false; 6165f757f3fSDimitry Andric if (F.getName().starts_with("__memprof_")) 617e8d8bef9SDimitry Andric return false; 618e8d8bef9SDimitry Andric 619e8d8bef9SDimitry Andric bool FunctionModified = false; 620e8d8bef9SDimitry Andric 621e8d8bef9SDimitry Andric // If needed, insert __memprof_init. 622e8d8bef9SDimitry Andric // This function needs to be called even if the function body is not 623e8d8bef9SDimitry Andric // instrumented. 624e8d8bef9SDimitry Andric if (maybeInsertMemProfInitAtFunctionEntry(F)) 625e8d8bef9SDimitry Andric FunctionModified = true; 626e8d8bef9SDimitry Andric 627e8d8bef9SDimitry Andric LLVM_DEBUG(dbgs() << "MEMPROF instrumenting:\n" << F << "\n"); 628e8d8bef9SDimitry Andric 629e8d8bef9SDimitry Andric initializeCallbacks(*F.getParent()); 630e8d8bef9SDimitry Andric 631e8d8bef9SDimitry Andric SmallVector<Instruction *, 16> ToInstrument; 632e8d8bef9SDimitry Andric 633e8d8bef9SDimitry Andric // Fill the set of memory operations to instrument. 634e8d8bef9SDimitry Andric for (auto &BB : F) { 635e8d8bef9SDimitry Andric for (auto &Inst : BB) { 636e8d8bef9SDimitry Andric if (isInterestingMemoryAccess(&Inst) || isa<MemIntrinsic>(Inst)) 637e8d8bef9SDimitry Andric ToInstrument.push_back(&Inst); 638e8d8bef9SDimitry Andric } 639e8d8bef9SDimitry Andric } 640e8d8bef9SDimitry Andric 64181ad6265SDimitry Andric if (ToInstrument.empty()) { 64281ad6265SDimitry Andric LLVM_DEBUG(dbgs() << "MEMPROF done instrumenting: " << FunctionModified 64381ad6265SDimitry Andric << " " << F << "\n"); 64481ad6265SDimitry Andric 64581ad6265SDimitry Andric return FunctionModified; 64681ad6265SDimitry Andric } 64781ad6265SDimitry Andric 64881ad6265SDimitry Andric FunctionModified |= insertDynamicShadowAtFunctionEntry(F); 64981ad6265SDimitry Andric 650e8d8bef9SDimitry Andric int NumInstrumented = 0; 651e8d8bef9SDimitry Andric for (auto *Inst : ToInstrument) { 652e8d8bef9SDimitry Andric if (ClDebugMin < 0 || ClDebugMax < 0 || 653e8d8bef9SDimitry Andric (NumInstrumented >= ClDebugMin && NumInstrumented <= ClDebugMax)) { 654bdd1243dSDimitry Andric std::optional<InterestingMemoryAccess> Access = 655e8d8bef9SDimitry Andric isInterestingMemoryAccess(Inst); 656e8d8bef9SDimitry Andric if (Access) 657*0fca6ea1SDimitry Andric instrumentMop(Inst, F.getDataLayout(), *Access); 658e8d8bef9SDimitry Andric else 659e8d8bef9SDimitry Andric instrumentMemIntrinsic(cast<MemIntrinsic>(Inst)); 660e8d8bef9SDimitry Andric } 661e8d8bef9SDimitry Andric NumInstrumented++; 662e8d8bef9SDimitry Andric } 663e8d8bef9SDimitry Andric 664e8d8bef9SDimitry Andric if (NumInstrumented > 0) 665e8d8bef9SDimitry Andric FunctionModified = true; 666e8d8bef9SDimitry Andric 667e8d8bef9SDimitry Andric LLVM_DEBUG(dbgs() << "MEMPROF done instrumenting: " << FunctionModified << " " 668e8d8bef9SDimitry Andric << F << "\n"); 669e8d8bef9SDimitry Andric 670e8d8bef9SDimitry Andric return FunctionModified; 671e8d8bef9SDimitry Andric } 67206c3fb27SDimitry Andric 67306c3fb27SDimitry Andric static void addCallsiteMetadata(Instruction &I, 67406c3fb27SDimitry Andric std::vector<uint64_t> &InlinedCallStack, 67506c3fb27SDimitry Andric LLVMContext &Ctx) { 67606c3fb27SDimitry Andric I.setMetadata(LLVMContext::MD_callsite, 67706c3fb27SDimitry Andric buildCallstackMetadata(InlinedCallStack, Ctx)); 67806c3fb27SDimitry Andric } 67906c3fb27SDimitry Andric 68006c3fb27SDimitry Andric static uint64_t computeStackId(GlobalValue::GUID Function, uint32_t LineOffset, 68106c3fb27SDimitry Andric uint32_t Column) { 6825f757f3fSDimitry Andric llvm::HashBuilder<llvm::TruncatedBLAKE3<8>, llvm::endianness::little> 68306c3fb27SDimitry Andric HashBuilder; 68406c3fb27SDimitry Andric HashBuilder.add(Function, LineOffset, Column); 68506c3fb27SDimitry Andric llvm::BLAKE3Result<8> Hash = HashBuilder.final(); 68606c3fb27SDimitry Andric uint64_t Id; 68706c3fb27SDimitry Andric std::memcpy(&Id, Hash.data(), sizeof(Hash)); 68806c3fb27SDimitry Andric return Id; 68906c3fb27SDimitry Andric } 69006c3fb27SDimitry Andric 69106c3fb27SDimitry Andric static uint64_t computeStackId(const memprof::Frame &Frame) { 69206c3fb27SDimitry Andric return computeStackId(Frame.Function, Frame.LineOffset, Frame.Column); 69306c3fb27SDimitry Andric } 69406c3fb27SDimitry Andric 695*0fca6ea1SDimitry Andric // Helper to generate a single hash id for a given callstack, used for emitting 696*0fca6ea1SDimitry Andric // matching statistics and useful for uniquing such statistics across modules. 697*0fca6ea1SDimitry Andric static uint64_t 698*0fca6ea1SDimitry Andric computeFullStackId(const std::vector<memprof::Frame> &CallStack) { 699*0fca6ea1SDimitry Andric llvm::HashBuilder<llvm::TruncatedBLAKE3<8>, llvm::endianness::little> 700*0fca6ea1SDimitry Andric HashBuilder; 701*0fca6ea1SDimitry Andric for (auto &F : CallStack) 702*0fca6ea1SDimitry Andric HashBuilder.add(F.Function, F.LineOffset, F.Column); 703*0fca6ea1SDimitry Andric llvm::BLAKE3Result<8> Hash = HashBuilder.final(); 704*0fca6ea1SDimitry Andric uint64_t Id; 705*0fca6ea1SDimitry Andric std::memcpy(&Id, Hash.data(), sizeof(Hash)); 706*0fca6ea1SDimitry Andric return Id; 707*0fca6ea1SDimitry Andric } 708*0fca6ea1SDimitry Andric 709*0fca6ea1SDimitry Andric static AllocationType addCallStack(CallStackTrie &AllocTrie, 71006c3fb27SDimitry Andric const AllocationInfo *AllocInfo) { 71106c3fb27SDimitry Andric SmallVector<uint64_t> StackIds; 71206c3fb27SDimitry Andric for (const auto &StackFrame : AllocInfo->CallStack) 71306c3fb27SDimitry Andric StackIds.push_back(computeStackId(StackFrame)); 71406c3fb27SDimitry Andric auto AllocType = getAllocType(AllocInfo->Info.getTotalLifetimeAccessDensity(), 71506c3fb27SDimitry Andric AllocInfo->Info.getAllocCount(), 71606c3fb27SDimitry Andric AllocInfo->Info.getTotalLifetime()); 717*0fca6ea1SDimitry Andric uint64_t TotalSize = 0; 718*0fca6ea1SDimitry Andric if (MemProfReportHintedSizes) { 719*0fca6ea1SDimitry Andric TotalSize = AllocInfo->Info.getTotalSize(); 720*0fca6ea1SDimitry Andric assert(TotalSize); 721*0fca6ea1SDimitry Andric } 722*0fca6ea1SDimitry Andric AllocTrie.addCallStack(AllocType, StackIds, TotalSize); 723*0fca6ea1SDimitry Andric return AllocType; 72406c3fb27SDimitry Andric } 72506c3fb27SDimitry Andric 72606c3fb27SDimitry Andric // Helper to compare the InlinedCallStack computed from an instruction's debug 72706c3fb27SDimitry Andric // info to a list of Frames from profile data (either the allocation data or a 72806c3fb27SDimitry Andric // callsite). For callsites, the StartIndex to use in the Frame array may be 72906c3fb27SDimitry Andric // non-zero. 73006c3fb27SDimitry Andric static bool 73106c3fb27SDimitry Andric stackFrameIncludesInlinedCallStack(ArrayRef<Frame> ProfileCallStack, 73206c3fb27SDimitry Andric ArrayRef<uint64_t> InlinedCallStack, 73306c3fb27SDimitry Andric unsigned StartIndex = 0) { 73406c3fb27SDimitry Andric auto StackFrame = ProfileCallStack.begin() + StartIndex; 73506c3fb27SDimitry Andric auto InlCallStackIter = InlinedCallStack.begin(); 73606c3fb27SDimitry Andric for (; StackFrame != ProfileCallStack.end() && 73706c3fb27SDimitry Andric InlCallStackIter != InlinedCallStack.end(); 73806c3fb27SDimitry Andric ++StackFrame, ++InlCallStackIter) { 73906c3fb27SDimitry Andric uint64_t StackId = computeStackId(*StackFrame); 74006c3fb27SDimitry Andric if (StackId != *InlCallStackIter) 74106c3fb27SDimitry Andric return false; 74206c3fb27SDimitry Andric } 74306c3fb27SDimitry Andric // Return true if we found and matched all stack ids from the call 74406c3fb27SDimitry Andric // instruction. 74506c3fb27SDimitry Andric return InlCallStackIter == InlinedCallStack.end(); 74606c3fb27SDimitry Andric } 74706c3fb27SDimitry Andric 748*0fca6ea1SDimitry Andric static bool isNewWithHotColdVariant(Function *Callee, 74906c3fb27SDimitry Andric const TargetLibraryInfo &TLI) { 750*0fca6ea1SDimitry Andric if (!Callee) 751*0fca6ea1SDimitry Andric return false; 752*0fca6ea1SDimitry Andric LibFunc Func; 753*0fca6ea1SDimitry Andric if (!TLI.getLibFunc(*Callee, Func)) 754*0fca6ea1SDimitry Andric return false; 755*0fca6ea1SDimitry Andric switch (Func) { 756*0fca6ea1SDimitry Andric case LibFunc_Znwm: 757*0fca6ea1SDimitry Andric case LibFunc_ZnwmRKSt9nothrow_t: 758*0fca6ea1SDimitry Andric case LibFunc_ZnwmSt11align_val_t: 759*0fca6ea1SDimitry Andric case LibFunc_ZnwmSt11align_val_tRKSt9nothrow_t: 760*0fca6ea1SDimitry Andric case LibFunc_Znam: 761*0fca6ea1SDimitry Andric case LibFunc_ZnamRKSt9nothrow_t: 762*0fca6ea1SDimitry Andric case LibFunc_ZnamSt11align_val_t: 763*0fca6ea1SDimitry Andric case LibFunc_ZnamSt11align_val_tRKSt9nothrow_t: 764*0fca6ea1SDimitry Andric return true; 765*0fca6ea1SDimitry Andric case LibFunc_Znwm12__hot_cold_t: 766*0fca6ea1SDimitry Andric case LibFunc_ZnwmRKSt9nothrow_t12__hot_cold_t: 767*0fca6ea1SDimitry Andric case LibFunc_ZnwmSt11align_val_t12__hot_cold_t: 768*0fca6ea1SDimitry Andric case LibFunc_ZnwmSt11align_val_tRKSt9nothrow_t12__hot_cold_t: 769*0fca6ea1SDimitry Andric case LibFunc_Znam12__hot_cold_t: 770*0fca6ea1SDimitry Andric case LibFunc_ZnamRKSt9nothrow_t12__hot_cold_t: 771*0fca6ea1SDimitry Andric case LibFunc_ZnamSt11align_val_t12__hot_cold_t: 772*0fca6ea1SDimitry Andric case LibFunc_ZnamSt11align_val_tRKSt9nothrow_t12__hot_cold_t: 773*0fca6ea1SDimitry Andric return ClMemProfMatchHotColdNew; 774*0fca6ea1SDimitry Andric default: 775*0fca6ea1SDimitry Andric return false; 776*0fca6ea1SDimitry Andric } 777*0fca6ea1SDimitry Andric } 778*0fca6ea1SDimitry Andric 779*0fca6ea1SDimitry Andric struct AllocMatchInfo { 780*0fca6ea1SDimitry Andric uint64_t TotalSize = 0; 781*0fca6ea1SDimitry Andric AllocationType AllocType = AllocationType::None; 782*0fca6ea1SDimitry Andric bool Matched = false; 783*0fca6ea1SDimitry Andric }; 784*0fca6ea1SDimitry Andric 785*0fca6ea1SDimitry Andric static void 786*0fca6ea1SDimitry Andric readMemprof(Module &M, Function &F, IndexedInstrProfReader *MemProfReader, 787*0fca6ea1SDimitry Andric const TargetLibraryInfo &TLI, 788*0fca6ea1SDimitry Andric std::map<uint64_t, AllocMatchInfo> &FullStackIdToAllocMatchInfo) { 78906c3fb27SDimitry Andric auto &Ctx = M.getContext(); 7905f757f3fSDimitry Andric // Previously we used getIRPGOFuncName() here. If F is local linkage, 7915f757f3fSDimitry Andric // getIRPGOFuncName() returns FuncName with prefix 'FileName;'. But 7925f757f3fSDimitry Andric // llvm-profdata uses FuncName in dwarf to create GUID which doesn't 7935f757f3fSDimitry Andric // contain FileName's prefix. It caused local linkage function can't 7945f757f3fSDimitry Andric // find MemProfRecord. So we use getName() now. 7955f757f3fSDimitry Andric // 'unique-internal-linkage-names' can make MemProf work better for local 7965f757f3fSDimitry Andric // linkage function. 7975f757f3fSDimitry Andric auto FuncName = F.getName(); 79806c3fb27SDimitry Andric auto FuncGUID = Function::getGUID(FuncName); 7995f757f3fSDimitry Andric std::optional<memprof::MemProfRecord> MemProfRec; 8005f757f3fSDimitry Andric auto Err = MemProfReader->getMemProfRecord(FuncGUID).moveInto(MemProfRec); 8015f757f3fSDimitry Andric if (Err) { 8025f757f3fSDimitry Andric handleAllErrors(std::move(Err), [&](const InstrProfError &IPE) { 80306c3fb27SDimitry Andric auto Err = IPE.get(); 80406c3fb27SDimitry Andric bool SkipWarning = false; 80506c3fb27SDimitry Andric LLVM_DEBUG(dbgs() << "Error in reading profile for Func " << FuncName 80606c3fb27SDimitry Andric << ": "); 80706c3fb27SDimitry Andric if (Err == instrprof_error::unknown_function) { 80806c3fb27SDimitry Andric NumOfMemProfMissing++; 80906c3fb27SDimitry Andric SkipWarning = !PGOWarnMissing; 81006c3fb27SDimitry Andric LLVM_DEBUG(dbgs() << "unknown function"); 81106c3fb27SDimitry Andric } else if (Err == instrprof_error::hash_mismatch) { 812*0fca6ea1SDimitry Andric NumOfMemProfMismatch++; 81306c3fb27SDimitry Andric SkipWarning = 81406c3fb27SDimitry Andric NoPGOWarnMismatch || 81506c3fb27SDimitry Andric (NoPGOWarnMismatchComdatWeak && 81606c3fb27SDimitry Andric (F.hasComdat() || 81706c3fb27SDimitry Andric F.getLinkage() == GlobalValue::AvailableExternallyLinkage)); 81806c3fb27SDimitry Andric LLVM_DEBUG(dbgs() << "hash mismatch (skip=" << SkipWarning << ")"); 81906c3fb27SDimitry Andric } 82006c3fb27SDimitry Andric 82106c3fb27SDimitry Andric if (SkipWarning) 82206c3fb27SDimitry Andric return; 82306c3fb27SDimitry Andric 82406c3fb27SDimitry Andric std::string Msg = (IPE.message() + Twine(" ") + F.getName().str() + 82506c3fb27SDimitry Andric Twine(" Hash = ") + std::to_string(FuncGUID)) 82606c3fb27SDimitry Andric .str(); 82706c3fb27SDimitry Andric 82806c3fb27SDimitry Andric Ctx.diagnose( 82906c3fb27SDimitry Andric DiagnosticInfoPGOProfile(M.getName().data(), Msg, DS_Warning)); 83006c3fb27SDimitry Andric }); 83106c3fb27SDimitry Andric return; 83206c3fb27SDimitry Andric } 83306c3fb27SDimitry Andric 834*0fca6ea1SDimitry Andric NumOfMemProfFunc++; 835*0fca6ea1SDimitry Andric 8365f757f3fSDimitry Andric // Detect if there are non-zero column numbers in the profile. If not, 8375f757f3fSDimitry Andric // treat all column numbers as 0 when matching (i.e. ignore any non-zero 8385f757f3fSDimitry Andric // columns in the IR). The profiled binary might have been built with 8395f757f3fSDimitry Andric // column numbers disabled, for example. 8405f757f3fSDimitry Andric bool ProfileHasColumns = false; 8415f757f3fSDimitry Andric 84206c3fb27SDimitry Andric // Build maps of the location hash to all profile data with that leaf location 84306c3fb27SDimitry Andric // (allocation info and the callsites). 84406c3fb27SDimitry Andric std::map<uint64_t, std::set<const AllocationInfo *>> LocHashToAllocInfo; 84506c3fb27SDimitry Andric // For the callsites we need to record the index of the associated frame in 84606c3fb27SDimitry Andric // the frame array (see comments below where the map entries are added). 847*0fca6ea1SDimitry Andric std::map<uint64_t, std::set<std::pair<const std::vector<Frame> *, unsigned>>> 84806c3fb27SDimitry Andric LocHashToCallSites; 8495f757f3fSDimitry Andric for (auto &AI : MemProfRec->AllocSites) { 850*0fca6ea1SDimitry Andric NumOfMemProfAllocContextProfiles++; 85106c3fb27SDimitry Andric // Associate the allocation info with the leaf frame. The later matching 85206c3fb27SDimitry Andric // code will match any inlined call sequences in the IR with a longer prefix 85306c3fb27SDimitry Andric // of call stack frames. 85406c3fb27SDimitry Andric uint64_t StackId = computeStackId(AI.CallStack[0]); 85506c3fb27SDimitry Andric LocHashToAllocInfo[StackId].insert(&AI); 8565f757f3fSDimitry Andric ProfileHasColumns |= AI.CallStack[0].Column; 85706c3fb27SDimitry Andric } 8585f757f3fSDimitry Andric for (auto &CS : MemProfRec->CallSites) { 859*0fca6ea1SDimitry Andric NumOfMemProfCallSiteProfiles++; 86006c3fb27SDimitry Andric // Need to record all frames from leaf up to and including this function, 86106c3fb27SDimitry Andric // as any of these may or may not have been inlined at this point. 86206c3fb27SDimitry Andric unsigned Idx = 0; 86306c3fb27SDimitry Andric for (auto &StackFrame : CS) { 86406c3fb27SDimitry Andric uint64_t StackId = computeStackId(StackFrame); 86506c3fb27SDimitry Andric LocHashToCallSites[StackId].insert(std::make_pair(&CS, Idx++)); 8665f757f3fSDimitry Andric ProfileHasColumns |= StackFrame.Column; 86706c3fb27SDimitry Andric // Once we find this function, we can stop recording. 86806c3fb27SDimitry Andric if (StackFrame.Function == FuncGUID) 86906c3fb27SDimitry Andric break; 87006c3fb27SDimitry Andric } 87106c3fb27SDimitry Andric assert(Idx <= CS.size() && CS[Idx - 1].Function == FuncGUID); 87206c3fb27SDimitry Andric } 87306c3fb27SDimitry Andric 87406c3fb27SDimitry Andric auto GetOffset = [](const DILocation *DIL) { 87506c3fb27SDimitry Andric return (DIL->getLine() - DIL->getScope()->getSubprogram()->getLine()) & 87606c3fb27SDimitry Andric 0xffff; 87706c3fb27SDimitry Andric }; 87806c3fb27SDimitry Andric 87906c3fb27SDimitry Andric // Now walk the instructions, looking up the associated profile data using 880*0fca6ea1SDimitry Andric // debug locations. 88106c3fb27SDimitry Andric for (auto &BB : F) { 88206c3fb27SDimitry Andric for (auto &I : BB) { 88306c3fb27SDimitry Andric if (I.isDebugOrPseudoInst()) 88406c3fb27SDimitry Andric continue; 88506c3fb27SDimitry Andric // We are only interested in calls (allocation or interior call stack 88606c3fb27SDimitry Andric // context calls). 88706c3fb27SDimitry Andric auto *CI = dyn_cast<CallBase>(&I); 88806c3fb27SDimitry Andric if (!CI) 88906c3fb27SDimitry Andric continue; 89006c3fb27SDimitry Andric auto *CalledFunction = CI->getCalledFunction(); 89106c3fb27SDimitry Andric if (CalledFunction && CalledFunction->isIntrinsic()) 89206c3fb27SDimitry Andric continue; 89306c3fb27SDimitry Andric // List of call stack ids computed from the location hashes on debug 89406c3fb27SDimitry Andric // locations (leaf to inlined at root). 89506c3fb27SDimitry Andric std::vector<uint64_t> InlinedCallStack; 89606c3fb27SDimitry Andric // Was the leaf location found in one of the profile maps? 89706c3fb27SDimitry Andric bool LeafFound = false; 89806c3fb27SDimitry Andric // If leaf was found in a map, iterators pointing to its location in both 89906c3fb27SDimitry Andric // of the maps. It might exist in neither, one, or both (the latter case 90006c3fb27SDimitry Andric // can happen because we don't currently have discriminators to 90106c3fb27SDimitry Andric // distinguish the case when a single line/col maps to both an allocation 90206c3fb27SDimitry Andric // and another callsite). 90306c3fb27SDimitry Andric std::map<uint64_t, std::set<const AllocationInfo *>>::iterator 90406c3fb27SDimitry Andric AllocInfoIter; 905*0fca6ea1SDimitry Andric std::map<uint64_t, std::set<std::pair<const std::vector<Frame> *, 90606c3fb27SDimitry Andric unsigned>>>::iterator CallSitesIter; 90706c3fb27SDimitry Andric for (const DILocation *DIL = I.getDebugLoc(); DIL != nullptr; 90806c3fb27SDimitry Andric DIL = DIL->getInlinedAt()) { 90906c3fb27SDimitry Andric // Use C++ linkage name if possible. Need to compile with 91006c3fb27SDimitry Andric // -fdebug-info-for-profiling to get linkage name. 91106c3fb27SDimitry Andric StringRef Name = DIL->getScope()->getSubprogram()->getLinkageName(); 91206c3fb27SDimitry Andric if (Name.empty()) 91306c3fb27SDimitry Andric Name = DIL->getScope()->getSubprogram()->getName(); 91406c3fb27SDimitry Andric auto CalleeGUID = Function::getGUID(Name); 9155f757f3fSDimitry Andric auto StackId = computeStackId(CalleeGUID, GetOffset(DIL), 9165f757f3fSDimitry Andric ProfileHasColumns ? DIL->getColumn() : 0); 9175f757f3fSDimitry Andric // Check if we have found the profile's leaf frame. If yes, collect 9185f757f3fSDimitry Andric // the rest of the call's inlined context starting here. If not, see if 9195f757f3fSDimitry Andric // we find a match further up the inlined context (in case the profile 9205f757f3fSDimitry Andric // was missing debug frames at the leaf). 92106c3fb27SDimitry Andric if (!LeafFound) { 92206c3fb27SDimitry Andric AllocInfoIter = LocHashToAllocInfo.find(StackId); 92306c3fb27SDimitry Andric CallSitesIter = LocHashToCallSites.find(StackId); 9245f757f3fSDimitry Andric if (AllocInfoIter != LocHashToAllocInfo.end() || 9255f757f3fSDimitry Andric CallSitesIter != LocHashToCallSites.end()) 92606c3fb27SDimitry Andric LeafFound = true; 92706c3fb27SDimitry Andric } 9285f757f3fSDimitry Andric if (LeafFound) 92906c3fb27SDimitry Andric InlinedCallStack.push_back(StackId); 93006c3fb27SDimitry Andric } 93106c3fb27SDimitry Andric // If leaf not in either of the maps, skip inst. 93206c3fb27SDimitry Andric if (!LeafFound) 93306c3fb27SDimitry Andric continue; 93406c3fb27SDimitry Andric 93506c3fb27SDimitry Andric // First add !memprof metadata from allocation info, if we found the 93606c3fb27SDimitry Andric // instruction's leaf location in that map, and if the rest of the 93706c3fb27SDimitry Andric // instruction's locations match the prefix Frame locations on an 93806c3fb27SDimitry Andric // allocation context with the same leaf. 93906c3fb27SDimitry Andric if (AllocInfoIter != LocHashToAllocInfo.end()) { 94006c3fb27SDimitry Andric // Only consider allocations via new, to reduce unnecessary metadata, 94106c3fb27SDimitry Andric // since those are the only allocations that will be targeted initially. 942*0fca6ea1SDimitry Andric if (!isNewWithHotColdVariant(CI->getCalledFunction(), TLI)) 94306c3fb27SDimitry Andric continue; 94406c3fb27SDimitry Andric // We may match this instruction's location list to multiple MIB 94506c3fb27SDimitry Andric // contexts. Add them to a Trie specialized for trimming the contexts to 94606c3fb27SDimitry Andric // the minimal needed to disambiguate contexts with unique behavior. 94706c3fb27SDimitry Andric CallStackTrie AllocTrie; 94806c3fb27SDimitry Andric for (auto *AllocInfo : AllocInfoIter->second) { 94906c3fb27SDimitry Andric // Check the full inlined call stack against this one. 95006c3fb27SDimitry Andric // If we found and thus matched all frames on the call, include 95106c3fb27SDimitry Andric // this MIB. 95206c3fb27SDimitry Andric if (stackFrameIncludesInlinedCallStack(AllocInfo->CallStack, 953*0fca6ea1SDimitry Andric InlinedCallStack)) { 954*0fca6ea1SDimitry Andric NumOfMemProfMatchedAllocContexts++; 955*0fca6ea1SDimitry Andric auto AllocType = addCallStack(AllocTrie, AllocInfo); 956*0fca6ea1SDimitry Andric // Record information about the allocation if match info printing 957*0fca6ea1SDimitry Andric // was requested. 958*0fca6ea1SDimitry Andric if (ClPrintMemProfMatchInfo) { 959*0fca6ea1SDimitry Andric auto FullStackId = computeFullStackId(AllocInfo->CallStack); 960*0fca6ea1SDimitry Andric FullStackIdToAllocMatchInfo[FullStackId] = { 961*0fca6ea1SDimitry Andric AllocInfo->Info.getTotalSize(), AllocType, /*Matched=*/true}; 962*0fca6ea1SDimitry Andric } 963*0fca6ea1SDimitry Andric } 96406c3fb27SDimitry Andric } 96506c3fb27SDimitry Andric // We might not have matched any to the full inlined call stack. 96606c3fb27SDimitry Andric // But if we did, create and attach metadata, or a function attribute if 96706c3fb27SDimitry Andric // all contexts have identical profiled behavior. 96806c3fb27SDimitry Andric if (!AllocTrie.empty()) { 969*0fca6ea1SDimitry Andric NumOfMemProfMatchedAllocs++; 97006c3fb27SDimitry Andric // MemprofMDAttached will be false if a function attribute was 97106c3fb27SDimitry Andric // attached. 97206c3fb27SDimitry Andric bool MemprofMDAttached = AllocTrie.buildAndAttachMIBMetadata(CI); 97306c3fb27SDimitry Andric assert(MemprofMDAttached == I.hasMetadata(LLVMContext::MD_memprof)); 97406c3fb27SDimitry Andric if (MemprofMDAttached) { 97506c3fb27SDimitry Andric // Add callsite metadata for the instruction's location list so that 97606c3fb27SDimitry Andric // it simpler later on to identify which part of the MIB contexts 97706c3fb27SDimitry Andric // are from this particular instruction (including during inlining, 978*0fca6ea1SDimitry Andric // when the callsite metadata will be updated appropriately). 97906c3fb27SDimitry Andric // FIXME: can this be changed to strip out the matching stack 98006c3fb27SDimitry Andric // context ids from the MIB contexts and not add any callsite 98106c3fb27SDimitry Andric // metadata here to save space? 98206c3fb27SDimitry Andric addCallsiteMetadata(I, InlinedCallStack, Ctx); 98306c3fb27SDimitry Andric } 98406c3fb27SDimitry Andric } 98506c3fb27SDimitry Andric continue; 98606c3fb27SDimitry Andric } 98706c3fb27SDimitry Andric 98806c3fb27SDimitry Andric // Otherwise, add callsite metadata. If we reach here then we found the 98906c3fb27SDimitry Andric // instruction's leaf location in the callsites map and not the allocation 99006c3fb27SDimitry Andric // map. 99106c3fb27SDimitry Andric assert(CallSitesIter != LocHashToCallSites.end()); 99206c3fb27SDimitry Andric for (auto CallStackIdx : CallSitesIter->second) { 99306c3fb27SDimitry Andric // If we found and thus matched all frames on the call, create and 99406c3fb27SDimitry Andric // attach call stack metadata. 99506c3fb27SDimitry Andric if (stackFrameIncludesInlinedCallStack( 99606c3fb27SDimitry Andric *CallStackIdx.first, InlinedCallStack, CallStackIdx.second)) { 997*0fca6ea1SDimitry Andric NumOfMemProfMatchedCallSites++; 99806c3fb27SDimitry Andric addCallsiteMetadata(I, InlinedCallStack, Ctx); 99906c3fb27SDimitry Andric // Only need to find one with a matching call stack and add a single 100006c3fb27SDimitry Andric // callsite metadata. 100106c3fb27SDimitry Andric break; 100206c3fb27SDimitry Andric } 100306c3fb27SDimitry Andric } 100406c3fb27SDimitry Andric } 100506c3fb27SDimitry Andric } 100606c3fb27SDimitry Andric } 100706c3fb27SDimitry Andric 100806c3fb27SDimitry Andric MemProfUsePass::MemProfUsePass(std::string MemoryProfileFile, 100906c3fb27SDimitry Andric IntrusiveRefCntPtr<vfs::FileSystem> FS) 101006c3fb27SDimitry Andric : MemoryProfileFileName(MemoryProfileFile), FS(FS) { 101106c3fb27SDimitry Andric if (!FS) 101206c3fb27SDimitry Andric this->FS = vfs::getRealFileSystem(); 101306c3fb27SDimitry Andric } 101406c3fb27SDimitry Andric 101506c3fb27SDimitry Andric PreservedAnalyses MemProfUsePass::run(Module &M, ModuleAnalysisManager &AM) { 101606c3fb27SDimitry Andric LLVM_DEBUG(dbgs() << "Read in memory profile:"); 101706c3fb27SDimitry Andric auto &Ctx = M.getContext(); 101806c3fb27SDimitry Andric auto ReaderOrErr = IndexedInstrProfReader::create(MemoryProfileFileName, *FS); 101906c3fb27SDimitry Andric if (Error E = ReaderOrErr.takeError()) { 102006c3fb27SDimitry Andric handleAllErrors(std::move(E), [&](const ErrorInfoBase &EI) { 102106c3fb27SDimitry Andric Ctx.diagnose( 102206c3fb27SDimitry Andric DiagnosticInfoPGOProfile(MemoryProfileFileName.data(), EI.message())); 102306c3fb27SDimitry Andric }); 102406c3fb27SDimitry Andric return PreservedAnalyses::all(); 102506c3fb27SDimitry Andric } 102606c3fb27SDimitry Andric 102706c3fb27SDimitry Andric std::unique_ptr<IndexedInstrProfReader> MemProfReader = 102806c3fb27SDimitry Andric std::move(ReaderOrErr.get()); 102906c3fb27SDimitry Andric if (!MemProfReader) { 103006c3fb27SDimitry Andric Ctx.diagnose(DiagnosticInfoPGOProfile( 103106c3fb27SDimitry Andric MemoryProfileFileName.data(), StringRef("Cannot get MemProfReader"))); 103206c3fb27SDimitry Andric return PreservedAnalyses::all(); 103306c3fb27SDimitry Andric } 103406c3fb27SDimitry Andric 103506c3fb27SDimitry Andric if (!MemProfReader->hasMemoryProfile()) { 103606c3fb27SDimitry Andric Ctx.diagnose(DiagnosticInfoPGOProfile(MemoryProfileFileName.data(), 103706c3fb27SDimitry Andric "Not a memory profile")); 103806c3fb27SDimitry Andric return PreservedAnalyses::all(); 103906c3fb27SDimitry Andric } 104006c3fb27SDimitry Andric 104106c3fb27SDimitry Andric auto &FAM = AM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager(); 104206c3fb27SDimitry Andric 1043*0fca6ea1SDimitry Andric // Map from the stack has of each allocation context in the function profiles 1044*0fca6ea1SDimitry Andric // to the total profiled size (bytes), allocation type, and whether we matched 1045*0fca6ea1SDimitry Andric // it to an allocation in the IR. 1046*0fca6ea1SDimitry Andric std::map<uint64_t, AllocMatchInfo> FullStackIdToAllocMatchInfo; 1047*0fca6ea1SDimitry Andric 104806c3fb27SDimitry Andric for (auto &F : M) { 104906c3fb27SDimitry Andric if (F.isDeclaration()) 105006c3fb27SDimitry Andric continue; 105106c3fb27SDimitry Andric 105206c3fb27SDimitry Andric const TargetLibraryInfo &TLI = FAM.getResult<TargetLibraryAnalysis>(F); 1053*0fca6ea1SDimitry Andric readMemprof(M, F, MemProfReader.get(), TLI, FullStackIdToAllocMatchInfo); 1054*0fca6ea1SDimitry Andric } 1055*0fca6ea1SDimitry Andric 1056*0fca6ea1SDimitry Andric if (ClPrintMemProfMatchInfo) { 1057*0fca6ea1SDimitry Andric for (const auto &[Id, Info] : FullStackIdToAllocMatchInfo) 1058*0fca6ea1SDimitry Andric errs() << "MemProf " << getAllocTypeAttributeString(Info.AllocType) 1059*0fca6ea1SDimitry Andric << " context with id " << Id << " has total profiled size " 1060*0fca6ea1SDimitry Andric << Info.TotalSize << (Info.Matched ? " is" : " not") 1061*0fca6ea1SDimitry Andric << " matched\n"; 106206c3fb27SDimitry Andric } 106306c3fb27SDimitry Andric 106406c3fb27SDimitry Andric return PreservedAnalyses::none(); 106506c3fb27SDimitry Andric } 1066