blob: 8feadd2827d5cf30964e7301e9c922e2af6e93c2 [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
62static const char *const EsanModuleCtorName = "esan.module_ctor";
63static const char *const EsanInitName = "__esan_init";
64
65namespace {
66
67static EfficiencySanitizerOptions
68OverrideOptionsFromCL(EfficiencySanitizerOptions Options) {
69 if (ClToolCacheFrag)
70 Options.ToolType = EfficiencySanitizerOptions::ESAN_CacheFrag;
71
72 // Direct opt invocation with no params will have the default ESAN_None.
73 // We run the default tool in that case.
74 if (Options.ToolType == EfficiencySanitizerOptions::ESAN_None)
75 Options.ToolType = EfficiencySanitizerOptions::ESAN_CacheFrag;
76
77 return Options;
78}
79
80/// EfficiencySanitizer: instrument each module to find performance issues.
Derek Brueningbc0a68e2016-05-20 20:00:05 +000081class EfficiencySanitizer : public ModulePass {
Derek Brueningd862c172016-04-21 21:30:22 +000082public:
83 EfficiencySanitizer(
84 const EfficiencySanitizerOptions &Opts = EfficiencySanitizerOptions())
Derek Brueningbc0a68e2016-05-20 20:00:05 +000085 : ModulePass(ID), Options(OverrideOptionsFromCL(Opts)) {}
Derek Brueningd862c172016-04-21 21:30:22 +000086 const char *getPassName() const override;
Derek Brueningbc0a68e2016-05-20 20:00:05 +000087 bool runOnModule(Module &M) override;
Derek Brueningd862c172016-04-21 21:30:22 +000088 static char ID;
89
90private:
Derek Brueningbc0a68e2016-05-20 20:00:05 +000091 bool initOnModule(Module &M);
Derek Brueningd862c172016-04-21 21:30:22 +000092 void initializeCallbacks(Module &M);
Derek Brueningbc0a68e2016-05-20 20:00:05 +000093 bool runOnFunction(Function &F, Module &M);
Derek Brueningd862c172016-04-21 21:30:22 +000094 bool instrumentLoadOrStore(Instruction *I, const DataLayout &DL);
95 bool instrumentMemIntrinsic(MemIntrinsic *MI);
96 bool shouldIgnoreMemoryAccess(Instruction *I);
97 int getMemoryAccessFuncIndex(Value *Addr, const DataLayout &DL);
98 bool instrumentFastpath(Instruction *I, const DataLayout &DL, bool IsStore,
99 Value *Addr, unsigned Alignment);
100 // Each tool has its own fastpath routine:
101 bool instrumentFastpathCacheFrag(Instruction *I, const DataLayout &DL,
102 Value *Addr, unsigned Alignment);
103
104 EfficiencySanitizerOptions Options;
105 LLVMContext *Ctx;
106 Type *IntptrTy;
107 // Our slowpath involves callouts to the runtime library.
108 // Access sizes are powers of two: 1, 2, 4, 8, 16.
109 static const size_t NumberOfAccessSizes = 5;
110 Function *EsanAlignedLoad[NumberOfAccessSizes];
111 Function *EsanAlignedStore[NumberOfAccessSizes];
112 Function *EsanUnalignedLoad[NumberOfAccessSizes];
113 Function *EsanUnalignedStore[NumberOfAccessSizes];
114 // For irregular sizes of any alignment:
115 Function *EsanUnalignedLoadN, *EsanUnalignedStoreN;
116 Function *MemmoveFn, *MemcpyFn, *MemsetFn;
117 Function *EsanCtorFunction;
118};
119} // namespace
120
121char EfficiencySanitizer::ID = 0;
122INITIALIZE_PASS(EfficiencySanitizer, "esan",
123 "EfficiencySanitizer: finds performance issues.", false, false)
124
125const char *EfficiencySanitizer::getPassName() const {
126 return "EfficiencySanitizer";
127}
128
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000129ModulePass *
Derek Brueningd862c172016-04-21 21:30:22 +0000130llvm::createEfficiencySanitizerPass(const EfficiencySanitizerOptions &Options) {
131 return new EfficiencySanitizer(Options);
132}
133
134void EfficiencySanitizer::initializeCallbacks(Module &M) {
135 IRBuilder<> IRB(M.getContext());
136 // Initialize the callbacks.
137 for (size_t Idx = 0; Idx < NumberOfAccessSizes; ++Idx) {
138 const unsigned ByteSize = 1U << Idx;
139 std::string ByteSizeStr = utostr(ByteSize);
140 // We'll inline the most common (i.e., aligned and frequent sizes)
141 // load + store instrumentation: these callouts are for the slowpath.
142 SmallString<32> AlignedLoadName("__esan_aligned_load" + ByteSizeStr);
143 EsanAlignedLoad[Idx] =
144 checkSanitizerInterfaceFunction(M.getOrInsertFunction(
145 AlignedLoadName, IRB.getVoidTy(), IRB.getInt8PtrTy(), nullptr));
146 SmallString<32> AlignedStoreName("__esan_aligned_store" + ByteSizeStr);
147 EsanAlignedStore[Idx] =
148 checkSanitizerInterfaceFunction(M.getOrInsertFunction(
149 AlignedStoreName, IRB.getVoidTy(), IRB.getInt8PtrTy(), nullptr));
150 SmallString<32> UnalignedLoadName("__esan_unaligned_load" + ByteSizeStr);
151 EsanUnalignedLoad[Idx] =
152 checkSanitizerInterfaceFunction(M.getOrInsertFunction(
153 UnalignedLoadName, IRB.getVoidTy(), IRB.getInt8PtrTy(), nullptr));
154 SmallString<32> UnalignedStoreName("__esan_unaligned_store" + ByteSizeStr);
155 EsanUnalignedStore[Idx] =
156 checkSanitizerInterfaceFunction(M.getOrInsertFunction(
157 UnalignedStoreName, IRB.getVoidTy(), IRB.getInt8PtrTy(), nullptr));
158 }
159 EsanUnalignedLoadN = checkSanitizerInterfaceFunction(
160 M.getOrInsertFunction("__esan_unaligned_loadN", IRB.getVoidTy(),
161 IRB.getInt8PtrTy(), IntptrTy, nullptr));
162 EsanUnalignedStoreN = checkSanitizerInterfaceFunction(
163 M.getOrInsertFunction("__esan_unaligned_storeN", IRB.getVoidTy(),
164 IRB.getInt8PtrTy(), IntptrTy, nullptr));
165 MemmoveFn = checkSanitizerInterfaceFunction(
166 M.getOrInsertFunction("memmove", IRB.getInt8PtrTy(), IRB.getInt8PtrTy(),
167 IRB.getInt8PtrTy(), IntptrTy, nullptr));
168 MemcpyFn = checkSanitizerInterfaceFunction(
169 M.getOrInsertFunction("memcpy", IRB.getInt8PtrTy(), IRB.getInt8PtrTy(),
170 IRB.getInt8PtrTy(), IntptrTy, nullptr));
171 MemsetFn = checkSanitizerInterfaceFunction(
172 M.getOrInsertFunction("memset", IRB.getInt8PtrTy(), IRB.getInt8PtrTy(),
173 IRB.getInt32Ty(), IntptrTy, nullptr));
174}
175
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000176bool EfficiencySanitizer::initOnModule(Module &M) {
Derek Brueningd862c172016-04-21 21:30:22 +0000177 Ctx = &M.getContext();
178 const DataLayout &DL = M.getDataLayout();
179 IRBuilder<> IRB(M.getContext());
180 IntegerType *OrdTy = IRB.getInt32Ty();
181 IntptrTy = DL.getIntPtrType(M.getContext());
182 std::tie(EsanCtorFunction, std::ignore) = createSanitizerCtorAndInitFunctions(
183 M, EsanModuleCtorName, EsanInitName, /*InitArgTypes=*/{OrdTy},
184 /*InitArgs=*/{
185 ConstantInt::get(OrdTy, static_cast<int>(Options.ToolType))});
186
187 appendToGlobalCtors(M, EsanCtorFunction, 0);
188
189 return true;
190}
191
192bool EfficiencySanitizer::shouldIgnoreMemoryAccess(Instruction *I) {
193 if (Options.ToolType == EfficiencySanitizerOptions::ESAN_CacheFrag) {
194 // We'd like to know about cache fragmentation in vtable accesses and
195 // constant data references, so we do not currently ignore anything.
196 return false;
197 }
198 // TODO(bruening): future tools will be returning true for some cases.
199 return false;
200}
201
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000202bool EfficiencySanitizer::runOnModule(Module &M) {
203 bool Res = initOnModule(M);
204 initializeCallbacks(M);
205 for (auto &F : M) {
206 Res |= runOnFunction(F, M);
207 }
208 return Res;
209}
210
211bool EfficiencySanitizer::runOnFunction(Function &F, Module &M) {
Derek Brueningd862c172016-04-21 21:30:22 +0000212 // This is required to prevent instrumenting the call to __esan_init from
213 // within the module constructor.
214 if (&F == EsanCtorFunction)
215 return false;
Derek Brueningd862c172016-04-21 21:30:22 +0000216 SmallVector<Instruction *, 8> LoadsAndStores;
217 SmallVector<Instruction *, 8> MemIntrinCalls;
218 bool Res = false;
219 const DataLayout &DL = F.getParent()->getDataLayout();
220
221 for (auto &BB : F) {
222 for (auto &Inst : BB) {
223 if ((isa<LoadInst>(Inst) || isa<StoreInst>(Inst) ||
224 isa<AtomicRMWInst>(Inst) || isa<AtomicCmpXchgInst>(Inst)) &&
225 !shouldIgnoreMemoryAccess(&Inst))
226 LoadsAndStores.push_back(&Inst);
227 else if (isa<MemIntrinsic>(Inst))
228 MemIntrinCalls.push_back(&Inst);
229 }
230 }
231
232 if (ClInstrumentLoadsAndStores) {
233 for (auto Inst : LoadsAndStores) {
234 Res |= instrumentLoadOrStore(Inst, DL);
235 }
236 }
237
238 if (ClInstrumentMemIntrinsics) {
239 for (auto Inst : MemIntrinCalls) {
240 Res |= instrumentMemIntrinsic(cast<MemIntrinsic>(Inst));
241 }
242 }
243
244 return Res;
245}
246
247bool EfficiencySanitizer::instrumentLoadOrStore(Instruction *I,
248 const DataLayout &DL) {
249 IRBuilder<> IRB(I);
250 bool IsStore;
251 Value *Addr;
252 unsigned Alignment;
253 if (LoadInst *Load = dyn_cast<LoadInst>(I)) {
254 IsStore = false;
255 Alignment = Load->getAlignment();
256 Addr = Load->getPointerOperand();
257 } else if (StoreInst *Store = dyn_cast<StoreInst>(I)) {
258 IsStore = true;
259 Alignment = Store->getAlignment();
260 Addr = Store->getPointerOperand();
261 } else if (AtomicRMWInst *RMW = dyn_cast<AtomicRMWInst>(I)) {
262 IsStore = true;
263 Alignment = 0;
264 Addr = RMW->getPointerOperand();
265 } else if (AtomicCmpXchgInst *Xchg = dyn_cast<AtomicCmpXchgInst>(I)) {
266 IsStore = true;
267 Alignment = 0;
268 Addr = Xchg->getPointerOperand();
269 } else
270 llvm_unreachable("Unsupported mem access type");
271
272 Type *OrigTy = cast<PointerType>(Addr->getType())->getElementType();
273 const uint32_t TypeSizeBytes = DL.getTypeStoreSizeInBits(OrigTy) / 8;
274 Value *OnAccessFunc = nullptr;
275 if (IsStore)
276 NumInstrumentedStores++;
277 else
278 NumInstrumentedLoads++;
279 int Idx = getMemoryAccessFuncIndex(Addr, DL);
280 if (Idx < 0) {
281 OnAccessFunc = IsStore ? EsanUnalignedStoreN : EsanUnalignedLoadN;
282 IRB.CreateCall(OnAccessFunc,
283 {IRB.CreatePointerCast(Addr, IRB.getInt8PtrTy()),
284 ConstantInt::get(IntptrTy, TypeSizeBytes)});
285 } else {
286 if (instrumentFastpath(I, DL, IsStore, Addr, Alignment)) {
287 NumFastpaths++;
288 return true;
289 }
290 if (Alignment == 0 || Alignment >= 8 || (Alignment % TypeSizeBytes) == 0)
291 OnAccessFunc = IsStore ? EsanAlignedStore[Idx] : EsanAlignedLoad[Idx];
292 else
293 OnAccessFunc = IsStore ? EsanUnalignedStore[Idx] : EsanUnalignedLoad[Idx];
294 IRB.CreateCall(OnAccessFunc,
295 IRB.CreatePointerCast(Addr, IRB.getInt8PtrTy()));
296 }
297 return true;
298}
299
300// It's simplest to replace the memset/memmove/memcpy intrinsics with
301// calls that the runtime library intercepts.
302// Our pass is late enough that calls should not turn back into intrinsics.
303bool EfficiencySanitizer::instrumentMemIntrinsic(MemIntrinsic *MI) {
304 IRBuilder<> IRB(MI);
305 bool Res = false;
306 if (isa<MemSetInst>(MI)) {
307 IRB.CreateCall(
308 MemsetFn,
309 {IRB.CreatePointerCast(MI->getArgOperand(0), IRB.getInt8PtrTy()),
310 IRB.CreateIntCast(MI->getArgOperand(1), IRB.getInt32Ty(), false),
311 IRB.CreateIntCast(MI->getArgOperand(2), IntptrTy, false)});
312 MI->eraseFromParent();
313 Res = true;
314 } else if (isa<MemTransferInst>(MI)) {
315 IRB.CreateCall(
316 isa<MemCpyInst>(MI) ? MemcpyFn : MemmoveFn,
317 {IRB.CreatePointerCast(MI->getArgOperand(0), IRB.getInt8PtrTy()),
318 IRB.CreatePointerCast(MI->getArgOperand(1), IRB.getInt8PtrTy()),
319 IRB.CreateIntCast(MI->getArgOperand(2), IntptrTy, false)});
320 MI->eraseFromParent();
321 Res = true;
322 } else
323 llvm_unreachable("Unsupported mem intrinsic type");
324 return Res;
325}
326
327int EfficiencySanitizer::getMemoryAccessFuncIndex(Value *Addr,
328 const DataLayout &DL) {
329 Type *OrigPtrTy = Addr->getType();
330 Type *OrigTy = cast<PointerType>(OrigPtrTy)->getElementType();
331 assert(OrigTy->isSized());
332 // The size is always a multiple of 8.
333 uint32_t TypeSizeBytes = DL.getTypeStoreSizeInBits(OrigTy) / 8;
334 if (TypeSizeBytes != 1 && TypeSizeBytes != 2 && TypeSizeBytes != 4 &&
335 TypeSizeBytes != 8 && TypeSizeBytes != 16) {
336 // Irregular sizes do not have per-size call targets.
337 NumAccessesWithIrregularSize++;
338 return -1;
339 }
340 size_t Idx = countTrailingZeros(TypeSizeBytes);
341 assert(Idx < NumberOfAccessSizes);
342 return Idx;
343}
344
345bool EfficiencySanitizer::instrumentFastpath(Instruction *I,
346 const DataLayout &DL, bool IsStore,
347 Value *Addr, unsigned Alignment) {
348 if (Options.ToolType == EfficiencySanitizerOptions::ESAN_CacheFrag) {
349 return instrumentFastpathCacheFrag(I, DL, Addr, Alignment);
350 }
351 return false;
352}
353
354bool EfficiencySanitizer::instrumentFastpathCacheFrag(Instruction *I,
355 const DataLayout &DL,
356 Value *Addr,
357 unsigned Alignment) {
358 // TODO(bruening): implement a fastpath for aligned accesses
359 return false;
360}