xref: /freebsd-src/contrib/llvm-project/llvm/lib/Transforms/Instrumentation/MemProfiler.cpp (revision 06c3fb2749bda94cb5201f81ffdb8fa6c3161b2e)
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"
21*06c3fb27SDimitry Andric #include "llvm/Analysis/MemoryBuiltins.h"
22*06c3fb27SDimitry 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"
26*06c3fb27SDimitry 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"
36*06c3fb27SDimitry Andric #include "llvm/ProfileData/InstrProfReader.h"
37*06c3fb27SDimitry Andric #include "llvm/Support/BLAKE3.h"
38e8d8bef9SDimitry Andric #include "llvm/Support/CommandLine.h"
39e8d8bef9SDimitry Andric #include "llvm/Support/Debug.h"
40*06c3fb27SDimitry Andric #include "llvm/Support/HashBuilder.h"
41*06c3fb27SDimitry Andric #include "llvm/Support/VirtualFileSystem.h"
42*06c3fb27SDimitry Andric #include "llvm/TargetParser/Triple.h"
43e8d8bef9SDimitry Andric #include "llvm/Transforms/Utils/BasicBlockUtils.h"
44e8d8bef9SDimitry Andric #include "llvm/Transforms/Utils/ModuleUtils.h"
45*06c3fb27SDimitry Andric #include <map>
46*06c3fb27SDimitry Andric #include <set>
47e8d8bef9SDimitry Andric 
48e8d8bef9SDimitry Andric using namespace llvm;
49*06c3fb27SDimitry Andric using namespace llvm::memprof;
50e8d8bef9SDimitry Andric 
51e8d8bef9SDimitry Andric #define DEBUG_TYPE "memprof"
52e8d8bef9SDimitry Andric 
53*06c3fb27SDimitry Andric namespace llvm {
54*06c3fb27SDimitry Andric extern cl::opt<bool> PGOWarnMissing;
55*06c3fb27SDimitry Andric extern cl::opt<bool> NoPGOWarnMismatch;
56*06c3fb27SDimitry Andric extern cl::opt<bool> NoPGOWarnMismatchComdatWeak;
57*06c3fb27SDimitry Andric } // namespace llvm
58*06c3fb27SDimitry Andric 
59e8d8bef9SDimitry Andric constexpr int LLVM_MEM_PROFILER_VERSION = 1;
60e8d8bef9SDimitry Andric 
61e8d8bef9SDimitry Andric // Size of memory mapped to a single shadow location.
62e8d8bef9SDimitry Andric constexpr uint64_t DefaultShadowGranularity = 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 
80e8d8bef9SDimitry Andric // Command-line flags.
81e8d8bef9SDimitry Andric 
82e8d8bef9SDimitry Andric static cl::opt<bool> ClInsertVersionCheck(
83e8d8bef9SDimitry Andric     "memprof-guard-against-version-mismatch",
84e8d8bef9SDimitry Andric     cl::desc("Guard against compiler/runtime version mismatch."), cl::Hidden,
85e8d8bef9SDimitry Andric     cl::init(true));
86e8d8bef9SDimitry Andric 
87e8d8bef9SDimitry Andric // This flag may need to be replaced with -f[no-]memprof-reads.
88e8d8bef9SDimitry Andric static cl::opt<bool> ClInstrumentReads("memprof-instrument-reads",
89e8d8bef9SDimitry Andric                                        cl::desc("instrument read instructions"),
90e8d8bef9SDimitry Andric                                        cl::Hidden, cl::init(true));
91e8d8bef9SDimitry Andric 
92e8d8bef9SDimitry Andric static cl::opt<bool>
93e8d8bef9SDimitry Andric     ClInstrumentWrites("memprof-instrument-writes",
94e8d8bef9SDimitry Andric                        cl::desc("instrument write instructions"), cl::Hidden,
95e8d8bef9SDimitry Andric                        cl::init(true));
96e8d8bef9SDimitry Andric 
97e8d8bef9SDimitry Andric static cl::opt<bool> ClInstrumentAtomics(
98e8d8bef9SDimitry Andric     "memprof-instrument-atomics",
99e8d8bef9SDimitry Andric     cl::desc("instrument atomic instructions (rmw, cmpxchg)"), cl::Hidden,
100e8d8bef9SDimitry Andric     cl::init(true));
101e8d8bef9SDimitry Andric 
102e8d8bef9SDimitry Andric static cl::opt<bool> ClUseCalls(
103e8d8bef9SDimitry Andric     "memprof-use-callbacks",
104e8d8bef9SDimitry Andric     cl::desc("Use callbacks instead of inline instrumentation sequences."),
105e8d8bef9SDimitry Andric     cl::Hidden, cl::init(false));
106e8d8bef9SDimitry Andric 
107e8d8bef9SDimitry Andric static cl::opt<std::string>
108e8d8bef9SDimitry Andric     ClMemoryAccessCallbackPrefix("memprof-memory-access-callback-prefix",
109e8d8bef9SDimitry Andric                                  cl::desc("Prefix for memory access callbacks"),
110e8d8bef9SDimitry Andric                                  cl::Hidden, cl::init("__memprof_"));
111e8d8bef9SDimitry Andric 
112e8d8bef9SDimitry Andric // These flags allow to change the shadow mapping.
113e8d8bef9SDimitry Andric // The shadow mapping looks like
114e8d8bef9SDimitry Andric //    Shadow = ((Mem & mask) >> scale) + offset
115e8d8bef9SDimitry Andric 
116e8d8bef9SDimitry Andric static cl::opt<int> ClMappingScale("memprof-mapping-scale",
117e8d8bef9SDimitry Andric                                    cl::desc("scale of memprof shadow mapping"),
118e8d8bef9SDimitry Andric                                    cl::Hidden, cl::init(DefaultShadowScale));
119e8d8bef9SDimitry Andric 
120e8d8bef9SDimitry Andric static cl::opt<int>
121e8d8bef9SDimitry Andric     ClMappingGranularity("memprof-mapping-granularity",
122e8d8bef9SDimitry Andric                          cl::desc("granularity of memprof shadow mapping"),
123e8d8bef9SDimitry Andric                          cl::Hidden, cl::init(DefaultShadowGranularity));
124e8d8bef9SDimitry Andric 
125349cc55cSDimitry Andric static cl::opt<bool> ClStack("memprof-instrument-stack",
126349cc55cSDimitry Andric                              cl::desc("Instrument scalar stack variables"),
127349cc55cSDimitry Andric                              cl::Hidden, cl::init(false));
128349cc55cSDimitry Andric 
129e8d8bef9SDimitry Andric // Debug flags.
130e8d8bef9SDimitry Andric 
131e8d8bef9SDimitry Andric static cl::opt<int> ClDebug("memprof-debug", cl::desc("debug"), cl::Hidden,
132e8d8bef9SDimitry Andric                             cl::init(0));
133e8d8bef9SDimitry Andric 
134e8d8bef9SDimitry Andric static cl::opt<std::string> ClDebugFunc("memprof-debug-func", cl::Hidden,
135e8d8bef9SDimitry Andric                                         cl::desc("Debug func"));
136e8d8bef9SDimitry Andric 
137e8d8bef9SDimitry Andric static cl::opt<int> ClDebugMin("memprof-debug-min", cl::desc("Debug min inst"),
138e8d8bef9SDimitry Andric                                cl::Hidden, cl::init(-1));
139e8d8bef9SDimitry Andric 
140e8d8bef9SDimitry Andric static cl::opt<int> ClDebugMax("memprof-debug-max", cl::desc("Debug max inst"),
141e8d8bef9SDimitry Andric                                cl::Hidden, cl::init(-1));
142e8d8bef9SDimitry Andric 
143e8d8bef9SDimitry Andric STATISTIC(NumInstrumentedReads, "Number of instrumented reads");
144e8d8bef9SDimitry Andric STATISTIC(NumInstrumentedWrites, "Number of instrumented writes");
145349cc55cSDimitry Andric STATISTIC(NumSkippedStackReads, "Number of non-instrumented stack reads");
146349cc55cSDimitry Andric STATISTIC(NumSkippedStackWrites, "Number of non-instrumented stack writes");
147*06c3fb27SDimitry Andric STATISTIC(NumOfMemProfMissing, "Number of functions without memory profile.");
148e8d8bef9SDimitry Andric 
149e8d8bef9SDimitry Andric namespace {
150e8d8bef9SDimitry Andric 
151e8d8bef9SDimitry Andric /// This struct defines the shadow mapping using the rule:
152e8d8bef9SDimitry Andric ///   shadow = ((mem & mask) >> Scale) ADD DynamicShadowOffset.
153e8d8bef9SDimitry Andric struct ShadowMapping {
154e8d8bef9SDimitry Andric   ShadowMapping() {
155e8d8bef9SDimitry Andric     Scale = ClMappingScale;
156e8d8bef9SDimitry Andric     Granularity = ClMappingGranularity;
157e8d8bef9SDimitry Andric     Mask = ~(Granularity - 1);
158e8d8bef9SDimitry Andric   }
159e8d8bef9SDimitry Andric 
160e8d8bef9SDimitry Andric   int Scale;
161e8d8bef9SDimitry Andric   int Granularity;
162e8d8bef9SDimitry Andric   uint64_t Mask; // Computed as ~(Granularity-1)
163e8d8bef9SDimitry Andric };
164e8d8bef9SDimitry Andric 
165e8d8bef9SDimitry Andric static uint64_t getCtorAndDtorPriority(Triple &TargetTriple) {
166e8d8bef9SDimitry Andric   return TargetTriple.isOSEmscripten() ? MemProfEmscriptenCtorAndDtorPriority
167e8d8bef9SDimitry Andric                                        : MemProfCtorAndDtorPriority;
168e8d8bef9SDimitry Andric }
169e8d8bef9SDimitry Andric 
170e8d8bef9SDimitry Andric struct InterestingMemoryAccess {
171e8d8bef9SDimitry Andric   Value *Addr = nullptr;
172e8d8bef9SDimitry Andric   bool IsWrite;
17304eeddc0SDimitry Andric   Type *AccessTy;
174e8d8bef9SDimitry Andric   uint64_t TypeSize;
175e8d8bef9SDimitry Andric   Value *MaybeMask = nullptr;
176e8d8bef9SDimitry Andric };
177e8d8bef9SDimitry Andric 
178e8d8bef9SDimitry Andric /// Instrument the code in module to profile memory accesses.
179e8d8bef9SDimitry Andric class MemProfiler {
180e8d8bef9SDimitry Andric public:
181e8d8bef9SDimitry Andric   MemProfiler(Module &M) {
182e8d8bef9SDimitry Andric     C = &(M.getContext());
183e8d8bef9SDimitry Andric     LongSize = M.getDataLayout().getPointerSizeInBits();
184e8d8bef9SDimitry Andric     IntptrTy = Type::getIntNTy(*C, LongSize);
185e8d8bef9SDimitry Andric   }
186e8d8bef9SDimitry Andric 
187e8d8bef9SDimitry Andric   /// If it is an interesting memory access, populate information
188e8d8bef9SDimitry Andric   /// about the access and return a InterestingMemoryAccess struct.
189bdd1243dSDimitry Andric   /// Otherwise return std::nullopt.
190bdd1243dSDimitry Andric   std::optional<InterestingMemoryAccess>
191e8d8bef9SDimitry Andric   isInterestingMemoryAccess(Instruction *I) const;
192e8d8bef9SDimitry Andric 
193e8d8bef9SDimitry Andric   void instrumentMop(Instruction *I, const DataLayout &DL,
194e8d8bef9SDimitry Andric                      InterestingMemoryAccess &Access);
195e8d8bef9SDimitry Andric   void instrumentAddress(Instruction *OrigIns, Instruction *InsertBefore,
196e8d8bef9SDimitry Andric                          Value *Addr, uint32_t TypeSize, bool IsWrite);
197e8d8bef9SDimitry Andric   void instrumentMaskedLoadOrStore(const DataLayout &DL, Value *Mask,
19881ad6265SDimitry Andric                                    Instruction *I, Value *Addr, Type *AccessTy,
199e8d8bef9SDimitry Andric                                    bool IsWrite);
200e8d8bef9SDimitry Andric   void instrumentMemIntrinsic(MemIntrinsic *MI);
201e8d8bef9SDimitry Andric   Value *memToShadow(Value *Shadow, IRBuilder<> &IRB);
202e8d8bef9SDimitry Andric   bool instrumentFunction(Function &F);
203e8d8bef9SDimitry Andric   bool maybeInsertMemProfInitAtFunctionEntry(Function &F);
204e8d8bef9SDimitry Andric   bool insertDynamicShadowAtFunctionEntry(Function &F);
205e8d8bef9SDimitry Andric 
206e8d8bef9SDimitry Andric private:
207e8d8bef9SDimitry Andric   void initializeCallbacks(Module &M);
208e8d8bef9SDimitry Andric 
209e8d8bef9SDimitry Andric   LLVMContext *C;
210e8d8bef9SDimitry Andric   int LongSize;
211e8d8bef9SDimitry Andric   Type *IntptrTy;
212e8d8bef9SDimitry Andric   ShadowMapping Mapping;
213e8d8bef9SDimitry Andric 
214e8d8bef9SDimitry Andric   // These arrays is indexed by AccessIsWrite
215e8d8bef9SDimitry Andric   FunctionCallee MemProfMemoryAccessCallback[2];
216e8d8bef9SDimitry Andric   FunctionCallee MemProfMemoryAccessCallbackSized[2];
217e8d8bef9SDimitry Andric 
218e8d8bef9SDimitry Andric   FunctionCallee MemProfMemmove, MemProfMemcpy, MemProfMemset;
219e8d8bef9SDimitry Andric   Value *DynamicShadowOffset = nullptr;
220e8d8bef9SDimitry Andric };
221e8d8bef9SDimitry Andric 
222e8d8bef9SDimitry Andric class ModuleMemProfiler {
223e8d8bef9SDimitry Andric public:
224e8d8bef9SDimitry Andric   ModuleMemProfiler(Module &M) { TargetTriple = Triple(M.getTargetTriple()); }
225e8d8bef9SDimitry Andric 
226e8d8bef9SDimitry Andric   bool instrumentModule(Module &);
227e8d8bef9SDimitry Andric 
228e8d8bef9SDimitry Andric private:
229e8d8bef9SDimitry Andric   Triple TargetTriple;
230e8d8bef9SDimitry Andric   ShadowMapping Mapping;
231e8d8bef9SDimitry Andric   Function *MemProfCtorFunction = nullptr;
232e8d8bef9SDimitry Andric };
233e8d8bef9SDimitry Andric 
234e8d8bef9SDimitry Andric } // end anonymous namespace
235e8d8bef9SDimitry Andric 
23681ad6265SDimitry Andric MemProfilerPass::MemProfilerPass() = default;
237e8d8bef9SDimitry Andric 
238e8d8bef9SDimitry Andric PreservedAnalyses MemProfilerPass::run(Function &F,
239e8d8bef9SDimitry Andric                                        AnalysisManager<Function> &AM) {
240e8d8bef9SDimitry Andric   Module &M = *F.getParent();
241e8d8bef9SDimitry Andric   MemProfiler Profiler(M);
242e8d8bef9SDimitry Andric   if (Profiler.instrumentFunction(F))
243e8d8bef9SDimitry Andric     return PreservedAnalyses::none();
244e8d8bef9SDimitry Andric   return PreservedAnalyses::all();
245e8d8bef9SDimitry Andric }
246e8d8bef9SDimitry Andric 
24781ad6265SDimitry Andric ModuleMemProfilerPass::ModuleMemProfilerPass() = default;
248e8d8bef9SDimitry Andric 
249e8d8bef9SDimitry Andric PreservedAnalyses ModuleMemProfilerPass::run(Module &M,
250e8d8bef9SDimitry Andric                                              AnalysisManager<Module> &AM) {
251e8d8bef9SDimitry Andric   ModuleMemProfiler Profiler(M);
252e8d8bef9SDimitry Andric   if (Profiler.instrumentModule(M))
253e8d8bef9SDimitry Andric     return PreservedAnalyses::none();
254e8d8bef9SDimitry Andric   return PreservedAnalyses::all();
255e8d8bef9SDimitry Andric }
256e8d8bef9SDimitry Andric 
257e8d8bef9SDimitry Andric Value *MemProfiler::memToShadow(Value *Shadow, IRBuilder<> &IRB) {
258e8d8bef9SDimitry Andric   // (Shadow & mask) >> scale
259e8d8bef9SDimitry Andric   Shadow = IRB.CreateAnd(Shadow, Mapping.Mask);
260e8d8bef9SDimitry Andric   Shadow = IRB.CreateLShr(Shadow, Mapping.Scale);
261e8d8bef9SDimitry Andric   // (Shadow >> scale) | offset
262e8d8bef9SDimitry Andric   assert(DynamicShadowOffset);
263e8d8bef9SDimitry Andric   return IRB.CreateAdd(Shadow, DynamicShadowOffset);
264e8d8bef9SDimitry Andric }
265e8d8bef9SDimitry Andric 
266e8d8bef9SDimitry Andric // Instrument memset/memmove/memcpy
267e8d8bef9SDimitry Andric void MemProfiler::instrumentMemIntrinsic(MemIntrinsic *MI) {
268e8d8bef9SDimitry Andric   IRBuilder<> IRB(MI);
269e8d8bef9SDimitry Andric   if (isa<MemTransferInst>(MI)) {
270e8d8bef9SDimitry Andric     IRB.CreateCall(
271e8d8bef9SDimitry Andric         isa<MemMoveInst>(MI) ? MemProfMemmove : MemProfMemcpy,
272e8d8bef9SDimitry Andric         {IRB.CreatePointerCast(MI->getOperand(0), IRB.getInt8PtrTy()),
273e8d8bef9SDimitry Andric          IRB.CreatePointerCast(MI->getOperand(1), IRB.getInt8PtrTy()),
274e8d8bef9SDimitry Andric          IRB.CreateIntCast(MI->getOperand(2), IntptrTy, false)});
275e8d8bef9SDimitry Andric   } else if (isa<MemSetInst>(MI)) {
276e8d8bef9SDimitry Andric     IRB.CreateCall(
277e8d8bef9SDimitry Andric         MemProfMemset,
278e8d8bef9SDimitry Andric         {IRB.CreatePointerCast(MI->getOperand(0), IRB.getInt8PtrTy()),
279e8d8bef9SDimitry Andric          IRB.CreateIntCast(MI->getOperand(1), IRB.getInt32Ty(), false),
280e8d8bef9SDimitry Andric          IRB.CreateIntCast(MI->getOperand(2), IntptrTy, false)});
281e8d8bef9SDimitry Andric   }
282e8d8bef9SDimitry Andric   MI->eraseFromParent();
283e8d8bef9SDimitry Andric }
284e8d8bef9SDimitry Andric 
285bdd1243dSDimitry Andric std::optional<InterestingMemoryAccess>
286e8d8bef9SDimitry Andric MemProfiler::isInterestingMemoryAccess(Instruction *I) const {
287e8d8bef9SDimitry Andric   // Do not instrument the load fetching the dynamic shadow address.
288e8d8bef9SDimitry Andric   if (DynamicShadowOffset == I)
289bdd1243dSDimitry Andric     return std::nullopt;
290e8d8bef9SDimitry Andric 
291e8d8bef9SDimitry Andric   InterestingMemoryAccess Access;
292e8d8bef9SDimitry Andric 
293e8d8bef9SDimitry Andric   if (LoadInst *LI = dyn_cast<LoadInst>(I)) {
294e8d8bef9SDimitry Andric     if (!ClInstrumentReads)
295bdd1243dSDimitry Andric       return std::nullopt;
296e8d8bef9SDimitry Andric     Access.IsWrite = false;
29704eeddc0SDimitry Andric     Access.AccessTy = LI->getType();
298e8d8bef9SDimitry Andric     Access.Addr = LI->getPointerOperand();
299e8d8bef9SDimitry Andric   } else if (StoreInst *SI = dyn_cast<StoreInst>(I)) {
300e8d8bef9SDimitry Andric     if (!ClInstrumentWrites)
301bdd1243dSDimitry Andric       return std::nullopt;
302e8d8bef9SDimitry Andric     Access.IsWrite = true;
30304eeddc0SDimitry Andric     Access.AccessTy = SI->getValueOperand()->getType();
304e8d8bef9SDimitry Andric     Access.Addr = SI->getPointerOperand();
305e8d8bef9SDimitry Andric   } else if (AtomicRMWInst *RMW = dyn_cast<AtomicRMWInst>(I)) {
306e8d8bef9SDimitry Andric     if (!ClInstrumentAtomics)
307bdd1243dSDimitry Andric       return std::nullopt;
308e8d8bef9SDimitry Andric     Access.IsWrite = true;
30904eeddc0SDimitry Andric     Access.AccessTy = RMW->getValOperand()->getType();
310e8d8bef9SDimitry Andric     Access.Addr = RMW->getPointerOperand();
311e8d8bef9SDimitry Andric   } else if (AtomicCmpXchgInst *XCHG = dyn_cast<AtomicCmpXchgInst>(I)) {
312e8d8bef9SDimitry Andric     if (!ClInstrumentAtomics)
313bdd1243dSDimitry Andric       return std::nullopt;
314e8d8bef9SDimitry Andric     Access.IsWrite = true;
31504eeddc0SDimitry Andric     Access.AccessTy = XCHG->getCompareOperand()->getType();
316e8d8bef9SDimitry Andric     Access.Addr = XCHG->getPointerOperand();
317e8d8bef9SDimitry Andric   } else if (auto *CI = dyn_cast<CallInst>(I)) {
318e8d8bef9SDimitry Andric     auto *F = CI->getCalledFunction();
319e8d8bef9SDimitry Andric     if (F && (F->getIntrinsicID() == Intrinsic::masked_load ||
320e8d8bef9SDimitry Andric               F->getIntrinsicID() == Intrinsic::masked_store)) {
321e8d8bef9SDimitry Andric       unsigned OpOffset = 0;
322e8d8bef9SDimitry Andric       if (F->getIntrinsicID() == Intrinsic::masked_store) {
323e8d8bef9SDimitry Andric         if (!ClInstrumentWrites)
324bdd1243dSDimitry Andric           return std::nullopt;
325e8d8bef9SDimitry Andric         // Masked store has an initial operand for the value.
326e8d8bef9SDimitry Andric         OpOffset = 1;
32704eeddc0SDimitry Andric         Access.AccessTy = CI->getArgOperand(0)->getType();
328e8d8bef9SDimitry Andric         Access.IsWrite = true;
329e8d8bef9SDimitry Andric       } else {
330e8d8bef9SDimitry Andric         if (!ClInstrumentReads)
331bdd1243dSDimitry Andric           return std::nullopt;
33204eeddc0SDimitry Andric         Access.AccessTy = CI->getType();
333e8d8bef9SDimitry Andric         Access.IsWrite = false;
334e8d8bef9SDimitry Andric       }
335e8d8bef9SDimitry Andric 
336e8d8bef9SDimitry Andric       auto *BasePtr = CI->getOperand(0 + OpOffset);
337e8d8bef9SDimitry Andric       Access.MaybeMask = CI->getOperand(2 + OpOffset);
338e8d8bef9SDimitry Andric       Access.Addr = BasePtr;
339e8d8bef9SDimitry Andric     }
340e8d8bef9SDimitry Andric   }
341e8d8bef9SDimitry Andric 
342e8d8bef9SDimitry Andric   if (!Access.Addr)
343bdd1243dSDimitry Andric     return std::nullopt;
344e8d8bef9SDimitry Andric 
345bdd1243dSDimitry Andric   // Do not instrument accesses from different address spaces; we cannot deal
346e8d8bef9SDimitry Andric   // with them.
347e8d8bef9SDimitry Andric   Type *PtrTy = cast<PointerType>(Access.Addr->getType()->getScalarType());
348e8d8bef9SDimitry Andric   if (PtrTy->getPointerAddressSpace() != 0)
349bdd1243dSDimitry Andric     return std::nullopt;
350e8d8bef9SDimitry Andric 
351e8d8bef9SDimitry Andric   // Ignore swifterror addresses.
352e8d8bef9SDimitry Andric   // swifterror memory addresses are mem2reg promoted by instruction
353e8d8bef9SDimitry Andric   // selection. As such they cannot have regular uses like an instrumentation
354e8d8bef9SDimitry Andric   // function and it makes no sense to track them as memory.
355e8d8bef9SDimitry Andric   if (Access.Addr->isSwiftError())
356bdd1243dSDimitry Andric     return std::nullopt;
357e8d8bef9SDimitry Andric 
35881ad6265SDimitry Andric   // Peel off GEPs and BitCasts.
35981ad6265SDimitry Andric   auto *Addr = Access.Addr->stripInBoundsOffsets();
36081ad6265SDimitry Andric 
36181ad6265SDimitry Andric   if (GlobalVariable *GV = dyn_cast<GlobalVariable>(Addr)) {
36281ad6265SDimitry Andric     // Do not instrument PGO counter updates.
36381ad6265SDimitry Andric     if (GV->hasSection()) {
36481ad6265SDimitry Andric       StringRef SectionName = GV->getSection();
36581ad6265SDimitry Andric       // Check if the global is in the PGO counters section.
36681ad6265SDimitry Andric       auto OF = Triple(I->getModule()->getTargetTriple()).getObjectFormat();
36781ad6265SDimitry Andric       if (SectionName.endswith(
36881ad6265SDimitry Andric               getInstrProfSectionName(IPSK_cnts, OF, /*AddSegmentInfo=*/false)))
369bdd1243dSDimitry Andric         return std::nullopt;
37081ad6265SDimitry Andric     }
37181ad6265SDimitry Andric 
37281ad6265SDimitry Andric     // Do not instrument accesses to LLVM internal variables.
37381ad6265SDimitry Andric     if (GV->getName().startswith("__llvm"))
374bdd1243dSDimitry Andric       return std::nullopt;
37581ad6265SDimitry Andric   }
37681ad6265SDimitry Andric 
37704eeddc0SDimitry Andric   const DataLayout &DL = I->getModule()->getDataLayout();
37804eeddc0SDimitry Andric   Access.TypeSize = DL.getTypeStoreSizeInBits(Access.AccessTy);
379e8d8bef9SDimitry Andric   return Access;
380e8d8bef9SDimitry Andric }
381e8d8bef9SDimitry Andric 
382e8d8bef9SDimitry Andric void MemProfiler::instrumentMaskedLoadOrStore(const DataLayout &DL, Value *Mask,
383e8d8bef9SDimitry Andric                                               Instruction *I, Value *Addr,
38404eeddc0SDimitry Andric                                               Type *AccessTy, bool IsWrite) {
38504eeddc0SDimitry Andric   auto *VTy = cast<FixedVectorType>(AccessTy);
386e8d8bef9SDimitry Andric   uint64_t ElemTypeSize = DL.getTypeStoreSizeInBits(VTy->getScalarType());
387e8d8bef9SDimitry Andric   unsigned Num = VTy->getNumElements();
388e8d8bef9SDimitry Andric   auto *Zero = ConstantInt::get(IntptrTy, 0);
389e8d8bef9SDimitry Andric   for (unsigned Idx = 0; Idx < Num; ++Idx) {
390e8d8bef9SDimitry Andric     Value *InstrumentedAddress = nullptr;
391e8d8bef9SDimitry Andric     Instruction *InsertBefore = I;
392e8d8bef9SDimitry Andric     if (auto *Vector = dyn_cast<ConstantVector>(Mask)) {
393e8d8bef9SDimitry Andric       // dyn_cast as we might get UndefValue
394e8d8bef9SDimitry Andric       if (auto *Masked = dyn_cast<ConstantInt>(Vector->getOperand(Idx))) {
395e8d8bef9SDimitry Andric         if (Masked->isZero())
396e8d8bef9SDimitry Andric           // Mask is constant false, so no instrumentation needed.
397e8d8bef9SDimitry Andric           continue;
398e8d8bef9SDimitry Andric         // If we have a true or undef value, fall through to instrumentAddress.
399e8d8bef9SDimitry Andric         // with InsertBefore == I
400e8d8bef9SDimitry Andric       }
401e8d8bef9SDimitry Andric     } else {
402e8d8bef9SDimitry Andric       IRBuilder<> IRB(I);
403e8d8bef9SDimitry Andric       Value *MaskElem = IRB.CreateExtractElement(Mask, Idx);
404e8d8bef9SDimitry Andric       Instruction *ThenTerm = SplitBlockAndInsertIfThen(MaskElem, I, false);
405e8d8bef9SDimitry Andric       InsertBefore = ThenTerm;
406e8d8bef9SDimitry Andric     }
407e8d8bef9SDimitry Andric 
408e8d8bef9SDimitry Andric     IRBuilder<> IRB(InsertBefore);
409e8d8bef9SDimitry Andric     InstrumentedAddress =
410e8d8bef9SDimitry Andric         IRB.CreateGEP(VTy, Addr, {Zero, ConstantInt::get(IntptrTy, Idx)});
411e8d8bef9SDimitry Andric     instrumentAddress(I, InsertBefore, InstrumentedAddress, ElemTypeSize,
412e8d8bef9SDimitry Andric                       IsWrite);
413e8d8bef9SDimitry Andric   }
414e8d8bef9SDimitry Andric }
415e8d8bef9SDimitry Andric 
416e8d8bef9SDimitry Andric void MemProfiler::instrumentMop(Instruction *I, const DataLayout &DL,
417e8d8bef9SDimitry Andric                                 InterestingMemoryAccess &Access) {
418349cc55cSDimitry Andric   // Skip instrumentation of stack accesses unless requested.
419349cc55cSDimitry Andric   if (!ClStack && isa<AllocaInst>(getUnderlyingObject(Access.Addr))) {
420349cc55cSDimitry Andric     if (Access.IsWrite)
421349cc55cSDimitry Andric       ++NumSkippedStackWrites;
422349cc55cSDimitry Andric     else
423349cc55cSDimitry Andric       ++NumSkippedStackReads;
424349cc55cSDimitry Andric     return;
425349cc55cSDimitry Andric   }
426349cc55cSDimitry Andric 
427e8d8bef9SDimitry Andric   if (Access.IsWrite)
428e8d8bef9SDimitry Andric     NumInstrumentedWrites++;
429e8d8bef9SDimitry Andric   else
430e8d8bef9SDimitry Andric     NumInstrumentedReads++;
431e8d8bef9SDimitry Andric 
432e8d8bef9SDimitry Andric   if (Access.MaybeMask) {
433e8d8bef9SDimitry Andric     instrumentMaskedLoadOrStore(DL, Access.MaybeMask, I, Access.Addr,
43481ad6265SDimitry Andric                                 Access.AccessTy, Access.IsWrite);
435e8d8bef9SDimitry Andric   } else {
436e8d8bef9SDimitry Andric     // Since the access counts will be accumulated across the entire allocation,
437e8d8bef9SDimitry Andric     // we only update the shadow access count for the first location and thus
438e8d8bef9SDimitry Andric     // don't need to worry about alignment and type size.
439e8d8bef9SDimitry Andric     instrumentAddress(I, I, Access.Addr, Access.TypeSize, Access.IsWrite);
440e8d8bef9SDimitry Andric   }
441e8d8bef9SDimitry Andric }
442e8d8bef9SDimitry Andric 
443e8d8bef9SDimitry Andric void MemProfiler::instrumentAddress(Instruction *OrigIns,
444e8d8bef9SDimitry Andric                                     Instruction *InsertBefore, Value *Addr,
445e8d8bef9SDimitry Andric                                     uint32_t TypeSize, bool IsWrite) {
446e8d8bef9SDimitry Andric   IRBuilder<> IRB(InsertBefore);
447e8d8bef9SDimitry Andric   Value *AddrLong = IRB.CreatePointerCast(Addr, IntptrTy);
448e8d8bef9SDimitry Andric 
449e8d8bef9SDimitry Andric   if (ClUseCalls) {
450e8d8bef9SDimitry Andric     IRB.CreateCall(MemProfMemoryAccessCallback[IsWrite], AddrLong);
451e8d8bef9SDimitry Andric     return;
452e8d8bef9SDimitry Andric   }
453e8d8bef9SDimitry Andric 
454e8d8bef9SDimitry Andric   // Create an inline sequence to compute shadow location, and increment the
455e8d8bef9SDimitry Andric   // value by one.
456e8d8bef9SDimitry Andric   Type *ShadowTy = Type::getInt64Ty(*C);
457e8d8bef9SDimitry Andric   Type *ShadowPtrTy = PointerType::get(ShadowTy, 0);
458e8d8bef9SDimitry Andric   Value *ShadowPtr = memToShadow(AddrLong, IRB);
459e8d8bef9SDimitry Andric   Value *ShadowAddr = IRB.CreateIntToPtr(ShadowPtr, ShadowPtrTy);
460e8d8bef9SDimitry Andric   Value *ShadowValue = IRB.CreateLoad(ShadowTy, ShadowAddr);
461e8d8bef9SDimitry Andric   Value *Inc = ConstantInt::get(Type::getInt64Ty(*C), 1);
462e8d8bef9SDimitry Andric   ShadowValue = IRB.CreateAdd(ShadowValue, Inc);
463e8d8bef9SDimitry Andric   IRB.CreateStore(ShadowValue, ShadowAddr);
464e8d8bef9SDimitry Andric }
465e8d8bef9SDimitry Andric 
466e8d8bef9SDimitry Andric // Create the variable for the profile file name.
467e8d8bef9SDimitry Andric void createProfileFileNameVar(Module &M) {
468e8d8bef9SDimitry Andric   const MDString *MemProfFilename =
469e8d8bef9SDimitry Andric       dyn_cast_or_null<MDString>(M.getModuleFlag("MemProfProfileFilename"));
470e8d8bef9SDimitry Andric   if (!MemProfFilename)
471e8d8bef9SDimitry Andric     return;
472e8d8bef9SDimitry Andric   assert(!MemProfFilename->getString().empty() &&
473e8d8bef9SDimitry Andric          "Unexpected MemProfProfileFilename metadata with empty string");
474e8d8bef9SDimitry Andric   Constant *ProfileNameConst = ConstantDataArray::getString(
475e8d8bef9SDimitry Andric       M.getContext(), MemProfFilename->getString(), true);
476e8d8bef9SDimitry Andric   GlobalVariable *ProfileNameVar = new GlobalVariable(
477e8d8bef9SDimitry Andric       M, ProfileNameConst->getType(), /*isConstant=*/true,
478e8d8bef9SDimitry Andric       GlobalValue::WeakAnyLinkage, ProfileNameConst, MemProfFilenameVar);
479e8d8bef9SDimitry Andric   Triple TT(M.getTargetTriple());
480e8d8bef9SDimitry Andric   if (TT.supportsCOMDAT()) {
481e8d8bef9SDimitry Andric     ProfileNameVar->setLinkage(GlobalValue::ExternalLinkage);
482e8d8bef9SDimitry Andric     ProfileNameVar->setComdat(M.getOrInsertComdat(MemProfFilenameVar));
483e8d8bef9SDimitry Andric   }
484e8d8bef9SDimitry Andric }
485e8d8bef9SDimitry Andric 
486e8d8bef9SDimitry Andric bool ModuleMemProfiler::instrumentModule(Module &M) {
487e8d8bef9SDimitry Andric   // Create a module constructor.
488e8d8bef9SDimitry Andric   std::string MemProfVersion = std::to_string(LLVM_MEM_PROFILER_VERSION);
489e8d8bef9SDimitry Andric   std::string VersionCheckName =
490e8d8bef9SDimitry Andric       ClInsertVersionCheck ? (MemProfVersionCheckNamePrefix + MemProfVersion)
491e8d8bef9SDimitry Andric                            : "";
492e8d8bef9SDimitry Andric   std::tie(MemProfCtorFunction, std::ignore) =
493e8d8bef9SDimitry Andric       createSanitizerCtorAndInitFunctions(M, MemProfModuleCtorName,
494e8d8bef9SDimitry Andric                                           MemProfInitName, /*InitArgTypes=*/{},
495e8d8bef9SDimitry Andric                                           /*InitArgs=*/{}, VersionCheckName);
496e8d8bef9SDimitry Andric 
497e8d8bef9SDimitry Andric   const uint64_t Priority = getCtorAndDtorPriority(TargetTriple);
498e8d8bef9SDimitry Andric   appendToGlobalCtors(M, MemProfCtorFunction, Priority);
499e8d8bef9SDimitry Andric 
500e8d8bef9SDimitry Andric   createProfileFileNameVar(M);
501e8d8bef9SDimitry Andric 
502e8d8bef9SDimitry Andric   return true;
503e8d8bef9SDimitry Andric }
504e8d8bef9SDimitry Andric 
505e8d8bef9SDimitry Andric void MemProfiler::initializeCallbacks(Module &M) {
506e8d8bef9SDimitry Andric   IRBuilder<> IRB(*C);
507e8d8bef9SDimitry Andric 
508e8d8bef9SDimitry Andric   for (size_t AccessIsWrite = 0; AccessIsWrite <= 1; AccessIsWrite++) {
509e8d8bef9SDimitry Andric     const std::string TypeStr = AccessIsWrite ? "store" : "load";
510e8d8bef9SDimitry Andric 
511e8d8bef9SDimitry Andric     SmallVector<Type *, 3> Args2 = {IntptrTy, IntptrTy};
512e8d8bef9SDimitry Andric     SmallVector<Type *, 2> Args1{1, IntptrTy};
513e8d8bef9SDimitry Andric     MemProfMemoryAccessCallbackSized[AccessIsWrite] =
514e8d8bef9SDimitry Andric         M.getOrInsertFunction(ClMemoryAccessCallbackPrefix + TypeStr + "N",
515e8d8bef9SDimitry Andric                               FunctionType::get(IRB.getVoidTy(), Args2, false));
516e8d8bef9SDimitry Andric 
517e8d8bef9SDimitry Andric     MemProfMemoryAccessCallback[AccessIsWrite] =
518e8d8bef9SDimitry Andric         M.getOrInsertFunction(ClMemoryAccessCallbackPrefix + TypeStr,
519e8d8bef9SDimitry Andric                               FunctionType::get(IRB.getVoidTy(), Args1, false));
520e8d8bef9SDimitry Andric   }
521e8d8bef9SDimitry Andric   MemProfMemmove = M.getOrInsertFunction(
522e8d8bef9SDimitry Andric       ClMemoryAccessCallbackPrefix + "memmove", IRB.getInt8PtrTy(),
523e8d8bef9SDimitry Andric       IRB.getInt8PtrTy(), IRB.getInt8PtrTy(), IntptrTy);
524e8d8bef9SDimitry Andric   MemProfMemcpy = M.getOrInsertFunction(ClMemoryAccessCallbackPrefix + "memcpy",
525e8d8bef9SDimitry Andric                                         IRB.getInt8PtrTy(), IRB.getInt8PtrTy(),
526e8d8bef9SDimitry Andric                                         IRB.getInt8PtrTy(), IntptrTy);
527e8d8bef9SDimitry Andric   MemProfMemset = M.getOrInsertFunction(ClMemoryAccessCallbackPrefix + "memset",
528e8d8bef9SDimitry Andric                                         IRB.getInt8PtrTy(), IRB.getInt8PtrTy(),
529e8d8bef9SDimitry Andric                                         IRB.getInt32Ty(), IntptrTy);
530e8d8bef9SDimitry Andric }
531e8d8bef9SDimitry Andric 
532e8d8bef9SDimitry Andric bool MemProfiler::maybeInsertMemProfInitAtFunctionEntry(Function &F) {
533e8d8bef9SDimitry Andric   // For each NSObject descendant having a +load method, this method is invoked
534e8d8bef9SDimitry Andric   // by the ObjC runtime before any of the static constructors is called.
535e8d8bef9SDimitry Andric   // Therefore we need to instrument such methods with a call to __memprof_init
536e8d8bef9SDimitry Andric   // at the beginning in order to initialize our runtime before any access to
537e8d8bef9SDimitry Andric   // the shadow memory.
538e8d8bef9SDimitry Andric   // We cannot just ignore these methods, because they may call other
539e8d8bef9SDimitry Andric   // instrumented functions.
540e8d8bef9SDimitry Andric   if (F.getName().find(" load]") != std::string::npos) {
541e8d8bef9SDimitry Andric     FunctionCallee MemProfInitFunction =
542e8d8bef9SDimitry Andric         declareSanitizerInitFunction(*F.getParent(), MemProfInitName, {});
543e8d8bef9SDimitry Andric     IRBuilder<> IRB(&F.front(), F.front().begin());
544e8d8bef9SDimitry Andric     IRB.CreateCall(MemProfInitFunction, {});
545e8d8bef9SDimitry Andric     return true;
546e8d8bef9SDimitry Andric   }
547e8d8bef9SDimitry Andric   return false;
548e8d8bef9SDimitry Andric }
549e8d8bef9SDimitry Andric 
550e8d8bef9SDimitry Andric bool MemProfiler::insertDynamicShadowAtFunctionEntry(Function &F) {
551e8d8bef9SDimitry Andric   IRBuilder<> IRB(&F.front().front());
552e8d8bef9SDimitry Andric   Value *GlobalDynamicAddress = F.getParent()->getOrInsertGlobal(
553e8d8bef9SDimitry Andric       MemProfShadowMemoryDynamicAddress, IntptrTy);
554e8d8bef9SDimitry Andric   if (F.getParent()->getPICLevel() == PICLevel::NotPIC)
555e8d8bef9SDimitry Andric     cast<GlobalVariable>(GlobalDynamicAddress)->setDSOLocal(true);
556e8d8bef9SDimitry Andric   DynamicShadowOffset = IRB.CreateLoad(IntptrTy, GlobalDynamicAddress);
557e8d8bef9SDimitry Andric   return true;
558e8d8bef9SDimitry Andric }
559e8d8bef9SDimitry Andric 
560e8d8bef9SDimitry Andric bool MemProfiler::instrumentFunction(Function &F) {
561e8d8bef9SDimitry Andric   if (F.getLinkage() == GlobalValue::AvailableExternallyLinkage)
562e8d8bef9SDimitry Andric     return false;
563e8d8bef9SDimitry Andric   if (ClDebugFunc == F.getName())
564e8d8bef9SDimitry Andric     return false;
565e8d8bef9SDimitry Andric   if (F.getName().startswith("__memprof_"))
566e8d8bef9SDimitry Andric     return false;
567e8d8bef9SDimitry Andric 
568e8d8bef9SDimitry Andric   bool FunctionModified = false;
569e8d8bef9SDimitry Andric 
570e8d8bef9SDimitry Andric   // If needed, insert __memprof_init.
571e8d8bef9SDimitry Andric   // This function needs to be called even if the function body is not
572e8d8bef9SDimitry Andric   // instrumented.
573e8d8bef9SDimitry Andric   if (maybeInsertMemProfInitAtFunctionEntry(F))
574e8d8bef9SDimitry Andric     FunctionModified = true;
575e8d8bef9SDimitry Andric 
576e8d8bef9SDimitry Andric   LLVM_DEBUG(dbgs() << "MEMPROF instrumenting:\n" << F << "\n");
577e8d8bef9SDimitry Andric 
578e8d8bef9SDimitry Andric   initializeCallbacks(*F.getParent());
579e8d8bef9SDimitry Andric 
580e8d8bef9SDimitry Andric   SmallVector<Instruction *, 16> ToInstrument;
581e8d8bef9SDimitry Andric 
582e8d8bef9SDimitry Andric   // Fill the set of memory operations to instrument.
583e8d8bef9SDimitry Andric   for (auto &BB : F) {
584e8d8bef9SDimitry Andric     for (auto &Inst : BB) {
585e8d8bef9SDimitry Andric       if (isInterestingMemoryAccess(&Inst) || isa<MemIntrinsic>(Inst))
586e8d8bef9SDimitry Andric         ToInstrument.push_back(&Inst);
587e8d8bef9SDimitry Andric     }
588e8d8bef9SDimitry Andric   }
589e8d8bef9SDimitry Andric 
59081ad6265SDimitry Andric   if (ToInstrument.empty()) {
59181ad6265SDimitry Andric     LLVM_DEBUG(dbgs() << "MEMPROF done instrumenting: " << FunctionModified
59281ad6265SDimitry Andric                       << " " << F << "\n");
59381ad6265SDimitry Andric 
59481ad6265SDimitry Andric     return FunctionModified;
59581ad6265SDimitry Andric   }
59681ad6265SDimitry Andric 
59781ad6265SDimitry Andric   FunctionModified |= insertDynamicShadowAtFunctionEntry(F);
59881ad6265SDimitry Andric 
599e8d8bef9SDimitry Andric   int NumInstrumented = 0;
600e8d8bef9SDimitry Andric   for (auto *Inst : ToInstrument) {
601e8d8bef9SDimitry Andric     if (ClDebugMin < 0 || ClDebugMax < 0 ||
602e8d8bef9SDimitry Andric         (NumInstrumented >= ClDebugMin && NumInstrumented <= ClDebugMax)) {
603bdd1243dSDimitry Andric       std::optional<InterestingMemoryAccess> Access =
604e8d8bef9SDimitry Andric           isInterestingMemoryAccess(Inst);
605e8d8bef9SDimitry Andric       if (Access)
606e8d8bef9SDimitry Andric         instrumentMop(Inst, F.getParent()->getDataLayout(), *Access);
607e8d8bef9SDimitry Andric       else
608e8d8bef9SDimitry Andric         instrumentMemIntrinsic(cast<MemIntrinsic>(Inst));
609e8d8bef9SDimitry Andric     }
610e8d8bef9SDimitry Andric     NumInstrumented++;
611e8d8bef9SDimitry Andric   }
612e8d8bef9SDimitry Andric 
613e8d8bef9SDimitry Andric   if (NumInstrumented > 0)
614e8d8bef9SDimitry Andric     FunctionModified = true;
615e8d8bef9SDimitry Andric 
616e8d8bef9SDimitry Andric   LLVM_DEBUG(dbgs() << "MEMPROF done instrumenting: " << FunctionModified << " "
617e8d8bef9SDimitry Andric                     << F << "\n");
618e8d8bef9SDimitry Andric 
619e8d8bef9SDimitry Andric   return FunctionModified;
620e8d8bef9SDimitry Andric }
621*06c3fb27SDimitry Andric 
622*06c3fb27SDimitry Andric static void addCallsiteMetadata(Instruction &I,
623*06c3fb27SDimitry Andric                                 std::vector<uint64_t> &InlinedCallStack,
624*06c3fb27SDimitry Andric                                 LLVMContext &Ctx) {
625*06c3fb27SDimitry Andric   I.setMetadata(LLVMContext::MD_callsite,
626*06c3fb27SDimitry Andric                 buildCallstackMetadata(InlinedCallStack, Ctx));
627*06c3fb27SDimitry Andric }
628*06c3fb27SDimitry Andric 
629*06c3fb27SDimitry Andric static uint64_t computeStackId(GlobalValue::GUID Function, uint32_t LineOffset,
630*06c3fb27SDimitry Andric                                uint32_t Column) {
631*06c3fb27SDimitry Andric   llvm::HashBuilder<llvm::TruncatedBLAKE3<8>, llvm::support::endianness::little>
632*06c3fb27SDimitry Andric       HashBuilder;
633*06c3fb27SDimitry Andric   HashBuilder.add(Function, LineOffset, Column);
634*06c3fb27SDimitry Andric   llvm::BLAKE3Result<8> Hash = HashBuilder.final();
635*06c3fb27SDimitry Andric   uint64_t Id;
636*06c3fb27SDimitry Andric   std::memcpy(&Id, Hash.data(), sizeof(Hash));
637*06c3fb27SDimitry Andric   return Id;
638*06c3fb27SDimitry Andric }
639*06c3fb27SDimitry Andric 
640*06c3fb27SDimitry Andric static uint64_t computeStackId(const memprof::Frame &Frame) {
641*06c3fb27SDimitry Andric   return computeStackId(Frame.Function, Frame.LineOffset, Frame.Column);
642*06c3fb27SDimitry Andric }
643*06c3fb27SDimitry Andric 
644*06c3fb27SDimitry Andric static void addCallStack(CallStackTrie &AllocTrie,
645*06c3fb27SDimitry Andric                          const AllocationInfo *AllocInfo) {
646*06c3fb27SDimitry Andric   SmallVector<uint64_t> StackIds;
647*06c3fb27SDimitry Andric   for (const auto &StackFrame : AllocInfo->CallStack)
648*06c3fb27SDimitry Andric     StackIds.push_back(computeStackId(StackFrame));
649*06c3fb27SDimitry Andric   auto AllocType = getAllocType(AllocInfo->Info.getTotalLifetimeAccessDensity(),
650*06c3fb27SDimitry Andric                                 AllocInfo->Info.getAllocCount(),
651*06c3fb27SDimitry Andric                                 AllocInfo->Info.getTotalLifetime());
652*06c3fb27SDimitry Andric   AllocTrie.addCallStack(AllocType, StackIds);
653*06c3fb27SDimitry Andric }
654*06c3fb27SDimitry Andric 
655*06c3fb27SDimitry Andric // Helper to compare the InlinedCallStack computed from an instruction's debug
656*06c3fb27SDimitry Andric // info to a list of Frames from profile data (either the allocation data or a
657*06c3fb27SDimitry Andric // callsite). For callsites, the StartIndex to use in the Frame array may be
658*06c3fb27SDimitry Andric // non-zero.
659*06c3fb27SDimitry Andric static bool
660*06c3fb27SDimitry Andric stackFrameIncludesInlinedCallStack(ArrayRef<Frame> ProfileCallStack,
661*06c3fb27SDimitry Andric                                    ArrayRef<uint64_t> InlinedCallStack,
662*06c3fb27SDimitry Andric                                    unsigned StartIndex = 0) {
663*06c3fb27SDimitry Andric   auto StackFrame = ProfileCallStack.begin() + StartIndex;
664*06c3fb27SDimitry Andric   auto InlCallStackIter = InlinedCallStack.begin();
665*06c3fb27SDimitry Andric   for (; StackFrame != ProfileCallStack.end() &&
666*06c3fb27SDimitry Andric          InlCallStackIter != InlinedCallStack.end();
667*06c3fb27SDimitry Andric        ++StackFrame, ++InlCallStackIter) {
668*06c3fb27SDimitry Andric     uint64_t StackId = computeStackId(*StackFrame);
669*06c3fb27SDimitry Andric     if (StackId != *InlCallStackIter)
670*06c3fb27SDimitry Andric       return false;
671*06c3fb27SDimitry Andric   }
672*06c3fb27SDimitry Andric   // Return true if we found and matched all stack ids from the call
673*06c3fb27SDimitry Andric   // instruction.
674*06c3fb27SDimitry Andric   return InlCallStackIter == InlinedCallStack.end();
675*06c3fb27SDimitry Andric }
676*06c3fb27SDimitry Andric 
677*06c3fb27SDimitry Andric static void readMemprof(Module &M, Function &F,
678*06c3fb27SDimitry Andric                         IndexedInstrProfReader *MemProfReader,
679*06c3fb27SDimitry Andric                         const TargetLibraryInfo &TLI) {
680*06c3fb27SDimitry Andric   auto &Ctx = M.getContext();
681*06c3fb27SDimitry Andric 
682*06c3fb27SDimitry Andric   auto FuncName = getPGOFuncName(F);
683*06c3fb27SDimitry Andric   auto FuncGUID = Function::getGUID(FuncName);
684*06c3fb27SDimitry Andric   Expected<memprof::MemProfRecord> MemProfResult =
685*06c3fb27SDimitry Andric       MemProfReader->getMemProfRecord(FuncGUID);
686*06c3fb27SDimitry Andric   if (Error E = MemProfResult.takeError()) {
687*06c3fb27SDimitry Andric     handleAllErrors(std::move(E), [&](const InstrProfError &IPE) {
688*06c3fb27SDimitry Andric       auto Err = IPE.get();
689*06c3fb27SDimitry Andric       bool SkipWarning = false;
690*06c3fb27SDimitry Andric       LLVM_DEBUG(dbgs() << "Error in reading profile for Func " << FuncName
691*06c3fb27SDimitry Andric                         << ": ");
692*06c3fb27SDimitry Andric       if (Err == instrprof_error::unknown_function) {
693*06c3fb27SDimitry Andric         NumOfMemProfMissing++;
694*06c3fb27SDimitry Andric         SkipWarning = !PGOWarnMissing;
695*06c3fb27SDimitry Andric         LLVM_DEBUG(dbgs() << "unknown function");
696*06c3fb27SDimitry Andric       } else if (Err == instrprof_error::hash_mismatch) {
697*06c3fb27SDimitry Andric         SkipWarning =
698*06c3fb27SDimitry Andric             NoPGOWarnMismatch ||
699*06c3fb27SDimitry Andric             (NoPGOWarnMismatchComdatWeak &&
700*06c3fb27SDimitry Andric              (F.hasComdat() ||
701*06c3fb27SDimitry Andric               F.getLinkage() == GlobalValue::AvailableExternallyLinkage));
702*06c3fb27SDimitry Andric         LLVM_DEBUG(dbgs() << "hash mismatch (skip=" << SkipWarning << ")");
703*06c3fb27SDimitry Andric       }
704*06c3fb27SDimitry Andric 
705*06c3fb27SDimitry Andric       if (SkipWarning)
706*06c3fb27SDimitry Andric         return;
707*06c3fb27SDimitry Andric 
708*06c3fb27SDimitry Andric       std::string Msg = (IPE.message() + Twine(" ") + F.getName().str() +
709*06c3fb27SDimitry Andric                          Twine(" Hash = ") + std::to_string(FuncGUID))
710*06c3fb27SDimitry Andric                             .str();
711*06c3fb27SDimitry Andric 
712*06c3fb27SDimitry Andric       Ctx.diagnose(
713*06c3fb27SDimitry Andric           DiagnosticInfoPGOProfile(M.getName().data(), Msg, DS_Warning));
714*06c3fb27SDimitry Andric     });
715*06c3fb27SDimitry Andric     return;
716*06c3fb27SDimitry Andric   }
717*06c3fb27SDimitry Andric 
718*06c3fb27SDimitry Andric   // Build maps of the location hash to all profile data with that leaf location
719*06c3fb27SDimitry Andric   // (allocation info and the callsites).
720*06c3fb27SDimitry Andric   std::map<uint64_t, std::set<const AllocationInfo *>> LocHashToAllocInfo;
721*06c3fb27SDimitry Andric   // For the callsites we need to record the index of the associated frame in
722*06c3fb27SDimitry Andric   // the frame array (see comments below where the map entries are added).
723*06c3fb27SDimitry Andric   std::map<uint64_t, std::set<std::pair<const SmallVector<Frame> *, unsigned>>>
724*06c3fb27SDimitry Andric       LocHashToCallSites;
725*06c3fb27SDimitry Andric   const auto MemProfRec = std::move(MemProfResult.get());
726*06c3fb27SDimitry Andric   for (auto &AI : MemProfRec.AllocSites) {
727*06c3fb27SDimitry Andric     // Associate the allocation info with the leaf frame. The later matching
728*06c3fb27SDimitry Andric     // code will match any inlined call sequences in the IR with a longer prefix
729*06c3fb27SDimitry Andric     // of call stack frames.
730*06c3fb27SDimitry Andric     uint64_t StackId = computeStackId(AI.CallStack[0]);
731*06c3fb27SDimitry Andric     LocHashToAllocInfo[StackId].insert(&AI);
732*06c3fb27SDimitry Andric   }
733*06c3fb27SDimitry Andric   for (auto &CS : MemProfRec.CallSites) {
734*06c3fb27SDimitry Andric     // Need to record all frames from leaf up to and including this function,
735*06c3fb27SDimitry Andric     // as any of these may or may not have been inlined at this point.
736*06c3fb27SDimitry Andric     unsigned Idx = 0;
737*06c3fb27SDimitry Andric     for (auto &StackFrame : CS) {
738*06c3fb27SDimitry Andric       uint64_t StackId = computeStackId(StackFrame);
739*06c3fb27SDimitry Andric       LocHashToCallSites[StackId].insert(std::make_pair(&CS, Idx++));
740*06c3fb27SDimitry Andric       // Once we find this function, we can stop recording.
741*06c3fb27SDimitry Andric       if (StackFrame.Function == FuncGUID)
742*06c3fb27SDimitry Andric         break;
743*06c3fb27SDimitry Andric     }
744*06c3fb27SDimitry Andric     assert(Idx <= CS.size() && CS[Idx - 1].Function == FuncGUID);
745*06c3fb27SDimitry Andric   }
746*06c3fb27SDimitry Andric 
747*06c3fb27SDimitry Andric   auto GetOffset = [](const DILocation *DIL) {
748*06c3fb27SDimitry Andric     return (DIL->getLine() - DIL->getScope()->getSubprogram()->getLine()) &
749*06c3fb27SDimitry Andric            0xffff;
750*06c3fb27SDimitry Andric   };
751*06c3fb27SDimitry Andric 
752*06c3fb27SDimitry Andric   // Now walk the instructions, looking up the associated profile data using
753*06c3fb27SDimitry Andric   // dbug locations.
754*06c3fb27SDimitry Andric   for (auto &BB : F) {
755*06c3fb27SDimitry Andric     for (auto &I : BB) {
756*06c3fb27SDimitry Andric       if (I.isDebugOrPseudoInst())
757*06c3fb27SDimitry Andric         continue;
758*06c3fb27SDimitry Andric       // We are only interested in calls (allocation or interior call stack
759*06c3fb27SDimitry Andric       // context calls).
760*06c3fb27SDimitry Andric       auto *CI = dyn_cast<CallBase>(&I);
761*06c3fb27SDimitry Andric       if (!CI)
762*06c3fb27SDimitry Andric         continue;
763*06c3fb27SDimitry Andric       auto *CalledFunction = CI->getCalledFunction();
764*06c3fb27SDimitry Andric       if (CalledFunction && CalledFunction->isIntrinsic())
765*06c3fb27SDimitry Andric         continue;
766*06c3fb27SDimitry Andric       // List of call stack ids computed from the location hashes on debug
767*06c3fb27SDimitry Andric       // locations (leaf to inlined at root).
768*06c3fb27SDimitry Andric       std::vector<uint64_t> InlinedCallStack;
769*06c3fb27SDimitry Andric       // Was the leaf location found in one of the profile maps?
770*06c3fb27SDimitry Andric       bool LeafFound = false;
771*06c3fb27SDimitry Andric       // If leaf was found in a map, iterators pointing to its location in both
772*06c3fb27SDimitry Andric       // of the maps. It might exist in neither, one, or both (the latter case
773*06c3fb27SDimitry Andric       // can happen because we don't currently have discriminators to
774*06c3fb27SDimitry Andric       // distinguish the case when a single line/col maps to both an allocation
775*06c3fb27SDimitry Andric       // and another callsite).
776*06c3fb27SDimitry Andric       std::map<uint64_t, std::set<const AllocationInfo *>>::iterator
777*06c3fb27SDimitry Andric           AllocInfoIter;
778*06c3fb27SDimitry Andric       std::map<uint64_t, std::set<std::pair<const SmallVector<Frame> *,
779*06c3fb27SDimitry Andric                                             unsigned>>>::iterator CallSitesIter;
780*06c3fb27SDimitry Andric       for (const DILocation *DIL = I.getDebugLoc(); DIL != nullptr;
781*06c3fb27SDimitry Andric            DIL = DIL->getInlinedAt()) {
782*06c3fb27SDimitry Andric         // Use C++ linkage name if possible. Need to compile with
783*06c3fb27SDimitry Andric         // -fdebug-info-for-profiling to get linkage name.
784*06c3fb27SDimitry Andric         StringRef Name = DIL->getScope()->getSubprogram()->getLinkageName();
785*06c3fb27SDimitry Andric         if (Name.empty())
786*06c3fb27SDimitry Andric           Name = DIL->getScope()->getSubprogram()->getName();
787*06c3fb27SDimitry Andric         auto CalleeGUID = Function::getGUID(Name);
788*06c3fb27SDimitry Andric         auto StackId =
789*06c3fb27SDimitry Andric             computeStackId(CalleeGUID, GetOffset(DIL), DIL->getColumn());
790*06c3fb27SDimitry Andric         // LeafFound will only be false on the first iteration, since we either
791*06c3fb27SDimitry Andric         // set it true or break out of the loop below.
792*06c3fb27SDimitry Andric         if (!LeafFound) {
793*06c3fb27SDimitry Andric           AllocInfoIter = LocHashToAllocInfo.find(StackId);
794*06c3fb27SDimitry Andric           CallSitesIter = LocHashToCallSites.find(StackId);
795*06c3fb27SDimitry Andric           // Check if the leaf is in one of the maps. If not, no need to look
796*06c3fb27SDimitry Andric           // further at this call.
797*06c3fb27SDimitry Andric           if (AllocInfoIter == LocHashToAllocInfo.end() &&
798*06c3fb27SDimitry Andric               CallSitesIter == LocHashToCallSites.end())
799*06c3fb27SDimitry Andric             break;
800*06c3fb27SDimitry Andric           LeafFound = true;
801*06c3fb27SDimitry Andric         }
802*06c3fb27SDimitry Andric         InlinedCallStack.push_back(StackId);
803*06c3fb27SDimitry Andric       }
804*06c3fb27SDimitry Andric       // If leaf not in either of the maps, skip inst.
805*06c3fb27SDimitry Andric       if (!LeafFound)
806*06c3fb27SDimitry Andric         continue;
807*06c3fb27SDimitry Andric 
808*06c3fb27SDimitry Andric       // First add !memprof metadata from allocation info, if we found the
809*06c3fb27SDimitry Andric       // instruction's leaf location in that map, and if the rest of the
810*06c3fb27SDimitry Andric       // instruction's locations match the prefix Frame locations on an
811*06c3fb27SDimitry Andric       // allocation context with the same leaf.
812*06c3fb27SDimitry Andric       if (AllocInfoIter != LocHashToAllocInfo.end()) {
813*06c3fb27SDimitry Andric         // Only consider allocations via new, to reduce unnecessary metadata,
814*06c3fb27SDimitry Andric         // since those are the only allocations that will be targeted initially.
815*06c3fb27SDimitry Andric         if (!isNewLikeFn(CI, &TLI))
816*06c3fb27SDimitry Andric           continue;
817*06c3fb27SDimitry Andric         // We may match this instruction's location list to multiple MIB
818*06c3fb27SDimitry Andric         // contexts. Add them to a Trie specialized for trimming the contexts to
819*06c3fb27SDimitry Andric         // the minimal needed to disambiguate contexts with unique behavior.
820*06c3fb27SDimitry Andric         CallStackTrie AllocTrie;
821*06c3fb27SDimitry Andric         for (auto *AllocInfo : AllocInfoIter->second) {
822*06c3fb27SDimitry Andric           // Check the full inlined call stack against this one.
823*06c3fb27SDimitry Andric           // If we found and thus matched all frames on the call, include
824*06c3fb27SDimitry Andric           // this MIB.
825*06c3fb27SDimitry Andric           if (stackFrameIncludesInlinedCallStack(AllocInfo->CallStack,
826*06c3fb27SDimitry Andric                                                  InlinedCallStack))
827*06c3fb27SDimitry Andric             addCallStack(AllocTrie, AllocInfo);
828*06c3fb27SDimitry Andric         }
829*06c3fb27SDimitry Andric         // We might not have matched any to the full inlined call stack.
830*06c3fb27SDimitry Andric         // But if we did, create and attach metadata, or a function attribute if
831*06c3fb27SDimitry Andric         // all contexts have identical profiled behavior.
832*06c3fb27SDimitry Andric         if (!AllocTrie.empty()) {
833*06c3fb27SDimitry Andric           // MemprofMDAttached will be false if a function attribute was
834*06c3fb27SDimitry Andric           // attached.
835*06c3fb27SDimitry Andric           bool MemprofMDAttached = AllocTrie.buildAndAttachMIBMetadata(CI);
836*06c3fb27SDimitry Andric           assert(MemprofMDAttached == I.hasMetadata(LLVMContext::MD_memprof));
837*06c3fb27SDimitry Andric           if (MemprofMDAttached) {
838*06c3fb27SDimitry Andric             // Add callsite metadata for the instruction's location list so that
839*06c3fb27SDimitry Andric             // it simpler later on to identify which part of the MIB contexts
840*06c3fb27SDimitry Andric             // are from this particular instruction (including during inlining,
841*06c3fb27SDimitry Andric             // when the callsite metdata will be updated appropriately).
842*06c3fb27SDimitry Andric             // FIXME: can this be changed to strip out the matching stack
843*06c3fb27SDimitry Andric             // context ids from the MIB contexts and not add any callsite
844*06c3fb27SDimitry Andric             // metadata here to save space?
845*06c3fb27SDimitry Andric             addCallsiteMetadata(I, InlinedCallStack, Ctx);
846*06c3fb27SDimitry Andric           }
847*06c3fb27SDimitry Andric         }
848*06c3fb27SDimitry Andric         continue;
849*06c3fb27SDimitry Andric       }
850*06c3fb27SDimitry Andric 
851*06c3fb27SDimitry Andric       // Otherwise, add callsite metadata. If we reach here then we found the
852*06c3fb27SDimitry Andric       // instruction's leaf location in the callsites map and not the allocation
853*06c3fb27SDimitry Andric       // map.
854*06c3fb27SDimitry Andric       assert(CallSitesIter != LocHashToCallSites.end());
855*06c3fb27SDimitry Andric       for (auto CallStackIdx : CallSitesIter->second) {
856*06c3fb27SDimitry Andric         // If we found and thus matched all frames on the call, create and
857*06c3fb27SDimitry Andric         // attach call stack metadata.
858*06c3fb27SDimitry Andric         if (stackFrameIncludesInlinedCallStack(
859*06c3fb27SDimitry Andric                 *CallStackIdx.first, InlinedCallStack, CallStackIdx.second)) {
860*06c3fb27SDimitry Andric           addCallsiteMetadata(I, InlinedCallStack, Ctx);
861*06c3fb27SDimitry Andric           // Only need to find one with a matching call stack and add a single
862*06c3fb27SDimitry Andric           // callsite metadata.
863*06c3fb27SDimitry Andric           break;
864*06c3fb27SDimitry Andric         }
865*06c3fb27SDimitry Andric       }
866*06c3fb27SDimitry Andric     }
867*06c3fb27SDimitry Andric   }
868*06c3fb27SDimitry Andric }
869*06c3fb27SDimitry Andric 
870*06c3fb27SDimitry Andric MemProfUsePass::MemProfUsePass(std::string MemoryProfileFile,
871*06c3fb27SDimitry Andric                                IntrusiveRefCntPtr<vfs::FileSystem> FS)
872*06c3fb27SDimitry Andric     : MemoryProfileFileName(MemoryProfileFile), FS(FS) {
873*06c3fb27SDimitry Andric   if (!FS)
874*06c3fb27SDimitry Andric     this->FS = vfs::getRealFileSystem();
875*06c3fb27SDimitry Andric }
876*06c3fb27SDimitry Andric 
877*06c3fb27SDimitry Andric PreservedAnalyses MemProfUsePass::run(Module &M, ModuleAnalysisManager &AM) {
878*06c3fb27SDimitry Andric   LLVM_DEBUG(dbgs() << "Read in memory profile:");
879*06c3fb27SDimitry Andric   auto &Ctx = M.getContext();
880*06c3fb27SDimitry Andric   auto ReaderOrErr = IndexedInstrProfReader::create(MemoryProfileFileName, *FS);
881*06c3fb27SDimitry Andric   if (Error E = ReaderOrErr.takeError()) {
882*06c3fb27SDimitry Andric     handleAllErrors(std::move(E), [&](const ErrorInfoBase &EI) {
883*06c3fb27SDimitry Andric       Ctx.diagnose(
884*06c3fb27SDimitry Andric           DiagnosticInfoPGOProfile(MemoryProfileFileName.data(), EI.message()));
885*06c3fb27SDimitry Andric     });
886*06c3fb27SDimitry Andric     return PreservedAnalyses::all();
887*06c3fb27SDimitry Andric   }
888*06c3fb27SDimitry Andric 
889*06c3fb27SDimitry Andric   std::unique_ptr<IndexedInstrProfReader> MemProfReader =
890*06c3fb27SDimitry Andric       std::move(ReaderOrErr.get());
891*06c3fb27SDimitry Andric   if (!MemProfReader) {
892*06c3fb27SDimitry Andric     Ctx.diagnose(DiagnosticInfoPGOProfile(
893*06c3fb27SDimitry Andric         MemoryProfileFileName.data(), StringRef("Cannot get MemProfReader")));
894*06c3fb27SDimitry Andric     return PreservedAnalyses::all();
895*06c3fb27SDimitry Andric   }
896*06c3fb27SDimitry Andric 
897*06c3fb27SDimitry Andric   if (!MemProfReader->hasMemoryProfile()) {
898*06c3fb27SDimitry Andric     Ctx.diagnose(DiagnosticInfoPGOProfile(MemoryProfileFileName.data(),
899*06c3fb27SDimitry Andric                                           "Not a memory profile"));
900*06c3fb27SDimitry Andric     return PreservedAnalyses::all();
901*06c3fb27SDimitry Andric   }
902*06c3fb27SDimitry Andric 
903*06c3fb27SDimitry Andric   auto &FAM = AM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager();
904*06c3fb27SDimitry Andric 
905*06c3fb27SDimitry Andric   for (auto &F : M) {
906*06c3fb27SDimitry Andric     if (F.isDeclaration())
907*06c3fb27SDimitry Andric       continue;
908*06c3fb27SDimitry Andric 
909*06c3fb27SDimitry Andric     const TargetLibraryInfo &TLI = FAM.getResult<TargetLibraryAnalysis>(F);
910*06c3fb27SDimitry Andric     readMemprof(M, F, MemProfReader.get(), TLI);
911*06c3fb27SDimitry Andric   }
912*06c3fb27SDimitry Andric 
913*06c3fb27SDimitry Andric   return PreservedAnalyses::none();
914*06c3fb27SDimitry Andric }
915