blob: 6c479600a3d4cd258f1340390ed2d085f6effd05 [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);
45// Each new tool will get its own opt flag here.
46// These are converted to EfficiencySanitizerOptions for use
47// in the code.
48
49static cl::opt<bool> ClInstrumentLoadsAndStores(
50 "esan-instrument-loads-and-stores", cl::init(true),
51 cl::desc("Instrument loads and stores"), cl::Hidden);
52static cl::opt<bool> ClInstrumentMemIntrinsics(
53 "esan-instrument-memintrinsics", cl::init(true),
54 cl::desc("Instrument memintrinsics (memset/memcpy/memmove)"), cl::Hidden);
55
56STATISTIC(NumInstrumentedLoads, "Number of instrumented loads");
57STATISTIC(NumInstrumentedStores, "Number of instrumented stores");
58STATISTIC(NumFastpaths, "Number of instrumented fastpaths");
59STATISTIC(NumAccessesWithIrregularSize,
60 "Number of accesses with a size outside our targeted callout sizes");
61
Derek Bruening0b872d92016-05-24 22:48:24 +000062static const uint64_t EsanCtorAndDtorPriority = 0;
Derek Brueningd862c172016-04-21 21:30:22 +000063static const char *const EsanModuleCtorName = "esan.module_ctor";
Derek Bruening0b872d92016-05-24 22:48:24 +000064static const char *const EsanModuleDtorName = "esan.module_dtor";
Derek Brueningd862c172016-04-21 21:30:22 +000065static const char *const EsanInitName = "__esan_init";
Derek Bruening0b872d92016-05-24 22:48:24 +000066static const char *const EsanExitName = "__esan_exit";
Derek Brueningd862c172016-04-21 21:30:22 +000067
68namespace {
69
70static EfficiencySanitizerOptions
71OverrideOptionsFromCL(EfficiencySanitizerOptions Options) {
72 if (ClToolCacheFrag)
73 Options.ToolType = EfficiencySanitizerOptions::ESAN_CacheFrag;
74
75 // Direct opt invocation with no params will have the default ESAN_None.
76 // We run the default tool in that case.
77 if (Options.ToolType == EfficiencySanitizerOptions::ESAN_None)
78 Options.ToolType = EfficiencySanitizerOptions::ESAN_CacheFrag;
79
80 return Options;
81}
82
83/// EfficiencySanitizer: instrument each module to find performance issues.
Derek Brueningbc0a68e2016-05-20 20:00:05 +000084class EfficiencySanitizer : public ModulePass {
Derek Brueningd862c172016-04-21 21:30:22 +000085public:
86 EfficiencySanitizer(
87 const EfficiencySanitizerOptions &Opts = EfficiencySanitizerOptions())
Derek Brueningbc0a68e2016-05-20 20:00:05 +000088 : ModulePass(ID), Options(OverrideOptionsFromCL(Opts)) {}
Derek Brueningd862c172016-04-21 21:30:22 +000089 const char *getPassName() const override;
Derek Brueningbc0a68e2016-05-20 20:00:05 +000090 bool runOnModule(Module &M) override;
Derek Brueningd862c172016-04-21 21:30:22 +000091 static char ID;
92
93private:
Derek Brueningbc0a68e2016-05-20 20:00:05 +000094 bool initOnModule(Module &M);
Derek Brueningd862c172016-04-21 21:30:22 +000095 void initializeCallbacks(Module &M);
Derek Bruening0b872d92016-05-24 22:48:24 +000096 GlobalVariable *createEsanInitToolGV(Module &M);
97 void createDestructor(Module &M, GlobalVariable *GV);
Derek Brueningbc0a68e2016-05-20 20:00:05 +000098 bool runOnFunction(Function &F, Module &M);
Derek Brueningd862c172016-04-21 21:30:22 +000099 bool instrumentLoadOrStore(Instruction *I, const DataLayout &DL);
100 bool instrumentMemIntrinsic(MemIntrinsic *MI);
101 bool shouldIgnoreMemoryAccess(Instruction *I);
102 int getMemoryAccessFuncIndex(Value *Addr, const DataLayout &DL);
103 bool instrumentFastpath(Instruction *I, const DataLayout &DL, bool IsStore,
104 Value *Addr, unsigned Alignment);
105 // Each tool has its own fastpath routine:
106 bool instrumentFastpathCacheFrag(Instruction *I, const DataLayout &DL,
107 Value *Addr, unsigned Alignment);
108
109 EfficiencySanitizerOptions Options;
110 LLVMContext *Ctx;
111 Type *IntptrTy;
112 // Our slowpath involves callouts to the runtime library.
113 // Access sizes are powers of two: 1, 2, 4, 8, 16.
114 static const size_t NumberOfAccessSizes = 5;
115 Function *EsanAlignedLoad[NumberOfAccessSizes];
116 Function *EsanAlignedStore[NumberOfAccessSizes];
117 Function *EsanUnalignedLoad[NumberOfAccessSizes];
118 Function *EsanUnalignedStore[NumberOfAccessSizes];
119 // For irregular sizes of any alignment:
120 Function *EsanUnalignedLoadN, *EsanUnalignedStoreN;
121 Function *MemmoveFn, *MemcpyFn, *MemsetFn;
122 Function *EsanCtorFunction;
Derek Bruening0b872d92016-05-24 22:48:24 +0000123 Function *EsanDtorFunction;
Derek Brueningd862c172016-04-21 21:30:22 +0000124};
125} // namespace
126
127char EfficiencySanitizer::ID = 0;
128INITIALIZE_PASS(EfficiencySanitizer, "esan",
129 "EfficiencySanitizer: finds performance issues.", false, false)
130
131const char *EfficiencySanitizer::getPassName() const {
132 return "EfficiencySanitizer";
133}
134
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000135ModulePass *
Derek Brueningd862c172016-04-21 21:30:22 +0000136llvm::createEfficiencySanitizerPass(const EfficiencySanitizerOptions &Options) {
137 return new EfficiencySanitizer(Options);
138}
139
140void EfficiencySanitizer::initializeCallbacks(Module &M) {
141 IRBuilder<> IRB(M.getContext());
142 // Initialize the callbacks.
143 for (size_t Idx = 0; Idx < NumberOfAccessSizes; ++Idx) {
144 const unsigned ByteSize = 1U << Idx;
145 std::string ByteSizeStr = utostr(ByteSize);
146 // We'll inline the most common (i.e., aligned and frequent sizes)
147 // load + store instrumentation: these callouts are for the slowpath.
148 SmallString<32> AlignedLoadName("__esan_aligned_load" + ByteSizeStr);
149 EsanAlignedLoad[Idx] =
150 checkSanitizerInterfaceFunction(M.getOrInsertFunction(
151 AlignedLoadName, IRB.getVoidTy(), IRB.getInt8PtrTy(), nullptr));
152 SmallString<32> AlignedStoreName("__esan_aligned_store" + ByteSizeStr);
153 EsanAlignedStore[Idx] =
154 checkSanitizerInterfaceFunction(M.getOrInsertFunction(
155 AlignedStoreName, IRB.getVoidTy(), IRB.getInt8PtrTy(), nullptr));
156 SmallString<32> UnalignedLoadName("__esan_unaligned_load" + ByteSizeStr);
157 EsanUnalignedLoad[Idx] =
158 checkSanitizerInterfaceFunction(M.getOrInsertFunction(
159 UnalignedLoadName, IRB.getVoidTy(), IRB.getInt8PtrTy(), nullptr));
160 SmallString<32> UnalignedStoreName("__esan_unaligned_store" + ByteSizeStr);
161 EsanUnalignedStore[Idx] =
162 checkSanitizerInterfaceFunction(M.getOrInsertFunction(
163 UnalignedStoreName, IRB.getVoidTy(), IRB.getInt8PtrTy(), nullptr));
164 }
165 EsanUnalignedLoadN = checkSanitizerInterfaceFunction(
166 M.getOrInsertFunction("__esan_unaligned_loadN", IRB.getVoidTy(),
167 IRB.getInt8PtrTy(), IntptrTy, nullptr));
168 EsanUnalignedStoreN = checkSanitizerInterfaceFunction(
169 M.getOrInsertFunction("__esan_unaligned_storeN", IRB.getVoidTy(),
170 IRB.getInt8PtrTy(), IntptrTy, nullptr));
171 MemmoveFn = checkSanitizerInterfaceFunction(
172 M.getOrInsertFunction("memmove", IRB.getInt8PtrTy(), IRB.getInt8PtrTy(),
173 IRB.getInt8PtrTy(), IntptrTy, nullptr));
174 MemcpyFn = checkSanitizerInterfaceFunction(
175 M.getOrInsertFunction("memcpy", IRB.getInt8PtrTy(), IRB.getInt8PtrTy(),
176 IRB.getInt8PtrTy(), IntptrTy, nullptr));
177 MemsetFn = checkSanitizerInterfaceFunction(
178 M.getOrInsertFunction("memset", IRB.getInt8PtrTy(), IRB.getInt8PtrTy(),
179 IRB.getInt32Ty(), IntptrTy, nullptr));
180}
181
Derek Bruening0b872d92016-05-24 22:48:24 +0000182// Create the tool-specific global variable passed to EsanInit and EsanExit.
183GlobalVariable *EfficiencySanitizer::createEsanInitToolGV(Module &M) {
184 GlobalVariable *GV = nullptr;
185 // FIXME: create the tool specific global variable.
186 if (GV == nullptr) {
187 GV = new GlobalVariable(M, IntptrTy, true, GlobalVariable::InternalLinkage,
188 Constant::getNullValue(IntptrTy));
189 }
190 return GV;
191}
192
193void EfficiencySanitizer::createDestructor(Module &M, GlobalVariable *GV) {
194 EsanDtorFunction = Function::Create(FunctionType::get(Type::getVoidTy(*Ctx),
195 false),
196 GlobalValue::InternalLinkage,
197 EsanModuleDtorName, &M);
198 ReturnInst::Create(*Ctx, BasicBlock::Create(*Ctx, "", EsanDtorFunction));
199 IRBuilder<> IRB_Dtor(EsanDtorFunction->getEntryBlock().getTerminator());
200 Function *EsanExit = checkSanitizerInterfaceFunction(
201 M.getOrInsertFunction(EsanExitName, IRB_Dtor.getVoidTy(),
202 IntptrTy, nullptr));
203 EsanExit->setLinkage(Function::ExternalLinkage);
204 IRB_Dtor.CreateCall(EsanExit,
205 {IRB_Dtor.CreatePointerCast(GV, IntptrTy)});
206 appendToGlobalDtors(M, EsanDtorFunction, EsanCtorAndDtorPriority);
207}
208
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000209bool EfficiencySanitizer::initOnModule(Module &M) {
Derek Brueningd862c172016-04-21 21:30:22 +0000210 Ctx = &M.getContext();
211 const DataLayout &DL = M.getDataLayout();
212 IRBuilder<> IRB(M.getContext());
213 IntegerType *OrdTy = IRB.getInt32Ty();
214 IntptrTy = DL.getIntPtrType(M.getContext());
Derek Bruening0b872d92016-05-24 22:48:24 +0000215 // Create the variable passed to EsanInit and EsanExit.
216 GlobalVariable *GV = createEsanInitToolGV(M);
217 // Constructor
Derek Brueningd862c172016-04-21 21:30:22 +0000218 std::tie(EsanCtorFunction, std::ignore) = createSanitizerCtorAndInitFunctions(
Derek Bruening0b872d92016-05-24 22:48:24 +0000219 M, EsanModuleCtorName, EsanInitName, /*InitArgTypes=*/{OrdTy, IntptrTy},
Derek Brueningd862c172016-04-21 21:30:22 +0000220 /*InitArgs=*/{
Derek Bruening0b872d92016-05-24 22:48:24 +0000221 ConstantInt::get(OrdTy, static_cast<int>(Options.ToolType)),
222 ConstantExpr::getPointerCast(GV, IntptrTy)});
223 appendToGlobalCtors(M, EsanCtorFunction, EsanCtorAndDtorPriority);
Derek Brueningd862c172016-04-21 21:30:22 +0000224
Derek Bruening0b872d92016-05-24 22:48:24 +0000225 createDestructor(M, GV);
Derek Brueningd862c172016-04-21 21:30:22 +0000226 return true;
227}
228
229bool EfficiencySanitizer::shouldIgnoreMemoryAccess(Instruction *I) {
230 if (Options.ToolType == EfficiencySanitizerOptions::ESAN_CacheFrag) {
231 // We'd like to know about cache fragmentation in vtable accesses and
232 // constant data references, so we do not currently ignore anything.
233 return false;
234 }
235 // TODO(bruening): future tools will be returning true for some cases.
236 return false;
237}
238
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000239bool EfficiencySanitizer::runOnModule(Module &M) {
240 bool Res = initOnModule(M);
241 initializeCallbacks(M);
242 for (auto &F : M) {
243 Res |= runOnFunction(F, M);
244 }
245 return Res;
246}
247
248bool EfficiencySanitizer::runOnFunction(Function &F, Module &M) {
Derek Brueningd862c172016-04-21 21:30:22 +0000249 // This is required to prevent instrumenting the call to __esan_init from
250 // within the module constructor.
251 if (&F == EsanCtorFunction)
252 return false;
Derek Brueningd862c172016-04-21 21:30:22 +0000253 SmallVector<Instruction *, 8> LoadsAndStores;
254 SmallVector<Instruction *, 8> MemIntrinCalls;
255 bool Res = false;
Derek Bruening0b872d92016-05-24 22:48:24 +0000256 const DataLayout &DL = M.getDataLayout();
Derek Brueningd862c172016-04-21 21:30:22 +0000257
258 for (auto &BB : F) {
259 for (auto &Inst : BB) {
260 if ((isa<LoadInst>(Inst) || isa<StoreInst>(Inst) ||
261 isa<AtomicRMWInst>(Inst) || isa<AtomicCmpXchgInst>(Inst)) &&
262 !shouldIgnoreMemoryAccess(&Inst))
263 LoadsAndStores.push_back(&Inst);
264 else if (isa<MemIntrinsic>(Inst))
265 MemIntrinCalls.push_back(&Inst);
266 }
267 }
268
269 if (ClInstrumentLoadsAndStores) {
270 for (auto Inst : LoadsAndStores) {
271 Res |= instrumentLoadOrStore(Inst, DL);
272 }
273 }
274
275 if (ClInstrumentMemIntrinsics) {
276 for (auto Inst : MemIntrinCalls) {
277 Res |= instrumentMemIntrinsic(cast<MemIntrinsic>(Inst));
278 }
279 }
280
281 return Res;
282}
283
284bool EfficiencySanitizer::instrumentLoadOrStore(Instruction *I,
285 const DataLayout &DL) {
286 IRBuilder<> IRB(I);
287 bool IsStore;
288 Value *Addr;
289 unsigned Alignment;
290 if (LoadInst *Load = dyn_cast<LoadInst>(I)) {
291 IsStore = false;
292 Alignment = Load->getAlignment();
293 Addr = Load->getPointerOperand();
294 } else if (StoreInst *Store = dyn_cast<StoreInst>(I)) {
295 IsStore = true;
296 Alignment = Store->getAlignment();
297 Addr = Store->getPointerOperand();
298 } else if (AtomicRMWInst *RMW = dyn_cast<AtomicRMWInst>(I)) {
299 IsStore = true;
300 Alignment = 0;
301 Addr = RMW->getPointerOperand();
302 } else if (AtomicCmpXchgInst *Xchg = dyn_cast<AtomicCmpXchgInst>(I)) {
303 IsStore = true;
304 Alignment = 0;
305 Addr = Xchg->getPointerOperand();
306 } else
307 llvm_unreachable("Unsupported mem access type");
308
309 Type *OrigTy = cast<PointerType>(Addr->getType())->getElementType();
310 const uint32_t TypeSizeBytes = DL.getTypeStoreSizeInBits(OrigTy) / 8;
311 Value *OnAccessFunc = nullptr;
312 if (IsStore)
313 NumInstrumentedStores++;
314 else
315 NumInstrumentedLoads++;
316 int Idx = getMemoryAccessFuncIndex(Addr, DL);
317 if (Idx < 0) {
318 OnAccessFunc = IsStore ? EsanUnalignedStoreN : EsanUnalignedLoadN;
319 IRB.CreateCall(OnAccessFunc,
320 {IRB.CreatePointerCast(Addr, IRB.getInt8PtrTy()),
321 ConstantInt::get(IntptrTy, TypeSizeBytes)});
322 } else {
323 if (instrumentFastpath(I, DL, IsStore, Addr, Alignment)) {
324 NumFastpaths++;
325 return true;
326 }
327 if (Alignment == 0 || Alignment >= 8 || (Alignment % TypeSizeBytes) == 0)
328 OnAccessFunc = IsStore ? EsanAlignedStore[Idx] : EsanAlignedLoad[Idx];
329 else
330 OnAccessFunc = IsStore ? EsanUnalignedStore[Idx] : EsanUnalignedLoad[Idx];
331 IRB.CreateCall(OnAccessFunc,
332 IRB.CreatePointerCast(Addr, IRB.getInt8PtrTy()));
333 }
334 return true;
335}
336
337// It's simplest to replace the memset/memmove/memcpy intrinsics with
338// calls that the runtime library intercepts.
339// Our pass is late enough that calls should not turn back into intrinsics.
340bool EfficiencySanitizer::instrumentMemIntrinsic(MemIntrinsic *MI) {
341 IRBuilder<> IRB(MI);
342 bool Res = false;
343 if (isa<MemSetInst>(MI)) {
344 IRB.CreateCall(
345 MemsetFn,
346 {IRB.CreatePointerCast(MI->getArgOperand(0), IRB.getInt8PtrTy()),
347 IRB.CreateIntCast(MI->getArgOperand(1), IRB.getInt32Ty(), false),
348 IRB.CreateIntCast(MI->getArgOperand(2), IntptrTy, false)});
349 MI->eraseFromParent();
350 Res = true;
351 } else if (isa<MemTransferInst>(MI)) {
352 IRB.CreateCall(
353 isa<MemCpyInst>(MI) ? MemcpyFn : MemmoveFn,
354 {IRB.CreatePointerCast(MI->getArgOperand(0), IRB.getInt8PtrTy()),
355 IRB.CreatePointerCast(MI->getArgOperand(1), IRB.getInt8PtrTy()),
356 IRB.CreateIntCast(MI->getArgOperand(2), IntptrTy, false)});
357 MI->eraseFromParent();
358 Res = true;
359 } else
360 llvm_unreachable("Unsupported mem intrinsic type");
361 return Res;
362}
363
364int EfficiencySanitizer::getMemoryAccessFuncIndex(Value *Addr,
365 const DataLayout &DL) {
366 Type *OrigPtrTy = Addr->getType();
367 Type *OrigTy = cast<PointerType>(OrigPtrTy)->getElementType();
368 assert(OrigTy->isSized());
369 // The size is always a multiple of 8.
370 uint32_t TypeSizeBytes = DL.getTypeStoreSizeInBits(OrigTy) / 8;
371 if (TypeSizeBytes != 1 && TypeSizeBytes != 2 && TypeSizeBytes != 4 &&
372 TypeSizeBytes != 8 && TypeSizeBytes != 16) {
373 // Irregular sizes do not have per-size call targets.
374 NumAccessesWithIrregularSize++;
375 return -1;
376 }
377 size_t Idx = countTrailingZeros(TypeSizeBytes);
378 assert(Idx < NumberOfAccessSizes);
379 return Idx;
380}
381
382bool EfficiencySanitizer::instrumentFastpath(Instruction *I,
383 const DataLayout &DL, bool IsStore,
384 Value *Addr, unsigned Alignment) {
385 if (Options.ToolType == EfficiencySanitizerOptions::ESAN_CacheFrag) {
386 return instrumentFastpathCacheFrag(I, DL, Addr, Alignment);
387 }
388 return false;
389}
390
391bool EfficiencySanitizer::instrumentFastpathCacheFrag(Instruction *I,
392 const DataLayout &DL,
393 Value *Addr,
394 unsigned Alignment) {
395 // TODO(bruening): implement a fastpath for aligned accesses
396 return false;
397}