blob: 63391b373c5d10f970fb8a76c4335001c8897cc4 [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"
Qin Zhao6d3bd682016-06-02 17:30:47 +000033#include "llvm/Support/raw_ostream.h"
Derek Brueningd862c172016-04-21 21:30:22 +000034#include "llvm/Transforms/Utils/BasicBlockUtils.h"
35#include "llvm/Transforms/Utils/ModuleUtils.h"
36
37using namespace llvm;
38
39#define DEBUG_TYPE "esan"
40
41// The tool type must be just one of these ClTool* options, as the tools
42// cannot be combined due to shadow memory constraints.
43static cl::opt<bool>
44 ClToolCacheFrag("esan-cache-frag", cl::init(false),
45 cl::desc("Detect data cache fragmentation"), cl::Hidden);
Derek Bruening5662b932016-05-25 00:17:24 +000046static cl::opt<bool>
47 ClToolWorkingSet("esan-working-set", cl::init(false),
48 cl::desc("Measure the working set size"), cl::Hidden);
Derek Brueningd862c172016-04-21 21:30:22 +000049// Each new tool will get its own opt flag here.
50// These are converted to EfficiencySanitizerOptions for use
51// in the code.
52
53static cl::opt<bool> ClInstrumentLoadsAndStores(
54 "esan-instrument-loads-and-stores", cl::init(true),
55 cl::desc("Instrument loads and stores"), cl::Hidden);
56static cl::opt<bool> ClInstrumentMemIntrinsics(
57 "esan-instrument-memintrinsics", cl::init(true),
58 cl::desc("Instrument memintrinsics (memset/memcpy/memmove)"), cl::Hidden);
Qin Zhaod677d882016-06-10 00:48:53 +000059static cl::opt<bool> ClInstrumentFastpath(
60 "esan-instrument-fastpath", cl::init(true),
61 cl::desc("Instrument fastpath"), cl::Hidden);
Derek Brueningd862c172016-04-21 21:30:22 +000062
Derek Bruening9ef57722016-06-03 22:29:52 +000063// Experiments show that the performance difference can be 2x or more,
64// and accuracy loss is typically negligible, so we turn this on by default.
65static cl::opt<bool> ClAssumeIntraCacheLine(
66 "esan-assume-intra-cache-line", cl::init(true),
67 cl::desc("Assume each memory access touches just one cache line, for "
68 "better performance but with a potential loss of accuracy."),
69 cl::Hidden);
70
Derek Brueningd862c172016-04-21 21:30:22 +000071STATISTIC(NumInstrumentedLoads, "Number of instrumented loads");
72STATISTIC(NumInstrumentedStores, "Number of instrumented stores");
73STATISTIC(NumFastpaths, "Number of instrumented fastpaths");
74STATISTIC(NumAccessesWithIrregularSize,
75 "Number of accesses with a size outside our targeted callout sizes");
Qin Zhao6d3bd682016-06-02 17:30:47 +000076STATISTIC(NumIgnoredStructs, "Number of ignored structs");
Qin Zhaoc14c2492016-06-03 02:33:04 +000077STATISTIC(NumIgnoredGEPs, "Number of ignored GEP instructions");
78STATISTIC(NumInstrumentedGEPs, "Number of instrumented GEP instructions");
Derek Bruening9ef57722016-06-03 22:29:52 +000079STATISTIC(NumAssumedIntraCacheLine,
80 "Number of accesses assumed to be intra-cache-line");
Derek Brueningd862c172016-04-21 21:30:22 +000081
Derek Bruening0b872d92016-05-24 22:48:24 +000082static const uint64_t EsanCtorAndDtorPriority = 0;
Derek Brueningd862c172016-04-21 21:30:22 +000083static const char *const EsanModuleCtorName = "esan.module_ctor";
Derek Bruening0b872d92016-05-24 22:48:24 +000084static const char *const EsanModuleDtorName = "esan.module_dtor";
Derek Brueningd862c172016-04-21 21:30:22 +000085static const char *const EsanInitName = "__esan_init";
Derek Bruening0b872d92016-05-24 22:48:24 +000086static const char *const EsanExitName = "__esan_exit";
Derek Brueningd862c172016-04-21 21:30:22 +000087
Derek Bruening4252a162016-06-03 19:40:37 +000088// We need to specify the tool to the runtime earlier than
89// the ctor is called in some cases, so we set a global variable.
90static const char *const EsanWhichToolName = "__esan_which_tool";
91
Derek Bruening5662b932016-05-25 00:17:24 +000092// We must keep these Shadow* constants consistent with the esan runtime.
93// FIXME: Try to place these shadow constants, the names of the __esan_*
94// interface functions, and the ToolType enum into a header shared between
95// llvm and compiler-rt.
96static const uint64_t ShadowMask = 0x00000fffffffffffull;
97static const uint64_t ShadowOffs[3] = { // Indexed by scale
98 0x0000130000000000ull,
99 0x0000220000000000ull,
100 0x0000440000000000ull,
101};
102// This array is indexed by the ToolType enum.
103static const int ShadowScale[] = {
104 0, // ESAN_None.
105 2, // ESAN_CacheFrag: 4B:1B, so 4 to 1 == >>2.
106 6, // ESAN_WorkingSet: 64B:1B, so 64 to 1 == >>6.
107};
108
Qin Zhao6d3bd682016-06-02 17:30:47 +0000109// MaxStructCounterNameSize is a soft size limit to avoid insanely long
110// names for those extremely large structs.
111static const unsigned MaxStructCounterNameSize = 512;
112
Derek Brueningd862c172016-04-21 21:30:22 +0000113namespace {
114
115static EfficiencySanitizerOptions
116OverrideOptionsFromCL(EfficiencySanitizerOptions Options) {
117 if (ClToolCacheFrag)
118 Options.ToolType = EfficiencySanitizerOptions::ESAN_CacheFrag;
Derek Bruening5662b932016-05-25 00:17:24 +0000119 else if (ClToolWorkingSet)
120 Options.ToolType = EfficiencySanitizerOptions::ESAN_WorkingSet;
Derek Brueningd862c172016-04-21 21:30:22 +0000121
122 // Direct opt invocation with no params will have the default ESAN_None.
123 // We run the default tool in that case.
124 if (Options.ToolType == EfficiencySanitizerOptions::ESAN_None)
125 Options.ToolType = EfficiencySanitizerOptions::ESAN_CacheFrag;
126
127 return Options;
128}
129
Qin Zhao1762eef2016-05-31 17:14:02 +0000130// Create a constant for Str so that we can pass it to the run-time lib.
131static GlobalVariable *createPrivateGlobalForString(Module &M, StringRef Str,
132 bool AllowMerging) {
133 Constant *StrConst = ConstantDataArray::getString(M.getContext(), Str);
134 // We use private linkage for module-local strings. If they can be merged
135 // with another one, we set the unnamed_addr attribute.
136 GlobalVariable *GV =
137 new GlobalVariable(M, StrConst->getType(), true,
138 GlobalValue::PrivateLinkage, StrConst, "");
139 if (AllowMerging)
140 GV->setUnnamedAddr(true);
141 GV->setAlignment(1); // Strings may not be merged w/o setting align 1.
142 return GV;
143}
144
Derek Brueningd862c172016-04-21 21:30:22 +0000145/// EfficiencySanitizer: instrument each module to find performance issues.
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000146class EfficiencySanitizer : public ModulePass {
Derek Brueningd862c172016-04-21 21:30:22 +0000147public:
148 EfficiencySanitizer(
149 const EfficiencySanitizerOptions &Opts = EfficiencySanitizerOptions())
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000150 : ModulePass(ID), Options(OverrideOptionsFromCL(Opts)) {}
Derek Brueningd862c172016-04-21 21:30:22 +0000151 const char *getPassName() const override;
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000152 bool runOnModule(Module &M) override;
Derek Brueningd862c172016-04-21 21:30:22 +0000153 static char ID;
154
155private:
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000156 bool initOnModule(Module &M);
Derek Brueningd862c172016-04-21 21:30:22 +0000157 void initializeCallbacks(Module &M);
Qin Zhao6d3bd682016-06-02 17:30:47 +0000158 bool shouldIgnoreStructType(StructType *StructTy);
159 void createStructCounterName(
160 StructType *StructTy, SmallString<MaxStructCounterNameSize> &NameStr);
Qin Zhao1762eef2016-05-31 17:14:02 +0000161 GlobalVariable *createCacheFragInfoGV(Module &M, Constant *UnitName);
162 Constant *createEsanInitToolInfoArg(Module &M);
163 void createDestructor(Module &M, Constant *ToolInfoArg);
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000164 bool runOnFunction(Function &F, Module &M);
Derek Brueningd862c172016-04-21 21:30:22 +0000165 bool instrumentLoadOrStore(Instruction *I, const DataLayout &DL);
166 bool instrumentMemIntrinsic(MemIntrinsic *MI);
Qin Zhaoc14c2492016-06-03 02:33:04 +0000167 bool instrumentGetElementPtr(Instruction *I, Module &M);
Derek Brueningd862c172016-04-21 21:30:22 +0000168 bool shouldIgnoreMemoryAccess(Instruction *I);
169 int getMemoryAccessFuncIndex(Value *Addr, const DataLayout &DL);
Derek Bruening5662b932016-05-25 00:17:24 +0000170 Value *appToShadow(Value *Shadow, IRBuilder<> &IRB);
Derek Brueningd862c172016-04-21 21:30:22 +0000171 bool instrumentFastpath(Instruction *I, const DataLayout &DL, bool IsStore,
172 Value *Addr, unsigned Alignment);
173 // Each tool has its own fastpath routine:
174 bool instrumentFastpathCacheFrag(Instruction *I, const DataLayout &DL,
175 Value *Addr, unsigned Alignment);
Derek Bruening5662b932016-05-25 00:17:24 +0000176 bool instrumentFastpathWorkingSet(Instruction *I, const DataLayout &DL,
177 Value *Addr, unsigned Alignment);
Derek Brueningd862c172016-04-21 21:30:22 +0000178
179 EfficiencySanitizerOptions Options;
180 LLVMContext *Ctx;
181 Type *IntptrTy;
182 // Our slowpath involves callouts to the runtime library.
183 // Access sizes are powers of two: 1, 2, 4, 8, 16.
184 static const size_t NumberOfAccessSizes = 5;
185 Function *EsanAlignedLoad[NumberOfAccessSizes];
186 Function *EsanAlignedStore[NumberOfAccessSizes];
187 Function *EsanUnalignedLoad[NumberOfAccessSizes];
188 Function *EsanUnalignedStore[NumberOfAccessSizes];
189 // For irregular sizes of any alignment:
190 Function *EsanUnalignedLoadN, *EsanUnalignedStoreN;
191 Function *MemmoveFn, *MemcpyFn, *MemsetFn;
192 Function *EsanCtorFunction;
Derek Bruening0b872d92016-05-24 22:48:24 +0000193 Function *EsanDtorFunction;
Qin Zhaoc14c2492016-06-03 02:33:04 +0000194 // Remember the counter variable for each struct type to avoid
195 // recomputing the variable name later during instrumentation.
196 std::map<Type *, GlobalVariable *> StructTyMap;
Derek Brueningd862c172016-04-21 21:30:22 +0000197};
198} // namespace
199
200char EfficiencySanitizer::ID = 0;
201INITIALIZE_PASS(EfficiencySanitizer, "esan",
202 "EfficiencySanitizer: finds performance issues.", false, false)
203
204const char *EfficiencySanitizer::getPassName() const {
205 return "EfficiencySanitizer";
206}
207
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000208ModulePass *
Derek Brueningd862c172016-04-21 21:30:22 +0000209llvm::createEfficiencySanitizerPass(const EfficiencySanitizerOptions &Options) {
210 return new EfficiencySanitizer(Options);
211}
212
213void EfficiencySanitizer::initializeCallbacks(Module &M) {
214 IRBuilder<> IRB(M.getContext());
215 // Initialize the callbacks.
216 for (size_t Idx = 0; Idx < NumberOfAccessSizes; ++Idx) {
217 const unsigned ByteSize = 1U << Idx;
218 std::string ByteSizeStr = utostr(ByteSize);
219 // We'll inline the most common (i.e., aligned and frequent sizes)
220 // load + store instrumentation: these callouts are for the slowpath.
221 SmallString<32> AlignedLoadName("__esan_aligned_load" + ByteSizeStr);
222 EsanAlignedLoad[Idx] =
223 checkSanitizerInterfaceFunction(M.getOrInsertFunction(
224 AlignedLoadName, IRB.getVoidTy(), IRB.getInt8PtrTy(), nullptr));
225 SmallString<32> AlignedStoreName("__esan_aligned_store" + ByteSizeStr);
226 EsanAlignedStore[Idx] =
227 checkSanitizerInterfaceFunction(M.getOrInsertFunction(
228 AlignedStoreName, IRB.getVoidTy(), IRB.getInt8PtrTy(), nullptr));
229 SmallString<32> UnalignedLoadName("__esan_unaligned_load" + ByteSizeStr);
230 EsanUnalignedLoad[Idx] =
231 checkSanitizerInterfaceFunction(M.getOrInsertFunction(
232 UnalignedLoadName, IRB.getVoidTy(), IRB.getInt8PtrTy(), nullptr));
233 SmallString<32> UnalignedStoreName("__esan_unaligned_store" + ByteSizeStr);
234 EsanUnalignedStore[Idx] =
235 checkSanitizerInterfaceFunction(M.getOrInsertFunction(
236 UnalignedStoreName, IRB.getVoidTy(), IRB.getInt8PtrTy(), nullptr));
237 }
238 EsanUnalignedLoadN = checkSanitizerInterfaceFunction(
239 M.getOrInsertFunction("__esan_unaligned_loadN", IRB.getVoidTy(),
240 IRB.getInt8PtrTy(), IntptrTy, nullptr));
241 EsanUnalignedStoreN = checkSanitizerInterfaceFunction(
242 M.getOrInsertFunction("__esan_unaligned_storeN", IRB.getVoidTy(),
243 IRB.getInt8PtrTy(), IntptrTy, nullptr));
244 MemmoveFn = checkSanitizerInterfaceFunction(
245 M.getOrInsertFunction("memmove", IRB.getInt8PtrTy(), IRB.getInt8PtrTy(),
246 IRB.getInt8PtrTy(), IntptrTy, nullptr));
247 MemcpyFn = checkSanitizerInterfaceFunction(
248 M.getOrInsertFunction("memcpy", IRB.getInt8PtrTy(), IRB.getInt8PtrTy(),
249 IRB.getInt8PtrTy(), IntptrTy, nullptr));
250 MemsetFn = checkSanitizerInterfaceFunction(
251 M.getOrInsertFunction("memset", IRB.getInt8PtrTy(), IRB.getInt8PtrTy(),
252 IRB.getInt32Ty(), IntptrTy, nullptr));
253}
254
Qin Zhao6d3bd682016-06-02 17:30:47 +0000255bool EfficiencySanitizer::shouldIgnoreStructType(StructType *StructTy) {
256 if (StructTy == nullptr || StructTy->isOpaque() /* no struct body */)
257 return true;
258 return false;
259}
260
261void EfficiencySanitizer::createStructCounterName(
262 StructType *StructTy, SmallString<MaxStructCounterNameSize> &NameStr) {
263 // Append NumFields and field type ids to avoid struct conflicts
264 // with the same name but different fields.
265 if (StructTy->hasName())
266 NameStr += StructTy->getName();
267 else
268 NameStr += "struct.anon";
269 // We allow the actual size of the StructCounterName to be larger than
270 // MaxStructCounterNameSize and append #NumFields and at least one
271 // field type id.
272 // Append #NumFields.
273 NameStr += "#";
274 Twine(StructTy->getNumElements()).toVector(NameStr);
275 // Append struct field type ids in the reverse order.
276 for (int i = StructTy->getNumElements() - 1; i >= 0; --i) {
277 NameStr += "#";
278 Twine(StructTy->getElementType(i)->getTypeID()).toVector(NameStr);
279 if (NameStr.size() >= MaxStructCounterNameSize)
280 break;
281 }
282 if (StructTy->isLiteral()) {
283 // End with # for literal struct.
284 NameStr += "#";
285 }
286}
287
Qin Zhao1762eef2016-05-31 17:14:02 +0000288// Create the global variable for the cache-fragmentation tool.
289GlobalVariable *EfficiencySanitizer::createCacheFragInfoGV(
290 Module &M, Constant *UnitName) {
291 assert(Options.ToolType == EfficiencySanitizerOptions::ESAN_CacheFrag);
292
293 auto *Int8PtrTy = Type::getInt8PtrTy(*Ctx);
294 auto *Int8PtrPtrTy = Int8PtrTy->getPointerTo();
295 auto *Int32Ty = Type::getInt32Ty(*Ctx);
Qin Zhao6d3bd682016-06-02 17:30:47 +0000296 auto *Int64Ty = Type::getInt64Ty(*Ctx);
Qin Zhao1762eef2016-05-31 17:14:02 +0000297 auto *Int64PtrTy = Type::getInt64PtrTy(*Ctx);
298 // This structure should be kept consistent with the StructInfo struct
299 // in the runtime library.
300 // struct StructInfo {
301 // const char *StructName;
Qin Zhao6d3bd682016-06-02 17:30:47 +0000302 // u32 NumFields;
Qin Zhao1762eef2016-05-31 17:14:02 +0000303 // u64 *FieldCounters;
304 // const char **FieldTypeNames;
305 // };
306 auto *StructInfoTy =
307 StructType::get(Int8PtrTy, Int32Ty, Int64PtrTy, Int8PtrPtrTy, nullptr);
308 auto *StructInfoPtrTy = StructInfoTy->getPointerTo();
309 // This structure should be kept consistent with the CacheFragInfo struct
310 // in the runtime library.
311 // struct CacheFragInfo {
312 // const char *UnitName;
Qin Zhao6d3bd682016-06-02 17:30:47 +0000313 // u32 NumStructs;
Qin Zhao1762eef2016-05-31 17:14:02 +0000314 // StructInfo *Structs;
315 // };
316 auto *CacheFragInfoTy =
317 StructType::get(Int8PtrTy, Int32Ty, StructInfoPtrTy, nullptr);
318
319 std::vector<StructType *> Vec = M.getIdentifiedStructTypes();
Qin Zhao6d3bd682016-06-02 17:30:47 +0000320 unsigned NumStructs = 0;
321 SmallVector<Constant *, 16> Initializers;
322
323 for (auto &StructTy : Vec) {
324 if (shouldIgnoreStructType(StructTy)) {
325 ++NumIgnoredStructs;
326 continue;
327 }
328 ++NumStructs;
329
330 // StructName.
331 SmallString<MaxStructCounterNameSize> CounterNameStr;
332 createStructCounterName(StructTy, CounterNameStr);
333 GlobalVariable *StructCounterName = createPrivateGlobalForString(
334 M, CounterNameStr, /*AllowMerging*/true);
335
336 // FieldCounters.
337 // We create the counter array with StructCounterName and weak linkage
338 // so that the structs with the same name and layout from different
339 // compilation units will be merged into one.
340 auto *CounterArrayTy = ArrayType::get(Int64Ty, StructTy->getNumElements());
341 GlobalVariable *Counters =
342 new GlobalVariable(M, CounterArrayTy, false,
343 GlobalVariable::WeakAnyLinkage,
344 ConstantAggregateZero::get(CounterArrayTy),
345 CounterNameStr);
346
Qin Zhaoc14c2492016-06-03 02:33:04 +0000347 // Remember the counter variable for each struct type.
348 StructTyMap.insert(std::pair<Type *, GlobalVariable *>(StructTy, Counters));
349
Qin Zhao6d3bd682016-06-02 17:30:47 +0000350 // FieldTypeNames.
351 // We pass the field type name array to the runtime for better reporting.
352 auto *TypeNameArrayTy = ArrayType::get(Int8PtrTy, StructTy->getNumElements());
353 GlobalVariable *TypeName =
354 new GlobalVariable(M, TypeNameArrayTy, true,
355 GlobalVariable::InternalLinkage, nullptr);
356 SmallVector<Constant *, 16> TypeNameVec;
357 for (unsigned i = 0; i < StructTy->getNumElements(); ++i) {
358 Type *Ty = StructTy->getElementType(i);
359 std::string Str;
360 raw_string_ostream StrOS(Str);
361 Ty->print(StrOS);
362 TypeNameVec.push_back(
363 ConstantExpr::getPointerCast(
364 createPrivateGlobalForString(M, StrOS.str(), true),
365 Int8PtrTy));
366 }
367 TypeName->setInitializer(ConstantArray::get(TypeNameArrayTy, TypeNameVec));
368
369 Initializers.push_back(
370 ConstantStruct::get(
371 StructInfoTy,
372 ConstantExpr::getPointerCast(StructCounterName, Int8PtrTy),
373 ConstantInt::get(Int32Ty, StructTy->getNumElements()),
374 ConstantExpr::getPointerCast(Counters, Int64PtrTy),
375 ConstantExpr::getPointerCast(TypeName, Int8PtrPtrTy),
376 nullptr));
377 }
378 // Structs.
379 Constant *StructInfo;
380 if (NumStructs == 0) {
381 StructInfo = ConstantPointerNull::get(StructInfoPtrTy);
382 } else {
383 auto *StructInfoArrayTy = ArrayType::get(StructInfoTy, NumStructs);
384 StructInfo = ConstantExpr::getPointerCast(
385 new GlobalVariable(M, StructInfoArrayTy, false,
386 GlobalVariable::InternalLinkage,
387 ConstantArray::get(StructInfoArrayTy, Initializers)),
388 StructInfoPtrTy);
389 }
390
Qin Zhao1762eef2016-05-31 17:14:02 +0000391 auto *CacheFragInfoGV = new GlobalVariable(
392 M, CacheFragInfoTy, true, GlobalVariable::InternalLinkage,
393 ConstantStruct::get(CacheFragInfoTy,
394 UnitName,
Qin Zhao6d3bd682016-06-02 17:30:47 +0000395 ConstantInt::get(Int32Ty, NumStructs),
396 StructInfo,
Qin Zhao1762eef2016-05-31 17:14:02 +0000397 nullptr));
398 return CacheFragInfoGV;
Derek Bruening0b872d92016-05-24 22:48:24 +0000399}
400
Qin Zhao1762eef2016-05-31 17:14:02 +0000401// Create the tool-specific argument passed to EsanInit and EsanExit.
402Constant *EfficiencySanitizer::createEsanInitToolInfoArg(Module &M) {
403 // This structure contains tool-specific information about each compilation
404 // unit (module) and is passed to the runtime library.
405 GlobalVariable *ToolInfoGV = nullptr;
406
407 auto *Int8PtrTy = Type::getInt8PtrTy(*Ctx);
408 // Compilation unit name.
409 auto *UnitName = ConstantExpr::getPointerCast(
410 createPrivateGlobalForString(M, M.getModuleIdentifier(), true),
411 Int8PtrTy);
412
413 // Create the tool-specific variable.
414 if (Options.ToolType == EfficiencySanitizerOptions::ESAN_CacheFrag)
415 ToolInfoGV = createCacheFragInfoGV(M, UnitName);
416
417 if (ToolInfoGV != nullptr)
418 return ConstantExpr::getPointerCast(ToolInfoGV, Int8PtrTy);
419
420 // Create the null pointer if no tool-specific variable created.
421 return ConstantPointerNull::get(Int8PtrTy);
422}
423
424void EfficiencySanitizer::createDestructor(Module &M, Constant *ToolInfoArg) {
425 PointerType *Int8PtrTy = Type::getInt8PtrTy(*Ctx);
Derek Bruening0b872d92016-05-24 22:48:24 +0000426 EsanDtorFunction = Function::Create(FunctionType::get(Type::getVoidTy(*Ctx),
427 false),
428 GlobalValue::InternalLinkage,
429 EsanModuleDtorName, &M);
430 ReturnInst::Create(*Ctx, BasicBlock::Create(*Ctx, "", EsanDtorFunction));
431 IRBuilder<> IRB_Dtor(EsanDtorFunction->getEntryBlock().getTerminator());
432 Function *EsanExit = checkSanitizerInterfaceFunction(
433 M.getOrInsertFunction(EsanExitName, IRB_Dtor.getVoidTy(),
Qin Zhao1762eef2016-05-31 17:14:02 +0000434 Int8PtrTy, nullptr));
Derek Bruening0b872d92016-05-24 22:48:24 +0000435 EsanExit->setLinkage(Function::ExternalLinkage);
Qin Zhao1762eef2016-05-31 17:14:02 +0000436 IRB_Dtor.CreateCall(EsanExit, {ToolInfoArg});
Derek Bruening0b872d92016-05-24 22:48:24 +0000437 appendToGlobalDtors(M, EsanDtorFunction, EsanCtorAndDtorPriority);
438}
439
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000440bool EfficiencySanitizer::initOnModule(Module &M) {
Derek Brueningd862c172016-04-21 21:30:22 +0000441 Ctx = &M.getContext();
442 const DataLayout &DL = M.getDataLayout();
443 IRBuilder<> IRB(M.getContext());
444 IntegerType *OrdTy = IRB.getInt32Ty();
Qin Zhao1762eef2016-05-31 17:14:02 +0000445 PointerType *Int8PtrTy = Type::getInt8PtrTy(*Ctx);
Derek Brueningd862c172016-04-21 21:30:22 +0000446 IntptrTy = DL.getIntPtrType(M.getContext());
Derek Bruening0b872d92016-05-24 22:48:24 +0000447 // Create the variable passed to EsanInit and EsanExit.
Qin Zhao1762eef2016-05-31 17:14:02 +0000448 Constant *ToolInfoArg = createEsanInitToolInfoArg(M);
Derek Bruening0b872d92016-05-24 22:48:24 +0000449 // Constructor
Derek Bruening4252a162016-06-03 19:40:37 +0000450 // We specify the tool type both in the EsanWhichToolName global
451 // and as an arg to the init routine as a sanity check.
Derek Brueningd862c172016-04-21 21:30:22 +0000452 std::tie(EsanCtorFunction, std::ignore) = createSanitizerCtorAndInitFunctions(
Qin Zhao1762eef2016-05-31 17:14:02 +0000453 M, EsanModuleCtorName, EsanInitName, /*InitArgTypes=*/{OrdTy, Int8PtrTy},
Derek Brueningd862c172016-04-21 21:30:22 +0000454 /*InitArgs=*/{
Derek Bruening0b872d92016-05-24 22:48:24 +0000455 ConstantInt::get(OrdTy, static_cast<int>(Options.ToolType)),
Qin Zhao1762eef2016-05-31 17:14:02 +0000456 ToolInfoArg});
Derek Bruening0b872d92016-05-24 22:48:24 +0000457 appendToGlobalCtors(M, EsanCtorFunction, EsanCtorAndDtorPriority);
Derek Brueningd862c172016-04-21 21:30:22 +0000458
Qin Zhao1762eef2016-05-31 17:14:02 +0000459 createDestructor(M, ToolInfoArg);
Derek Bruening4252a162016-06-03 19:40:37 +0000460
461 new GlobalVariable(M, OrdTy, true,
462 GlobalValue::WeakAnyLinkage,
463 ConstantInt::get(OrdTy,
464 static_cast<int>(Options.ToolType)),
465 EsanWhichToolName);
466
Derek Brueningd862c172016-04-21 21:30:22 +0000467 return true;
468}
469
Derek Bruening5662b932016-05-25 00:17:24 +0000470Value *EfficiencySanitizer::appToShadow(Value *Shadow, IRBuilder<> &IRB) {
471 // Shadow = ((App & Mask) + Offs) >> Scale
472 Shadow = IRB.CreateAnd(Shadow, ConstantInt::get(IntptrTy, ShadowMask));
473 uint64_t Offs;
474 int Scale = ShadowScale[Options.ToolType];
475 if (Scale <= 2)
476 Offs = ShadowOffs[Scale];
477 else
478 Offs = ShadowOffs[0] << Scale;
479 Shadow = IRB.CreateAdd(Shadow, ConstantInt::get(IntptrTy, Offs));
480 if (Scale > 0)
481 Shadow = IRB.CreateLShr(Shadow, Scale);
482 return Shadow;
483}
484
Derek Brueningd862c172016-04-21 21:30:22 +0000485bool EfficiencySanitizer::shouldIgnoreMemoryAccess(Instruction *I) {
486 if (Options.ToolType == EfficiencySanitizerOptions::ESAN_CacheFrag) {
487 // We'd like to know about cache fragmentation in vtable accesses and
488 // constant data references, so we do not currently ignore anything.
489 return false;
Derek Bruening5662b932016-05-25 00:17:24 +0000490 } else if (Options.ToolType == EfficiencySanitizerOptions::ESAN_WorkingSet) {
491 // TODO: the instrumentation disturbs the data layout on the stack, so we
492 // may want to add an option to ignore stack references (if we can
493 // distinguish them) to reduce overhead.
Derek Brueningd862c172016-04-21 21:30:22 +0000494 }
495 // TODO(bruening): future tools will be returning true for some cases.
496 return false;
497}
498
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000499bool EfficiencySanitizer::runOnModule(Module &M) {
500 bool Res = initOnModule(M);
501 initializeCallbacks(M);
502 for (auto &F : M) {
503 Res |= runOnFunction(F, M);
504 }
505 return Res;
506}
507
508bool EfficiencySanitizer::runOnFunction(Function &F, Module &M) {
Derek Brueningd862c172016-04-21 21:30:22 +0000509 // This is required to prevent instrumenting the call to __esan_init from
510 // within the module constructor.
511 if (&F == EsanCtorFunction)
512 return false;
Derek Brueningd862c172016-04-21 21:30:22 +0000513 SmallVector<Instruction *, 8> LoadsAndStores;
514 SmallVector<Instruction *, 8> MemIntrinCalls;
Qin Zhaoc14c2492016-06-03 02:33:04 +0000515 SmallVector<Instruction *, 8> GetElementPtrs;
Derek Brueningd862c172016-04-21 21:30:22 +0000516 bool Res = false;
Derek Bruening0b872d92016-05-24 22:48:24 +0000517 const DataLayout &DL = M.getDataLayout();
Derek Brueningd862c172016-04-21 21:30:22 +0000518
519 for (auto &BB : F) {
520 for (auto &Inst : BB) {
521 if ((isa<LoadInst>(Inst) || isa<StoreInst>(Inst) ||
522 isa<AtomicRMWInst>(Inst) || isa<AtomicCmpXchgInst>(Inst)) &&
523 !shouldIgnoreMemoryAccess(&Inst))
524 LoadsAndStores.push_back(&Inst);
525 else if (isa<MemIntrinsic>(Inst))
526 MemIntrinCalls.push_back(&Inst);
Qin Zhaoc14c2492016-06-03 02:33:04 +0000527 else if (isa<GetElementPtrInst>(Inst))
528 GetElementPtrs.push_back(&Inst);
Derek Brueningd862c172016-04-21 21:30:22 +0000529 }
530 }
531
532 if (ClInstrumentLoadsAndStores) {
533 for (auto Inst : LoadsAndStores) {
534 Res |= instrumentLoadOrStore(Inst, DL);
535 }
536 }
537
538 if (ClInstrumentMemIntrinsics) {
539 for (auto Inst : MemIntrinCalls) {
540 Res |= instrumentMemIntrinsic(cast<MemIntrinsic>(Inst));
541 }
542 }
543
Qin Zhaoc14c2492016-06-03 02:33:04 +0000544 if (Options.ToolType == EfficiencySanitizerOptions::ESAN_CacheFrag) {
545 for (auto Inst : GetElementPtrs) {
546 Res |= instrumentGetElementPtr(Inst, M);
547 }
548 }
549
Derek Brueningd862c172016-04-21 21:30:22 +0000550 return Res;
551}
552
553bool EfficiencySanitizer::instrumentLoadOrStore(Instruction *I,
554 const DataLayout &DL) {
555 IRBuilder<> IRB(I);
556 bool IsStore;
557 Value *Addr;
558 unsigned Alignment;
559 if (LoadInst *Load = dyn_cast<LoadInst>(I)) {
560 IsStore = false;
561 Alignment = Load->getAlignment();
562 Addr = Load->getPointerOperand();
563 } else if (StoreInst *Store = dyn_cast<StoreInst>(I)) {
564 IsStore = true;
565 Alignment = Store->getAlignment();
566 Addr = Store->getPointerOperand();
567 } else if (AtomicRMWInst *RMW = dyn_cast<AtomicRMWInst>(I)) {
568 IsStore = true;
569 Alignment = 0;
570 Addr = RMW->getPointerOperand();
571 } else if (AtomicCmpXchgInst *Xchg = dyn_cast<AtomicCmpXchgInst>(I)) {
572 IsStore = true;
573 Alignment = 0;
574 Addr = Xchg->getPointerOperand();
575 } else
576 llvm_unreachable("Unsupported mem access type");
577
578 Type *OrigTy = cast<PointerType>(Addr->getType())->getElementType();
579 const uint32_t TypeSizeBytes = DL.getTypeStoreSizeInBits(OrigTy) / 8;
580 Value *OnAccessFunc = nullptr;
Derek Bruening5662b932016-05-25 00:17:24 +0000581
582 // Convert 0 to the default alignment.
583 if (Alignment == 0)
584 Alignment = DL.getPrefTypeAlignment(OrigTy);
585
Derek Brueningd862c172016-04-21 21:30:22 +0000586 if (IsStore)
587 NumInstrumentedStores++;
588 else
589 NumInstrumentedLoads++;
590 int Idx = getMemoryAccessFuncIndex(Addr, DL);
591 if (Idx < 0) {
592 OnAccessFunc = IsStore ? EsanUnalignedStoreN : EsanUnalignedLoadN;
593 IRB.CreateCall(OnAccessFunc,
594 {IRB.CreatePointerCast(Addr, IRB.getInt8PtrTy()),
595 ConstantInt::get(IntptrTy, TypeSizeBytes)});
596 } else {
Qin Zhaod677d882016-06-10 00:48:53 +0000597 if (ClInstrumentFastpath &&
598 instrumentFastpath(I, DL, IsStore, Addr, Alignment)) {
Derek Brueningd862c172016-04-21 21:30:22 +0000599 NumFastpaths++;
600 return true;
601 }
602 if (Alignment == 0 || Alignment >= 8 || (Alignment % TypeSizeBytes) == 0)
603 OnAccessFunc = IsStore ? EsanAlignedStore[Idx] : EsanAlignedLoad[Idx];
604 else
605 OnAccessFunc = IsStore ? EsanUnalignedStore[Idx] : EsanUnalignedLoad[Idx];
606 IRB.CreateCall(OnAccessFunc,
607 IRB.CreatePointerCast(Addr, IRB.getInt8PtrTy()));
608 }
609 return true;
610}
611
612// It's simplest to replace the memset/memmove/memcpy intrinsics with
613// calls that the runtime library intercepts.
614// Our pass is late enough that calls should not turn back into intrinsics.
615bool EfficiencySanitizer::instrumentMemIntrinsic(MemIntrinsic *MI) {
616 IRBuilder<> IRB(MI);
617 bool Res = false;
618 if (isa<MemSetInst>(MI)) {
619 IRB.CreateCall(
620 MemsetFn,
621 {IRB.CreatePointerCast(MI->getArgOperand(0), IRB.getInt8PtrTy()),
622 IRB.CreateIntCast(MI->getArgOperand(1), IRB.getInt32Ty(), false),
623 IRB.CreateIntCast(MI->getArgOperand(2), IntptrTy, false)});
624 MI->eraseFromParent();
625 Res = true;
626 } else if (isa<MemTransferInst>(MI)) {
627 IRB.CreateCall(
628 isa<MemCpyInst>(MI) ? MemcpyFn : MemmoveFn,
629 {IRB.CreatePointerCast(MI->getArgOperand(0), IRB.getInt8PtrTy()),
630 IRB.CreatePointerCast(MI->getArgOperand(1), IRB.getInt8PtrTy()),
631 IRB.CreateIntCast(MI->getArgOperand(2), IntptrTy, false)});
632 MI->eraseFromParent();
633 Res = true;
634 } else
635 llvm_unreachable("Unsupported mem intrinsic type");
636 return Res;
637}
638
Qin Zhaoc14c2492016-06-03 02:33:04 +0000639bool EfficiencySanitizer::instrumentGetElementPtr(Instruction *I, Module &M) {
640 GetElementPtrInst *GepInst = dyn_cast<GetElementPtrInst>(I);
641 if (GepInst == nullptr || !isa<StructType>(GepInst->getSourceElementType()) ||
642 StructTyMap.count(GepInst->getSourceElementType()) == 0 ||
643 !GepInst->hasAllConstantIndices() ||
644 // Only handle simple struct field GEP.
645 GepInst->getNumIndices() != 2) {
646 ++NumIgnoredGEPs;
647 return false;
648 }
649 StructType *StructTy = dyn_cast<StructType>(GepInst->getSourceElementType());
650 if (shouldIgnoreStructType(StructTy)) {
651 ++NumIgnoredGEPs;
652 return false;
653 }
654 ++NumInstrumentedGEPs;
655 // Use the last index as the index within the struct.
656 ConstantInt *Idx = dyn_cast<ConstantInt>(GepInst->getOperand(2));
657 if (Idx == nullptr || Idx->getZExtValue() > StructTy->getNumElements())
658 return false;
659
660 GlobalVariable *CounterArray = StructTyMap[StructTy];
661 if (CounterArray == nullptr)
662 return false;
663 IRBuilder<> IRB(I);
664 Constant *Indices[2];
665 // Xref http://llvm.org/docs/LangRef.html#i-getelementptr and
666 // http://llvm.org/docs/GetElementPtr.html.
667 // The first index of the GEP instruction steps through the first operand,
668 // i.e., the array itself.
669 Indices[0] = ConstantInt::get(IRB.getInt32Ty(), 0);
670 // The second index is the index within the array.
671 Indices[1] = ConstantInt::get(IRB.getInt32Ty(), Idx->getZExtValue());
672 Constant *Counter =
673 ConstantExpr::getGetElementPtr(ArrayType::get(IRB.getInt64Ty(),
674 StructTy->getNumElements()),
675 CounterArray, Indices);
676 Value *Load = IRB.CreateLoad(Counter);
677 IRB.CreateStore(IRB.CreateAdd(Load, ConstantInt::get(IRB.getInt64Ty(), 1)),
678 Counter);
679 return true;
680}
681
Derek Brueningd862c172016-04-21 21:30:22 +0000682int EfficiencySanitizer::getMemoryAccessFuncIndex(Value *Addr,
683 const DataLayout &DL) {
684 Type *OrigPtrTy = Addr->getType();
685 Type *OrigTy = cast<PointerType>(OrigPtrTy)->getElementType();
686 assert(OrigTy->isSized());
687 // The size is always a multiple of 8.
688 uint32_t TypeSizeBytes = DL.getTypeStoreSizeInBits(OrigTy) / 8;
689 if (TypeSizeBytes != 1 && TypeSizeBytes != 2 && TypeSizeBytes != 4 &&
690 TypeSizeBytes != 8 && TypeSizeBytes != 16) {
691 // Irregular sizes do not have per-size call targets.
692 NumAccessesWithIrregularSize++;
693 return -1;
694 }
695 size_t Idx = countTrailingZeros(TypeSizeBytes);
696 assert(Idx < NumberOfAccessSizes);
697 return Idx;
698}
699
700bool EfficiencySanitizer::instrumentFastpath(Instruction *I,
701 const DataLayout &DL, bool IsStore,
702 Value *Addr, unsigned Alignment) {
703 if (Options.ToolType == EfficiencySanitizerOptions::ESAN_CacheFrag) {
704 return instrumentFastpathCacheFrag(I, DL, Addr, Alignment);
Derek Bruening5662b932016-05-25 00:17:24 +0000705 } else if (Options.ToolType == EfficiencySanitizerOptions::ESAN_WorkingSet) {
706 return instrumentFastpathWorkingSet(I, DL, Addr, Alignment);
Derek Brueningd862c172016-04-21 21:30:22 +0000707 }
708 return false;
709}
710
711bool EfficiencySanitizer::instrumentFastpathCacheFrag(Instruction *I,
712 const DataLayout &DL,
713 Value *Addr,
714 unsigned Alignment) {
Qin Zhaod677d882016-06-10 00:48:53 +0000715 // Do nothing.
716 return true; // Return true to avoid slowpath instrumentation.
Derek Brueningd862c172016-04-21 21:30:22 +0000717}
Derek Bruening5662b932016-05-25 00:17:24 +0000718
719bool EfficiencySanitizer::instrumentFastpathWorkingSet(
720 Instruction *I, const DataLayout &DL, Value *Addr, unsigned Alignment) {
721 assert(ShadowScale[Options.ToolType] == 6); // The code below assumes this
722 IRBuilder<> IRB(I);
723 Type *OrigTy = cast<PointerType>(Addr->getType())->getElementType();
724 const uint32_t TypeSize = DL.getTypeStoreSizeInBits(OrigTy);
725 // Bail to the slowpath if the access might touch multiple cache lines.
726 // An access aligned to its size is guaranteed to be intra-cache-line.
727 // getMemoryAccessFuncIndex has already ruled out a size larger than 16
728 // and thus larger than a cache line for platforms this tool targets
729 // (and our shadow memory setup assumes 64-byte cache lines).
730 assert(TypeSize <= 64);
731 if (!(TypeSize == 8 ||
Derek Bruening9ef57722016-06-03 22:29:52 +0000732 (Alignment % (TypeSize / 8)) == 0)) {
733 if (ClAssumeIntraCacheLine)
734 ++NumAssumedIntraCacheLine;
735 else
736 return false;
737 }
Derek Bruening5662b932016-05-25 00:17:24 +0000738
739 // We inline instrumentation to set the corresponding shadow bits for
740 // each cache line touched by the application. Here we handle a single
741 // load or store where we've already ruled out the possibility that it
742 // might touch more than one cache line and thus we simply update the
743 // shadow memory for a single cache line.
744 // Our shadow memory model is fine with races when manipulating shadow values.
745 // We generate the following code:
746 //
747 // const char BitMask = 0x81;
748 // char *ShadowAddr = appToShadow(AppAddr);
749 // if ((*ShadowAddr & BitMask) != BitMask)
750 // *ShadowAddr |= Bitmask;
751 //
752 Value *AddrPtr = IRB.CreatePointerCast(Addr, IntptrTy);
753 Value *ShadowPtr = appToShadow(AddrPtr, IRB);
754 Type *ShadowTy = IntegerType::get(*Ctx, 8U);
755 Type *ShadowPtrTy = PointerType::get(ShadowTy, 0);
756 // The bottom bit is used for the current sampling period's working set.
757 // The top bit is used for the total working set. We set both on each
758 // memory access, if they are not already set.
759 Value *ValueMask = ConstantInt::get(ShadowTy, 0x81); // 10000001B
760
761 Value *OldValue = IRB.CreateLoad(IRB.CreateIntToPtr(ShadowPtr, ShadowPtrTy));
762 // The AND and CMP will be turned into a TEST instruction by the compiler.
763 Value *Cmp = IRB.CreateICmpNE(IRB.CreateAnd(OldValue, ValueMask), ValueMask);
764 TerminatorInst *CmpTerm = SplitBlockAndInsertIfThen(Cmp, I, false);
765 // FIXME: do I need to call SetCurrentDebugLocation?
766 IRB.SetInsertPoint(CmpTerm);
767 // We use OR to set the shadow bits to avoid corrupting the middle 6 bits,
768 // which are used by the runtime library.
769 Value *NewVal = IRB.CreateOr(OldValue, ValueMask);
770 IRB.CreateStore(NewVal, IRB.CreateIntToPtr(ShadowPtr, ShadowPtrTy));
771 IRB.SetInsertPoint(I);
772
773 return true;
774}