blob: 9a400011aed87e944e8a671d79cf7823453cbe4a [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
105/// EfficiencySanitizer: instrument each module to find performance issues.
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000106class EfficiencySanitizer : public ModulePass {
Derek Brueningd862c172016-04-21 21:30:22 +0000107public:
108 EfficiencySanitizer(
109 const EfficiencySanitizerOptions &Opts = EfficiencySanitizerOptions())
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000110 : ModulePass(ID), Options(OverrideOptionsFromCL(Opts)) {}
Derek Brueningd862c172016-04-21 21:30:22 +0000111 const char *getPassName() const override;
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000112 bool runOnModule(Module &M) override;
Derek Brueningd862c172016-04-21 21:30:22 +0000113 static char ID;
114
115private:
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000116 bool initOnModule(Module &M);
Derek Brueningd862c172016-04-21 21:30:22 +0000117 void initializeCallbacks(Module &M);
Derek Bruening0b872d92016-05-24 22:48:24 +0000118 GlobalVariable *createEsanInitToolGV(Module &M);
119 void createDestructor(Module &M, GlobalVariable *GV);
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000120 bool runOnFunction(Function &F, Module &M);
Derek Brueningd862c172016-04-21 21:30:22 +0000121 bool instrumentLoadOrStore(Instruction *I, const DataLayout &DL);
122 bool instrumentMemIntrinsic(MemIntrinsic *MI);
123 bool shouldIgnoreMemoryAccess(Instruction *I);
124 int getMemoryAccessFuncIndex(Value *Addr, const DataLayout &DL);
Derek Bruening5662b932016-05-25 00:17:24 +0000125 Value *appToShadow(Value *Shadow, IRBuilder<> &IRB);
Derek Brueningd862c172016-04-21 21:30:22 +0000126 bool instrumentFastpath(Instruction *I, const DataLayout &DL, bool IsStore,
127 Value *Addr, unsigned Alignment);
128 // Each tool has its own fastpath routine:
129 bool instrumentFastpathCacheFrag(Instruction *I, const DataLayout &DL,
130 Value *Addr, unsigned Alignment);
Derek Bruening5662b932016-05-25 00:17:24 +0000131 bool instrumentFastpathWorkingSet(Instruction *I, const DataLayout &DL,
132 Value *Addr, unsigned Alignment);
Derek Brueningd862c172016-04-21 21:30:22 +0000133
134 EfficiencySanitizerOptions Options;
135 LLVMContext *Ctx;
136 Type *IntptrTy;
137 // Our slowpath involves callouts to the runtime library.
138 // Access sizes are powers of two: 1, 2, 4, 8, 16.
139 static const size_t NumberOfAccessSizes = 5;
140 Function *EsanAlignedLoad[NumberOfAccessSizes];
141 Function *EsanAlignedStore[NumberOfAccessSizes];
142 Function *EsanUnalignedLoad[NumberOfAccessSizes];
143 Function *EsanUnalignedStore[NumberOfAccessSizes];
144 // For irregular sizes of any alignment:
145 Function *EsanUnalignedLoadN, *EsanUnalignedStoreN;
146 Function *MemmoveFn, *MemcpyFn, *MemsetFn;
147 Function *EsanCtorFunction;
Derek Bruening0b872d92016-05-24 22:48:24 +0000148 Function *EsanDtorFunction;
Derek Brueningd862c172016-04-21 21:30:22 +0000149};
150} // namespace
151
152char EfficiencySanitizer::ID = 0;
153INITIALIZE_PASS(EfficiencySanitizer, "esan",
154 "EfficiencySanitizer: finds performance issues.", false, false)
155
156const char *EfficiencySanitizer::getPassName() const {
157 return "EfficiencySanitizer";
158}
159
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000160ModulePass *
Derek Brueningd862c172016-04-21 21:30:22 +0000161llvm::createEfficiencySanitizerPass(const EfficiencySanitizerOptions &Options) {
162 return new EfficiencySanitizer(Options);
163}
164
165void EfficiencySanitizer::initializeCallbacks(Module &M) {
166 IRBuilder<> IRB(M.getContext());
167 // Initialize the callbacks.
168 for (size_t Idx = 0; Idx < NumberOfAccessSizes; ++Idx) {
169 const unsigned ByteSize = 1U << Idx;
170 std::string ByteSizeStr = utostr(ByteSize);
171 // We'll inline the most common (i.e., aligned and frequent sizes)
172 // load + store instrumentation: these callouts are for the slowpath.
173 SmallString<32> AlignedLoadName("__esan_aligned_load" + ByteSizeStr);
174 EsanAlignedLoad[Idx] =
175 checkSanitizerInterfaceFunction(M.getOrInsertFunction(
176 AlignedLoadName, IRB.getVoidTy(), IRB.getInt8PtrTy(), nullptr));
177 SmallString<32> AlignedStoreName("__esan_aligned_store" + ByteSizeStr);
178 EsanAlignedStore[Idx] =
179 checkSanitizerInterfaceFunction(M.getOrInsertFunction(
180 AlignedStoreName, IRB.getVoidTy(), IRB.getInt8PtrTy(), nullptr));
181 SmallString<32> UnalignedLoadName("__esan_unaligned_load" + ByteSizeStr);
182 EsanUnalignedLoad[Idx] =
183 checkSanitizerInterfaceFunction(M.getOrInsertFunction(
184 UnalignedLoadName, IRB.getVoidTy(), IRB.getInt8PtrTy(), nullptr));
185 SmallString<32> UnalignedStoreName("__esan_unaligned_store" + ByteSizeStr);
186 EsanUnalignedStore[Idx] =
187 checkSanitizerInterfaceFunction(M.getOrInsertFunction(
188 UnalignedStoreName, IRB.getVoidTy(), IRB.getInt8PtrTy(), nullptr));
189 }
190 EsanUnalignedLoadN = checkSanitizerInterfaceFunction(
191 M.getOrInsertFunction("__esan_unaligned_loadN", IRB.getVoidTy(),
192 IRB.getInt8PtrTy(), IntptrTy, nullptr));
193 EsanUnalignedStoreN = checkSanitizerInterfaceFunction(
194 M.getOrInsertFunction("__esan_unaligned_storeN", IRB.getVoidTy(),
195 IRB.getInt8PtrTy(), IntptrTy, nullptr));
196 MemmoveFn = checkSanitizerInterfaceFunction(
197 M.getOrInsertFunction("memmove", IRB.getInt8PtrTy(), IRB.getInt8PtrTy(),
198 IRB.getInt8PtrTy(), IntptrTy, nullptr));
199 MemcpyFn = checkSanitizerInterfaceFunction(
200 M.getOrInsertFunction("memcpy", IRB.getInt8PtrTy(), IRB.getInt8PtrTy(),
201 IRB.getInt8PtrTy(), IntptrTy, nullptr));
202 MemsetFn = checkSanitizerInterfaceFunction(
203 M.getOrInsertFunction("memset", IRB.getInt8PtrTy(), IRB.getInt8PtrTy(),
204 IRB.getInt32Ty(), IntptrTy, nullptr));
205}
206
Derek Bruening0b872d92016-05-24 22:48:24 +0000207// Create the tool-specific global variable passed to EsanInit and EsanExit.
208GlobalVariable *EfficiencySanitizer::createEsanInitToolGV(Module &M) {
209 GlobalVariable *GV = nullptr;
210 // FIXME: create the tool specific global variable.
211 if (GV == nullptr) {
212 GV = new GlobalVariable(M, IntptrTy, true, GlobalVariable::InternalLinkage,
213 Constant::getNullValue(IntptrTy));
214 }
215 return GV;
216}
217
218void EfficiencySanitizer::createDestructor(Module &M, GlobalVariable *GV) {
219 EsanDtorFunction = Function::Create(FunctionType::get(Type::getVoidTy(*Ctx),
220 false),
221 GlobalValue::InternalLinkage,
222 EsanModuleDtorName, &M);
223 ReturnInst::Create(*Ctx, BasicBlock::Create(*Ctx, "", EsanDtorFunction));
224 IRBuilder<> IRB_Dtor(EsanDtorFunction->getEntryBlock().getTerminator());
225 Function *EsanExit = checkSanitizerInterfaceFunction(
226 M.getOrInsertFunction(EsanExitName, IRB_Dtor.getVoidTy(),
227 IntptrTy, nullptr));
228 EsanExit->setLinkage(Function::ExternalLinkage);
229 IRB_Dtor.CreateCall(EsanExit,
230 {IRB_Dtor.CreatePointerCast(GV, IntptrTy)});
231 appendToGlobalDtors(M, EsanDtorFunction, EsanCtorAndDtorPriority);
232}
233
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000234bool EfficiencySanitizer::initOnModule(Module &M) {
Derek Brueningd862c172016-04-21 21:30:22 +0000235 Ctx = &M.getContext();
236 const DataLayout &DL = M.getDataLayout();
237 IRBuilder<> IRB(M.getContext());
238 IntegerType *OrdTy = IRB.getInt32Ty();
239 IntptrTy = DL.getIntPtrType(M.getContext());
Derek Bruening0b872d92016-05-24 22:48:24 +0000240 // Create the variable passed to EsanInit and EsanExit.
241 GlobalVariable *GV = createEsanInitToolGV(M);
242 // Constructor
Derek Brueningd862c172016-04-21 21:30:22 +0000243 std::tie(EsanCtorFunction, std::ignore) = createSanitizerCtorAndInitFunctions(
Derek Bruening0b872d92016-05-24 22:48:24 +0000244 M, EsanModuleCtorName, EsanInitName, /*InitArgTypes=*/{OrdTy, IntptrTy},
Derek Brueningd862c172016-04-21 21:30:22 +0000245 /*InitArgs=*/{
Derek Bruening0b872d92016-05-24 22:48:24 +0000246 ConstantInt::get(OrdTy, static_cast<int>(Options.ToolType)),
247 ConstantExpr::getPointerCast(GV, IntptrTy)});
248 appendToGlobalCtors(M, EsanCtorFunction, EsanCtorAndDtorPriority);
Derek Brueningd862c172016-04-21 21:30:22 +0000249
Derek Bruening0b872d92016-05-24 22:48:24 +0000250 createDestructor(M, GV);
Derek Brueningd862c172016-04-21 21:30:22 +0000251 return true;
252}
253
Derek Bruening5662b932016-05-25 00:17:24 +0000254Value *EfficiencySanitizer::appToShadow(Value *Shadow, IRBuilder<> &IRB) {
255 // Shadow = ((App & Mask) + Offs) >> Scale
256 Shadow = IRB.CreateAnd(Shadow, ConstantInt::get(IntptrTy, ShadowMask));
257 uint64_t Offs;
258 int Scale = ShadowScale[Options.ToolType];
259 if (Scale <= 2)
260 Offs = ShadowOffs[Scale];
261 else
262 Offs = ShadowOffs[0] << Scale;
263 Shadow = IRB.CreateAdd(Shadow, ConstantInt::get(IntptrTy, Offs));
264 if (Scale > 0)
265 Shadow = IRB.CreateLShr(Shadow, Scale);
266 return Shadow;
267}
268
Derek Brueningd862c172016-04-21 21:30:22 +0000269bool EfficiencySanitizer::shouldIgnoreMemoryAccess(Instruction *I) {
270 if (Options.ToolType == EfficiencySanitizerOptions::ESAN_CacheFrag) {
271 // We'd like to know about cache fragmentation in vtable accesses and
272 // constant data references, so we do not currently ignore anything.
273 return false;
Derek Bruening5662b932016-05-25 00:17:24 +0000274 } else if (Options.ToolType == EfficiencySanitizerOptions::ESAN_WorkingSet) {
275 // TODO: the instrumentation disturbs the data layout on the stack, so we
276 // may want to add an option to ignore stack references (if we can
277 // distinguish them) to reduce overhead.
Derek Brueningd862c172016-04-21 21:30:22 +0000278 }
279 // TODO(bruening): future tools will be returning true for some cases.
280 return false;
281}
282
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000283bool EfficiencySanitizer::runOnModule(Module &M) {
284 bool Res = initOnModule(M);
285 initializeCallbacks(M);
286 for (auto &F : M) {
287 Res |= runOnFunction(F, M);
288 }
289 return Res;
290}
291
292bool EfficiencySanitizer::runOnFunction(Function &F, Module &M) {
Derek Brueningd862c172016-04-21 21:30:22 +0000293 // This is required to prevent instrumenting the call to __esan_init from
294 // within the module constructor.
295 if (&F == EsanCtorFunction)
296 return false;
Derek Brueningd862c172016-04-21 21:30:22 +0000297 SmallVector<Instruction *, 8> LoadsAndStores;
298 SmallVector<Instruction *, 8> MemIntrinCalls;
299 bool Res = false;
Derek Bruening0b872d92016-05-24 22:48:24 +0000300 const DataLayout &DL = M.getDataLayout();
Derek Brueningd862c172016-04-21 21:30:22 +0000301
302 for (auto &BB : F) {
303 for (auto &Inst : BB) {
304 if ((isa<LoadInst>(Inst) || isa<StoreInst>(Inst) ||
305 isa<AtomicRMWInst>(Inst) || isa<AtomicCmpXchgInst>(Inst)) &&
306 !shouldIgnoreMemoryAccess(&Inst))
307 LoadsAndStores.push_back(&Inst);
308 else if (isa<MemIntrinsic>(Inst))
309 MemIntrinCalls.push_back(&Inst);
310 }
311 }
312
313 if (ClInstrumentLoadsAndStores) {
314 for (auto Inst : LoadsAndStores) {
315 Res |= instrumentLoadOrStore(Inst, DL);
316 }
317 }
318
319 if (ClInstrumentMemIntrinsics) {
320 for (auto Inst : MemIntrinCalls) {
321 Res |= instrumentMemIntrinsic(cast<MemIntrinsic>(Inst));
322 }
323 }
324
325 return Res;
326}
327
328bool EfficiencySanitizer::instrumentLoadOrStore(Instruction *I,
329 const DataLayout &DL) {
330 IRBuilder<> IRB(I);
331 bool IsStore;
332 Value *Addr;
333 unsigned Alignment;
334 if (LoadInst *Load = dyn_cast<LoadInst>(I)) {
335 IsStore = false;
336 Alignment = Load->getAlignment();
337 Addr = Load->getPointerOperand();
338 } else if (StoreInst *Store = dyn_cast<StoreInst>(I)) {
339 IsStore = true;
340 Alignment = Store->getAlignment();
341 Addr = Store->getPointerOperand();
342 } else if (AtomicRMWInst *RMW = dyn_cast<AtomicRMWInst>(I)) {
343 IsStore = true;
344 Alignment = 0;
345 Addr = RMW->getPointerOperand();
346 } else if (AtomicCmpXchgInst *Xchg = dyn_cast<AtomicCmpXchgInst>(I)) {
347 IsStore = true;
348 Alignment = 0;
349 Addr = Xchg->getPointerOperand();
350 } else
351 llvm_unreachable("Unsupported mem access type");
352
353 Type *OrigTy = cast<PointerType>(Addr->getType())->getElementType();
354 const uint32_t TypeSizeBytes = DL.getTypeStoreSizeInBits(OrigTy) / 8;
355 Value *OnAccessFunc = nullptr;
Derek Bruening5662b932016-05-25 00:17:24 +0000356
357 // Convert 0 to the default alignment.
358 if (Alignment == 0)
359 Alignment = DL.getPrefTypeAlignment(OrigTy);
360
Derek Brueningd862c172016-04-21 21:30:22 +0000361 if (IsStore)
362 NumInstrumentedStores++;
363 else
364 NumInstrumentedLoads++;
365 int Idx = getMemoryAccessFuncIndex(Addr, DL);
366 if (Idx < 0) {
367 OnAccessFunc = IsStore ? EsanUnalignedStoreN : EsanUnalignedLoadN;
368 IRB.CreateCall(OnAccessFunc,
369 {IRB.CreatePointerCast(Addr, IRB.getInt8PtrTy()),
370 ConstantInt::get(IntptrTy, TypeSizeBytes)});
371 } else {
372 if (instrumentFastpath(I, DL, IsStore, Addr, Alignment)) {
373 NumFastpaths++;
374 return true;
375 }
376 if (Alignment == 0 || Alignment >= 8 || (Alignment % TypeSizeBytes) == 0)
377 OnAccessFunc = IsStore ? EsanAlignedStore[Idx] : EsanAlignedLoad[Idx];
378 else
379 OnAccessFunc = IsStore ? EsanUnalignedStore[Idx] : EsanUnalignedLoad[Idx];
380 IRB.CreateCall(OnAccessFunc,
381 IRB.CreatePointerCast(Addr, IRB.getInt8PtrTy()));
382 }
383 return true;
384}
385
386// It's simplest to replace the memset/memmove/memcpy intrinsics with
387// calls that the runtime library intercepts.
388// Our pass is late enough that calls should not turn back into intrinsics.
389bool EfficiencySanitizer::instrumentMemIntrinsic(MemIntrinsic *MI) {
390 IRBuilder<> IRB(MI);
391 bool Res = false;
392 if (isa<MemSetInst>(MI)) {
393 IRB.CreateCall(
394 MemsetFn,
395 {IRB.CreatePointerCast(MI->getArgOperand(0), IRB.getInt8PtrTy()),
396 IRB.CreateIntCast(MI->getArgOperand(1), IRB.getInt32Ty(), false),
397 IRB.CreateIntCast(MI->getArgOperand(2), IntptrTy, false)});
398 MI->eraseFromParent();
399 Res = true;
400 } else if (isa<MemTransferInst>(MI)) {
401 IRB.CreateCall(
402 isa<MemCpyInst>(MI) ? MemcpyFn : MemmoveFn,
403 {IRB.CreatePointerCast(MI->getArgOperand(0), IRB.getInt8PtrTy()),
404 IRB.CreatePointerCast(MI->getArgOperand(1), IRB.getInt8PtrTy()),
405 IRB.CreateIntCast(MI->getArgOperand(2), IntptrTy, false)});
406 MI->eraseFromParent();
407 Res = true;
408 } else
409 llvm_unreachable("Unsupported mem intrinsic type");
410 return Res;
411}
412
413int EfficiencySanitizer::getMemoryAccessFuncIndex(Value *Addr,
414 const DataLayout &DL) {
415 Type *OrigPtrTy = Addr->getType();
416 Type *OrigTy = cast<PointerType>(OrigPtrTy)->getElementType();
417 assert(OrigTy->isSized());
418 // The size is always a multiple of 8.
419 uint32_t TypeSizeBytes = DL.getTypeStoreSizeInBits(OrigTy) / 8;
420 if (TypeSizeBytes != 1 && TypeSizeBytes != 2 && TypeSizeBytes != 4 &&
421 TypeSizeBytes != 8 && TypeSizeBytes != 16) {
422 // Irregular sizes do not have per-size call targets.
423 NumAccessesWithIrregularSize++;
424 return -1;
425 }
426 size_t Idx = countTrailingZeros(TypeSizeBytes);
427 assert(Idx < NumberOfAccessSizes);
428 return Idx;
429}
430
431bool EfficiencySanitizer::instrumentFastpath(Instruction *I,
432 const DataLayout &DL, bool IsStore,
433 Value *Addr, unsigned Alignment) {
434 if (Options.ToolType == EfficiencySanitizerOptions::ESAN_CacheFrag) {
435 return instrumentFastpathCacheFrag(I, DL, Addr, Alignment);
Derek Bruening5662b932016-05-25 00:17:24 +0000436 } else if (Options.ToolType == EfficiencySanitizerOptions::ESAN_WorkingSet) {
437 return instrumentFastpathWorkingSet(I, DL, Addr, Alignment);
Derek Brueningd862c172016-04-21 21:30:22 +0000438 }
439 return false;
440}
441
442bool EfficiencySanitizer::instrumentFastpathCacheFrag(Instruction *I,
443 const DataLayout &DL,
444 Value *Addr,
445 unsigned Alignment) {
446 // TODO(bruening): implement a fastpath for aligned accesses
447 return false;
448}
Derek Bruening5662b932016-05-25 00:17:24 +0000449
450bool EfficiencySanitizer::instrumentFastpathWorkingSet(
451 Instruction *I, const DataLayout &DL, Value *Addr, unsigned Alignment) {
452 assert(ShadowScale[Options.ToolType] == 6); // The code below assumes this
453 IRBuilder<> IRB(I);
454 Type *OrigTy = cast<PointerType>(Addr->getType())->getElementType();
455 const uint32_t TypeSize = DL.getTypeStoreSizeInBits(OrigTy);
456 // Bail to the slowpath if the access might touch multiple cache lines.
457 // An access aligned to its size is guaranteed to be intra-cache-line.
458 // getMemoryAccessFuncIndex has already ruled out a size larger than 16
459 // and thus larger than a cache line for platforms this tool targets
460 // (and our shadow memory setup assumes 64-byte cache lines).
461 assert(TypeSize <= 64);
462 if (!(TypeSize == 8 ||
463 (Alignment % (TypeSize / 8)) == 0))
464 return false;
465
466 // We inline instrumentation to set the corresponding shadow bits for
467 // each cache line touched by the application. Here we handle a single
468 // load or store where we've already ruled out the possibility that it
469 // might touch more than one cache line and thus we simply update the
470 // shadow memory for a single cache line.
471 // Our shadow memory model is fine with races when manipulating shadow values.
472 // We generate the following code:
473 //
474 // const char BitMask = 0x81;
475 // char *ShadowAddr = appToShadow(AppAddr);
476 // if ((*ShadowAddr & BitMask) != BitMask)
477 // *ShadowAddr |= Bitmask;
478 //
479 Value *AddrPtr = IRB.CreatePointerCast(Addr, IntptrTy);
480 Value *ShadowPtr = appToShadow(AddrPtr, IRB);
481 Type *ShadowTy = IntegerType::get(*Ctx, 8U);
482 Type *ShadowPtrTy = PointerType::get(ShadowTy, 0);
483 // The bottom bit is used for the current sampling period's working set.
484 // The top bit is used for the total working set. We set both on each
485 // memory access, if they are not already set.
486 Value *ValueMask = ConstantInt::get(ShadowTy, 0x81); // 10000001B
487
488 Value *OldValue = IRB.CreateLoad(IRB.CreateIntToPtr(ShadowPtr, ShadowPtrTy));
489 // The AND and CMP will be turned into a TEST instruction by the compiler.
490 Value *Cmp = IRB.CreateICmpNE(IRB.CreateAnd(OldValue, ValueMask), ValueMask);
491 TerminatorInst *CmpTerm = SplitBlockAndInsertIfThen(Cmp, I, false);
492 // FIXME: do I need to call SetCurrentDebugLocation?
493 IRB.SetInsertPoint(CmpTerm);
494 // We use OR to set the shadow bits to avoid corrupting the middle 6 bits,
495 // which are used by the runtime library.
496 Value *NewVal = IRB.CreateOr(OldValue, ValueMask);
497 IRB.CreateStore(NewVal, IRB.CreateIntToPtr(ShadowPtr, ShadowPtrTy));
498 IRB.SetInsertPoint(I);
499
500 return true;
501}