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