blob: 9c48ddd3034faf940ed93396656ff2a5915bd91c [file] [log] [blame]
Derek Brueningd862c172016-04-21 21:30:22 +00001//===-- EfficiencySanitizer.cpp - performance tuner -----------------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file is a part of EfficiencySanitizer, a family of performance tuners
11// that detects multiple performance issues via separate sub-tools.
12//
13// The instrumentation phase is straightforward:
14// - Take action on every memory access: either inlined instrumentation,
15// or Inserted calls to our run-time library.
16// - Optimizations may apply to avoid instrumenting some of the accesses.
17// - Turn mem{set,cpy,move} instrinsics into library calls.
18// The rest is handled by the run-time library.
19//===----------------------------------------------------------------------===//
20
21#include "llvm/Transforms/Instrumentation.h"
22#include "llvm/ADT/SmallString.h"
23#include "llvm/ADT/SmallVector.h"
24#include "llvm/ADT/Statistic.h"
25#include "llvm/ADT/StringExtras.h"
26#include "llvm/IR/Function.h"
27#include "llvm/IR/IRBuilder.h"
28#include "llvm/IR/IntrinsicInst.h"
29#include "llvm/IR/Module.h"
30#include "llvm/IR/Type.h"
31#include "llvm/Support/CommandLine.h"
32#include "llvm/Support/Debug.h"
33#include "llvm/Transforms/Utils/BasicBlockUtils.h"
34#include "llvm/Transforms/Utils/ModuleUtils.h"
35
36using namespace llvm;
37
38#define DEBUG_TYPE "esan"
39
40// The tool type must be just one of these ClTool* options, as the tools
41// cannot be combined due to shadow memory constraints.
42static cl::opt<bool>
43 ClToolCacheFrag("esan-cache-frag", cl::init(false),
44 cl::desc("Detect data cache fragmentation"), cl::Hidden);
Derek Bruening5662b932016-05-25 00:17:24 +000045static cl::opt<bool>
46 ClToolWorkingSet("esan-working-set", cl::init(false),
47 cl::desc("Measure the working set size"), cl::Hidden);
Derek Brueningd862c172016-04-21 21:30:22 +000048// Each new tool will get its own opt flag here.
49// These are converted to EfficiencySanitizerOptions for use
50// in the code.
51
52static cl::opt<bool> ClInstrumentLoadsAndStores(
53 "esan-instrument-loads-and-stores", cl::init(true),
54 cl::desc("Instrument loads and stores"), cl::Hidden);
55static cl::opt<bool> ClInstrumentMemIntrinsics(
56 "esan-instrument-memintrinsics", cl::init(true),
57 cl::desc("Instrument memintrinsics (memset/memcpy/memmove)"), cl::Hidden);
58
59STATISTIC(NumInstrumentedLoads, "Number of instrumented loads");
60STATISTIC(NumInstrumentedStores, "Number of instrumented stores");
61STATISTIC(NumFastpaths, "Number of instrumented fastpaths");
62STATISTIC(NumAccessesWithIrregularSize,
63 "Number of accesses with a size outside our targeted callout sizes");
64
Derek Bruening0b872d92016-05-24 22:48:24 +000065static const uint64_t EsanCtorAndDtorPriority = 0;
Derek Brueningd862c172016-04-21 21:30:22 +000066static const char *const EsanModuleCtorName = "esan.module_ctor";
Derek Bruening0b872d92016-05-24 22:48:24 +000067static const char *const EsanModuleDtorName = "esan.module_dtor";
Derek Brueningd862c172016-04-21 21:30:22 +000068static const char *const EsanInitName = "__esan_init";
Derek Bruening0b872d92016-05-24 22:48:24 +000069static const char *const EsanExitName = "__esan_exit";
Derek Brueningd862c172016-04-21 21:30:22 +000070
Derek Bruening5662b932016-05-25 00:17:24 +000071// We must keep these Shadow* constants consistent with the esan runtime.
72// FIXME: Try to place these shadow constants, the names of the __esan_*
73// interface functions, and the ToolType enum into a header shared between
74// llvm and compiler-rt.
75static const uint64_t ShadowMask = 0x00000fffffffffffull;
76static const uint64_t ShadowOffs[3] = { // Indexed by scale
77 0x0000130000000000ull,
78 0x0000220000000000ull,
79 0x0000440000000000ull,
80};
81// This array is indexed by the ToolType enum.
82static const int ShadowScale[] = {
83 0, // ESAN_None.
84 2, // ESAN_CacheFrag: 4B:1B, so 4 to 1 == >>2.
85 6, // ESAN_WorkingSet: 64B:1B, so 64 to 1 == >>6.
86};
87
Derek Brueningd862c172016-04-21 21:30:22 +000088namespace {
89
90static EfficiencySanitizerOptions
91OverrideOptionsFromCL(EfficiencySanitizerOptions Options) {
92 if (ClToolCacheFrag)
93 Options.ToolType = EfficiencySanitizerOptions::ESAN_CacheFrag;
Derek Bruening5662b932016-05-25 00:17:24 +000094 else if (ClToolWorkingSet)
95 Options.ToolType = EfficiencySanitizerOptions::ESAN_WorkingSet;
Derek Brueningd862c172016-04-21 21:30:22 +000096
97 // Direct opt invocation with no params will have the default ESAN_None.
98 // We run the default tool in that case.
99 if (Options.ToolType == EfficiencySanitizerOptions::ESAN_None)
100 Options.ToolType = EfficiencySanitizerOptions::ESAN_CacheFrag;
101
102 return Options;
103}
104
Qin Zhao1762eef2016-05-31 17:14:02 +0000105// Create a constant for Str so that we can pass it to the run-time lib.
106static GlobalVariable *createPrivateGlobalForString(Module &M, StringRef Str,
107 bool AllowMerging) {
108 Constant *StrConst = ConstantDataArray::getString(M.getContext(), Str);
109 // We use private linkage for module-local strings. If they can be merged
110 // with another one, we set the unnamed_addr attribute.
111 GlobalVariable *GV =
112 new GlobalVariable(M, StrConst->getType(), true,
113 GlobalValue::PrivateLinkage, StrConst, "");
114 if (AllowMerging)
115 GV->setUnnamedAddr(true);
116 GV->setAlignment(1); // Strings may not be merged w/o setting align 1.
117 return GV;
118}
119
Derek Brueningd862c172016-04-21 21:30:22 +0000120/// EfficiencySanitizer: instrument each module to find performance issues.
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000121class EfficiencySanitizer : public ModulePass {
Derek Brueningd862c172016-04-21 21:30:22 +0000122public:
123 EfficiencySanitizer(
124 const EfficiencySanitizerOptions &Opts = EfficiencySanitizerOptions())
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000125 : ModulePass(ID), Options(OverrideOptionsFromCL(Opts)) {}
Derek Brueningd862c172016-04-21 21:30:22 +0000126 const char *getPassName() const override;
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000127 bool runOnModule(Module &M) override;
Derek Brueningd862c172016-04-21 21:30:22 +0000128 static char ID;
129
130private:
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000131 bool initOnModule(Module &M);
Derek Brueningd862c172016-04-21 21:30:22 +0000132 void initializeCallbacks(Module &M);
Qin Zhao1762eef2016-05-31 17:14:02 +0000133 GlobalVariable *createCacheFragInfoGV(Module &M, Constant *UnitName);
134 Constant *createEsanInitToolInfoArg(Module &M);
135 void createDestructor(Module &M, Constant *ToolInfoArg);
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000136 bool runOnFunction(Function &F, Module &M);
Derek Brueningd862c172016-04-21 21:30:22 +0000137 bool instrumentLoadOrStore(Instruction *I, const DataLayout &DL);
138 bool instrumentMemIntrinsic(MemIntrinsic *MI);
139 bool shouldIgnoreMemoryAccess(Instruction *I);
140 int getMemoryAccessFuncIndex(Value *Addr, const DataLayout &DL);
Derek Bruening5662b932016-05-25 00:17:24 +0000141 Value *appToShadow(Value *Shadow, IRBuilder<> &IRB);
Derek Brueningd862c172016-04-21 21:30:22 +0000142 bool instrumentFastpath(Instruction *I, const DataLayout &DL, bool IsStore,
143 Value *Addr, unsigned Alignment);
144 // Each tool has its own fastpath routine:
145 bool instrumentFastpathCacheFrag(Instruction *I, const DataLayout &DL,
146 Value *Addr, unsigned Alignment);
Derek Bruening5662b932016-05-25 00:17:24 +0000147 bool instrumentFastpathWorkingSet(Instruction *I, const DataLayout &DL,
148 Value *Addr, unsigned Alignment);
Derek Brueningd862c172016-04-21 21:30:22 +0000149
150 EfficiencySanitizerOptions Options;
151 LLVMContext *Ctx;
152 Type *IntptrTy;
153 // Our slowpath involves callouts to the runtime library.
154 // Access sizes are powers of two: 1, 2, 4, 8, 16.
155 static const size_t NumberOfAccessSizes = 5;
156 Function *EsanAlignedLoad[NumberOfAccessSizes];
157 Function *EsanAlignedStore[NumberOfAccessSizes];
158 Function *EsanUnalignedLoad[NumberOfAccessSizes];
159 Function *EsanUnalignedStore[NumberOfAccessSizes];
160 // For irregular sizes of any alignment:
161 Function *EsanUnalignedLoadN, *EsanUnalignedStoreN;
162 Function *MemmoveFn, *MemcpyFn, *MemsetFn;
163 Function *EsanCtorFunction;
Derek Bruening0b872d92016-05-24 22:48:24 +0000164 Function *EsanDtorFunction;
Derek Brueningd862c172016-04-21 21:30:22 +0000165};
166} // namespace
167
168char EfficiencySanitizer::ID = 0;
169INITIALIZE_PASS(EfficiencySanitizer, "esan",
170 "EfficiencySanitizer: finds performance issues.", false, false)
171
172const char *EfficiencySanitizer::getPassName() const {
173 return "EfficiencySanitizer";
174}
175
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000176ModulePass *
Derek Brueningd862c172016-04-21 21:30:22 +0000177llvm::createEfficiencySanitizerPass(const EfficiencySanitizerOptions &Options) {
178 return new EfficiencySanitizer(Options);
179}
180
181void EfficiencySanitizer::initializeCallbacks(Module &M) {
182 IRBuilder<> IRB(M.getContext());
183 // Initialize the callbacks.
184 for (size_t Idx = 0; Idx < NumberOfAccessSizes; ++Idx) {
185 const unsigned ByteSize = 1U << Idx;
186 std::string ByteSizeStr = utostr(ByteSize);
187 // We'll inline the most common (i.e., aligned and frequent sizes)
188 // load + store instrumentation: these callouts are for the slowpath.
189 SmallString<32> AlignedLoadName("__esan_aligned_load" + ByteSizeStr);
190 EsanAlignedLoad[Idx] =
191 checkSanitizerInterfaceFunction(M.getOrInsertFunction(
192 AlignedLoadName, IRB.getVoidTy(), IRB.getInt8PtrTy(), nullptr));
193 SmallString<32> AlignedStoreName("__esan_aligned_store" + ByteSizeStr);
194 EsanAlignedStore[Idx] =
195 checkSanitizerInterfaceFunction(M.getOrInsertFunction(
196 AlignedStoreName, IRB.getVoidTy(), IRB.getInt8PtrTy(), nullptr));
197 SmallString<32> UnalignedLoadName("__esan_unaligned_load" + ByteSizeStr);
198 EsanUnalignedLoad[Idx] =
199 checkSanitizerInterfaceFunction(M.getOrInsertFunction(
200 UnalignedLoadName, IRB.getVoidTy(), IRB.getInt8PtrTy(), nullptr));
201 SmallString<32> UnalignedStoreName("__esan_unaligned_store" + ByteSizeStr);
202 EsanUnalignedStore[Idx] =
203 checkSanitizerInterfaceFunction(M.getOrInsertFunction(
204 UnalignedStoreName, IRB.getVoidTy(), IRB.getInt8PtrTy(), nullptr));
205 }
206 EsanUnalignedLoadN = checkSanitizerInterfaceFunction(
207 M.getOrInsertFunction("__esan_unaligned_loadN", IRB.getVoidTy(),
208 IRB.getInt8PtrTy(), IntptrTy, nullptr));
209 EsanUnalignedStoreN = checkSanitizerInterfaceFunction(
210 M.getOrInsertFunction("__esan_unaligned_storeN", IRB.getVoidTy(),
211 IRB.getInt8PtrTy(), IntptrTy, nullptr));
212 MemmoveFn = checkSanitizerInterfaceFunction(
213 M.getOrInsertFunction("memmove", IRB.getInt8PtrTy(), IRB.getInt8PtrTy(),
214 IRB.getInt8PtrTy(), IntptrTy, nullptr));
215 MemcpyFn = checkSanitizerInterfaceFunction(
216 M.getOrInsertFunction("memcpy", IRB.getInt8PtrTy(), IRB.getInt8PtrTy(),
217 IRB.getInt8PtrTy(), IntptrTy, nullptr));
218 MemsetFn = checkSanitizerInterfaceFunction(
219 M.getOrInsertFunction("memset", IRB.getInt8PtrTy(), IRB.getInt8PtrTy(),
220 IRB.getInt32Ty(), IntptrTy, nullptr));
221}
222
Qin Zhao1762eef2016-05-31 17:14:02 +0000223// Create the global variable for the cache-fragmentation tool.
224GlobalVariable *EfficiencySanitizer::createCacheFragInfoGV(
225 Module &M, Constant *UnitName) {
226 assert(Options.ToolType == EfficiencySanitizerOptions::ESAN_CacheFrag);
227
228 auto *Int8PtrTy = Type::getInt8PtrTy(*Ctx);
229 auto *Int8PtrPtrTy = Int8PtrTy->getPointerTo();
230 auto *Int32Ty = Type::getInt32Ty(*Ctx);
231 auto *Int64PtrTy = Type::getInt64PtrTy(*Ctx);
232 // This structure should be kept consistent with the StructInfo struct
233 // in the runtime library.
234 // struct StructInfo {
235 // const char *StructName;
236 // u32 NumOfFields;
237 // u64 *FieldCounters;
238 // const char **FieldTypeNames;
239 // };
240 auto *StructInfoTy =
241 StructType::get(Int8PtrTy, Int32Ty, Int64PtrTy, Int8PtrPtrTy, nullptr);
242 auto *StructInfoPtrTy = StructInfoTy->getPointerTo();
243 // This structure should be kept consistent with the CacheFragInfo struct
244 // in the runtime library.
245 // struct CacheFragInfo {
246 // const char *UnitName;
247 // u32 NumOfStructs;
248 // StructInfo *Structs;
249 // };
250 auto *CacheFragInfoTy =
251 StructType::get(Int8PtrTy, Int32Ty, StructInfoPtrTy, nullptr);
252
253 std::vector<StructType *> Vec = M.getIdentifiedStructTypes();
254 // FIXME: iterate over Vec and create the StructInfo array.
255 auto *CacheFragInfoGV = new GlobalVariable(
256 M, CacheFragInfoTy, true, GlobalVariable::InternalLinkage,
257 ConstantStruct::get(CacheFragInfoTy,
258 UnitName,
259 ConstantInt::get(Int32Ty, Vec.size()),
260 ConstantPointerNull::get(StructInfoPtrTy),
261 nullptr));
262 return CacheFragInfoGV;
Derek Bruening0b872d92016-05-24 22:48:24 +0000263}
264
Qin Zhao1762eef2016-05-31 17:14:02 +0000265// Create the tool-specific argument passed to EsanInit and EsanExit.
266Constant *EfficiencySanitizer::createEsanInitToolInfoArg(Module &M) {
267 // This structure contains tool-specific information about each compilation
268 // unit (module) and is passed to the runtime library.
269 GlobalVariable *ToolInfoGV = nullptr;
270
271 auto *Int8PtrTy = Type::getInt8PtrTy(*Ctx);
272 // Compilation unit name.
273 auto *UnitName = ConstantExpr::getPointerCast(
274 createPrivateGlobalForString(M, M.getModuleIdentifier(), true),
275 Int8PtrTy);
276
277 // Create the tool-specific variable.
278 if (Options.ToolType == EfficiencySanitizerOptions::ESAN_CacheFrag)
279 ToolInfoGV = createCacheFragInfoGV(M, UnitName);
280
281 if (ToolInfoGV != nullptr)
282 return ConstantExpr::getPointerCast(ToolInfoGV, Int8PtrTy);
283
284 // Create the null pointer if no tool-specific variable created.
285 return ConstantPointerNull::get(Int8PtrTy);
286}
287
288void EfficiencySanitizer::createDestructor(Module &M, Constant *ToolInfoArg) {
289 PointerType *Int8PtrTy = Type::getInt8PtrTy(*Ctx);
Derek Bruening0b872d92016-05-24 22:48:24 +0000290 EsanDtorFunction = Function::Create(FunctionType::get(Type::getVoidTy(*Ctx),
291 false),
292 GlobalValue::InternalLinkage,
293 EsanModuleDtorName, &M);
294 ReturnInst::Create(*Ctx, BasicBlock::Create(*Ctx, "", EsanDtorFunction));
295 IRBuilder<> IRB_Dtor(EsanDtorFunction->getEntryBlock().getTerminator());
296 Function *EsanExit = checkSanitizerInterfaceFunction(
297 M.getOrInsertFunction(EsanExitName, IRB_Dtor.getVoidTy(),
Qin Zhao1762eef2016-05-31 17:14:02 +0000298 Int8PtrTy, nullptr));
Derek Bruening0b872d92016-05-24 22:48:24 +0000299 EsanExit->setLinkage(Function::ExternalLinkage);
Qin Zhao1762eef2016-05-31 17:14:02 +0000300 IRB_Dtor.CreateCall(EsanExit, {ToolInfoArg});
Derek Bruening0b872d92016-05-24 22:48:24 +0000301 appendToGlobalDtors(M, EsanDtorFunction, EsanCtorAndDtorPriority);
302}
303
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000304bool EfficiencySanitizer::initOnModule(Module &M) {
Derek Brueningd862c172016-04-21 21:30:22 +0000305 Ctx = &M.getContext();
306 const DataLayout &DL = M.getDataLayout();
307 IRBuilder<> IRB(M.getContext());
308 IntegerType *OrdTy = IRB.getInt32Ty();
Qin Zhao1762eef2016-05-31 17:14:02 +0000309 PointerType *Int8PtrTy = Type::getInt8PtrTy(*Ctx);
Derek Brueningd862c172016-04-21 21:30:22 +0000310 IntptrTy = DL.getIntPtrType(M.getContext());
Derek Bruening0b872d92016-05-24 22:48:24 +0000311 // Create the variable passed to EsanInit and EsanExit.
Qin Zhao1762eef2016-05-31 17:14:02 +0000312 Constant *ToolInfoArg = createEsanInitToolInfoArg(M);
Derek Bruening0b872d92016-05-24 22:48:24 +0000313 // Constructor
Derek Brueningd862c172016-04-21 21:30:22 +0000314 std::tie(EsanCtorFunction, std::ignore) = createSanitizerCtorAndInitFunctions(
Qin Zhao1762eef2016-05-31 17:14:02 +0000315 M, EsanModuleCtorName, EsanInitName, /*InitArgTypes=*/{OrdTy, Int8PtrTy},
Derek Brueningd862c172016-04-21 21:30:22 +0000316 /*InitArgs=*/{
Derek Bruening0b872d92016-05-24 22:48:24 +0000317 ConstantInt::get(OrdTy, static_cast<int>(Options.ToolType)),
Qin Zhao1762eef2016-05-31 17:14:02 +0000318 ToolInfoArg});
Derek Bruening0b872d92016-05-24 22:48:24 +0000319 appendToGlobalCtors(M, EsanCtorFunction, EsanCtorAndDtorPriority);
Derek Brueningd862c172016-04-21 21:30:22 +0000320
Qin Zhao1762eef2016-05-31 17:14:02 +0000321 createDestructor(M, ToolInfoArg);
Derek Brueningd862c172016-04-21 21:30:22 +0000322 return true;
323}
324
Derek Bruening5662b932016-05-25 00:17:24 +0000325Value *EfficiencySanitizer::appToShadow(Value *Shadow, IRBuilder<> &IRB) {
326 // Shadow = ((App & Mask) + Offs) >> Scale
327 Shadow = IRB.CreateAnd(Shadow, ConstantInt::get(IntptrTy, ShadowMask));
328 uint64_t Offs;
329 int Scale = ShadowScale[Options.ToolType];
330 if (Scale <= 2)
331 Offs = ShadowOffs[Scale];
332 else
333 Offs = ShadowOffs[0] << Scale;
334 Shadow = IRB.CreateAdd(Shadow, ConstantInt::get(IntptrTy, Offs));
335 if (Scale > 0)
336 Shadow = IRB.CreateLShr(Shadow, Scale);
337 return Shadow;
338}
339
Derek Brueningd862c172016-04-21 21:30:22 +0000340bool EfficiencySanitizer::shouldIgnoreMemoryAccess(Instruction *I) {
341 if (Options.ToolType == EfficiencySanitizerOptions::ESAN_CacheFrag) {
342 // We'd like to know about cache fragmentation in vtable accesses and
343 // constant data references, so we do not currently ignore anything.
344 return false;
Derek Bruening5662b932016-05-25 00:17:24 +0000345 } else if (Options.ToolType == EfficiencySanitizerOptions::ESAN_WorkingSet) {
346 // TODO: the instrumentation disturbs the data layout on the stack, so we
347 // may want to add an option to ignore stack references (if we can
348 // distinguish them) to reduce overhead.
Derek Brueningd862c172016-04-21 21:30:22 +0000349 }
350 // TODO(bruening): future tools will be returning true for some cases.
351 return false;
352}
353
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000354bool EfficiencySanitizer::runOnModule(Module &M) {
355 bool Res = initOnModule(M);
356 initializeCallbacks(M);
357 for (auto &F : M) {
358 Res |= runOnFunction(F, M);
359 }
360 return Res;
361}
362
363bool EfficiencySanitizer::runOnFunction(Function &F, Module &M) {
Derek Brueningd862c172016-04-21 21:30:22 +0000364 // This is required to prevent instrumenting the call to __esan_init from
365 // within the module constructor.
366 if (&F == EsanCtorFunction)
367 return false;
Derek Brueningd862c172016-04-21 21:30:22 +0000368 SmallVector<Instruction *, 8> LoadsAndStores;
369 SmallVector<Instruction *, 8> MemIntrinCalls;
370 bool Res = false;
Derek Bruening0b872d92016-05-24 22:48:24 +0000371 const DataLayout &DL = M.getDataLayout();
Derek Brueningd862c172016-04-21 21:30:22 +0000372
373 for (auto &BB : F) {
374 for (auto &Inst : BB) {
375 if ((isa<LoadInst>(Inst) || isa<StoreInst>(Inst) ||
376 isa<AtomicRMWInst>(Inst) || isa<AtomicCmpXchgInst>(Inst)) &&
377 !shouldIgnoreMemoryAccess(&Inst))
378 LoadsAndStores.push_back(&Inst);
379 else if (isa<MemIntrinsic>(Inst))
380 MemIntrinCalls.push_back(&Inst);
381 }
382 }
383
384 if (ClInstrumentLoadsAndStores) {
385 for (auto Inst : LoadsAndStores) {
386 Res |= instrumentLoadOrStore(Inst, DL);
387 }
388 }
389
390 if (ClInstrumentMemIntrinsics) {
391 for (auto Inst : MemIntrinCalls) {
392 Res |= instrumentMemIntrinsic(cast<MemIntrinsic>(Inst));
393 }
394 }
395
396 return Res;
397}
398
399bool EfficiencySanitizer::instrumentLoadOrStore(Instruction *I,
400 const DataLayout &DL) {
401 IRBuilder<> IRB(I);
402 bool IsStore;
403 Value *Addr;
404 unsigned Alignment;
405 if (LoadInst *Load = dyn_cast<LoadInst>(I)) {
406 IsStore = false;
407 Alignment = Load->getAlignment();
408 Addr = Load->getPointerOperand();
409 } else if (StoreInst *Store = dyn_cast<StoreInst>(I)) {
410 IsStore = true;
411 Alignment = Store->getAlignment();
412 Addr = Store->getPointerOperand();
413 } else if (AtomicRMWInst *RMW = dyn_cast<AtomicRMWInst>(I)) {
414 IsStore = true;
415 Alignment = 0;
416 Addr = RMW->getPointerOperand();
417 } else if (AtomicCmpXchgInst *Xchg = dyn_cast<AtomicCmpXchgInst>(I)) {
418 IsStore = true;
419 Alignment = 0;
420 Addr = Xchg->getPointerOperand();
421 } else
422 llvm_unreachable("Unsupported mem access type");
423
424 Type *OrigTy = cast<PointerType>(Addr->getType())->getElementType();
425 const uint32_t TypeSizeBytes = DL.getTypeStoreSizeInBits(OrigTy) / 8;
426 Value *OnAccessFunc = nullptr;
Derek Bruening5662b932016-05-25 00:17:24 +0000427
428 // Convert 0 to the default alignment.
429 if (Alignment == 0)
430 Alignment = DL.getPrefTypeAlignment(OrigTy);
431
Derek Brueningd862c172016-04-21 21:30:22 +0000432 if (IsStore)
433 NumInstrumentedStores++;
434 else
435 NumInstrumentedLoads++;
436 int Idx = getMemoryAccessFuncIndex(Addr, DL);
437 if (Idx < 0) {
438 OnAccessFunc = IsStore ? EsanUnalignedStoreN : EsanUnalignedLoadN;
439 IRB.CreateCall(OnAccessFunc,
440 {IRB.CreatePointerCast(Addr, IRB.getInt8PtrTy()),
441 ConstantInt::get(IntptrTy, TypeSizeBytes)});
442 } else {
443 if (instrumentFastpath(I, DL, IsStore, Addr, Alignment)) {
444 NumFastpaths++;
445 return true;
446 }
447 if (Alignment == 0 || Alignment >= 8 || (Alignment % TypeSizeBytes) == 0)
448 OnAccessFunc = IsStore ? EsanAlignedStore[Idx] : EsanAlignedLoad[Idx];
449 else
450 OnAccessFunc = IsStore ? EsanUnalignedStore[Idx] : EsanUnalignedLoad[Idx];
451 IRB.CreateCall(OnAccessFunc,
452 IRB.CreatePointerCast(Addr, IRB.getInt8PtrTy()));
453 }
454 return true;
455}
456
457// It's simplest to replace the memset/memmove/memcpy intrinsics with
458// calls that the runtime library intercepts.
459// Our pass is late enough that calls should not turn back into intrinsics.
460bool EfficiencySanitizer::instrumentMemIntrinsic(MemIntrinsic *MI) {
461 IRBuilder<> IRB(MI);
462 bool Res = false;
463 if (isa<MemSetInst>(MI)) {
464 IRB.CreateCall(
465 MemsetFn,
466 {IRB.CreatePointerCast(MI->getArgOperand(0), IRB.getInt8PtrTy()),
467 IRB.CreateIntCast(MI->getArgOperand(1), IRB.getInt32Ty(), false),
468 IRB.CreateIntCast(MI->getArgOperand(2), IntptrTy, false)});
469 MI->eraseFromParent();
470 Res = true;
471 } else if (isa<MemTransferInst>(MI)) {
472 IRB.CreateCall(
473 isa<MemCpyInst>(MI) ? MemcpyFn : MemmoveFn,
474 {IRB.CreatePointerCast(MI->getArgOperand(0), IRB.getInt8PtrTy()),
475 IRB.CreatePointerCast(MI->getArgOperand(1), IRB.getInt8PtrTy()),
476 IRB.CreateIntCast(MI->getArgOperand(2), IntptrTy, false)});
477 MI->eraseFromParent();
478 Res = true;
479 } else
480 llvm_unreachable("Unsupported mem intrinsic type");
481 return Res;
482}
483
484int EfficiencySanitizer::getMemoryAccessFuncIndex(Value *Addr,
485 const DataLayout &DL) {
486 Type *OrigPtrTy = Addr->getType();
487 Type *OrigTy = cast<PointerType>(OrigPtrTy)->getElementType();
488 assert(OrigTy->isSized());
489 // The size is always a multiple of 8.
490 uint32_t TypeSizeBytes = DL.getTypeStoreSizeInBits(OrigTy) / 8;
491 if (TypeSizeBytes != 1 && TypeSizeBytes != 2 && TypeSizeBytes != 4 &&
492 TypeSizeBytes != 8 && TypeSizeBytes != 16) {
493 // Irregular sizes do not have per-size call targets.
494 NumAccessesWithIrregularSize++;
495 return -1;
496 }
497 size_t Idx = countTrailingZeros(TypeSizeBytes);
498 assert(Idx < NumberOfAccessSizes);
499 return Idx;
500}
501
502bool EfficiencySanitizer::instrumentFastpath(Instruction *I,
503 const DataLayout &DL, bool IsStore,
504 Value *Addr, unsigned Alignment) {
505 if (Options.ToolType == EfficiencySanitizerOptions::ESAN_CacheFrag) {
506 return instrumentFastpathCacheFrag(I, DL, Addr, Alignment);
Derek Bruening5662b932016-05-25 00:17:24 +0000507 } else if (Options.ToolType == EfficiencySanitizerOptions::ESAN_WorkingSet) {
508 return instrumentFastpathWorkingSet(I, DL, Addr, Alignment);
Derek Brueningd862c172016-04-21 21:30:22 +0000509 }
510 return false;
511}
512
513bool EfficiencySanitizer::instrumentFastpathCacheFrag(Instruction *I,
514 const DataLayout &DL,
515 Value *Addr,
516 unsigned Alignment) {
517 // TODO(bruening): implement a fastpath for aligned accesses
518 return false;
519}
Derek Bruening5662b932016-05-25 00:17:24 +0000520
521bool EfficiencySanitizer::instrumentFastpathWorkingSet(
522 Instruction *I, const DataLayout &DL, Value *Addr, unsigned Alignment) {
523 assert(ShadowScale[Options.ToolType] == 6); // The code below assumes this
524 IRBuilder<> IRB(I);
525 Type *OrigTy = cast<PointerType>(Addr->getType())->getElementType();
526 const uint32_t TypeSize = DL.getTypeStoreSizeInBits(OrigTy);
527 // Bail to the slowpath if the access might touch multiple cache lines.
528 // An access aligned to its size is guaranteed to be intra-cache-line.
529 // getMemoryAccessFuncIndex has already ruled out a size larger than 16
530 // and thus larger than a cache line for platforms this tool targets
531 // (and our shadow memory setup assumes 64-byte cache lines).
532 assert(TypeSize <= 64);
533 if (!(TypeSize == 8 ||
534 (Alignment % (TypeSize / 8)) == 0))
535 return false;
536
537 // We inline instrumentation to set the corresponding shadow bits for
538 // each cache line touched by the application. Here we handle a single
539 // load or store where we've already ruled out the possibility that it
540 // might touch more than one cache line and thus we simply update the
541 // shadow memory for a single cache line.
542 // Our shadow memory model is fine with races when manipulating shadow values.
543 // We generate the following code:
544 //
545 // const char BitMask = 0x81;
546 // char *ShadowAddr = appToShadow(AppAddr);
547 // if ((*ShadowAddr & BitMask) != BitMask)
548 // *ShadowAddr |= Bitmask;
549 //
550 Value *AddrPtr = IRB.CreatePointerCast(Addr, IntptrTy);
551 Value *ShadowPtr = appToShadow(AddrPtr, IRB);
552 Type *ShadowTy = IntegerType::get(*Ctx, 8U);
553 Type *ShadowPtrTy = PointerType::get(ShadowTy, 0);
554 // The bottom bit is used for the current sampling period's working set.
555 // The top bit is used for the total working set. We set both on each
556 // memory access, if they are not already set.
557 Value *ValueMask = ConstantInt::get(ShadowTy, 0x81); // 10000001B
558
559 Value *OldValue = IRB.CreateLoad(IRB.CreateIntToPtr(ShadowPtr, ShadowPtrTy));
560 // The AND and CMP will be turned into a TEST instruction by the compiler.
561 Value *Cmp = IRB.CreateICmpNE(IRB.CreateAnd(OldValue, ValueMask), ValueMask);
562 TerminatorInst *CmpTerm = SplitBlockAndInsertIfThen(Cmp, I, false);
563 // FIXME: do I need to call SetCurrentDebugLocation?
564 IRB.SetInsertPoint(CmpTerm);
565 // We use OR to set the shadow bits to avoid corrupting the middle 6 bits,
566 // which are used by the runtime library.
567 Value *NewVal = IRB.CreateOr(OldValue, ValueMask);
568 IRB.CreateStore(NewVal, IRB.CreateIntToPtr(ShadowPtr, ShadowPtrTy));
569 IRB.SetInsertPoint(I);
570
571 return true;
572}