blob: 48a9396a9562d9045eacf41c37f2abd3fc464436 [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);
59
Derek Bruening9ef57722016-06-03 22:29:52 +000060// Experiments show that the performance difference can be 2x or more,
61// and accuracy loss is typically negligible, so we turn this on by default.
62static cl::opt<bool> ClAssumeIntraCacheLine(
63 "esan-assume-intra-cache-line", cl::init(true),
64 cl::desc("Assume each memory access touches just one cache line, for "
65 "better performance but with a potential loss of accuracy."),
66 cl::Hidden);
67
Derek Brueningd862c172016-04-21 21:30:22 +000068STATISTIC(NumInstrumentedLoads, "Number of instrumented loads");
69STATISTIC(NumInstrumentedStores, "Number of instrumented stores");
70STATISTIC(NumFastpaths, "Number of instrumented fastpaths");
71STATISTIC(NumAccessesWithIrregularSize,
72 "Number of accesses with a size outside our targeted callout sizes");
Qin Zhao6d3bd682016-06-02 17:30:47 +000073STATISTIC(NumIgnoredStructs, "Number of ignored structs");
Qin Zhaoc14c2492016-06-03 02:33:04 +000074STATISTIC(NumIgnoredGEPs, "Number of ignored GEP instructions");
75STATISTIC(NumInstrumentedGEPs, "Number of instrumented GEP instructions");
Derek Bruening9ef57722016-06-03 22:29:52 +000076STATISTIC(NumAssumedIntraCacheLine,
77 "Number of accesses assumed to be intra-cache-line");
Derek Brueningd862c172016-04-21 21:30:22 +000078
Derek Bruening0b872d92016-05-24 22:48:24 +000079static const uint64_t EsanCtorAndDtorPriority = 0;
Derek Brueningd862c172016-04-21 21:30:22 +000080static const char *const EsanModuleCtorName = "esan.module_ctor";
Derek Bruening0b872d92016-05-24 22:48:24 +000081static const char *const EsanModuleDtorName = "esan.module_dtor";
Derek Brueningd862c172016-04-21 21:30:22 +000082static const char *const EsanInitName = "__esan_init";
Derek Bruening0b872d92016-05-24 22:48:24 +000083static const char *const EsanExitName = "__esan_exit";
Derek Brueningd862c172016-04-21 21:30:22 +000084
Derek Bruening4252a162016-06-03 19:40:37 +000085// We need to specify the tool to the runtime earlier than
86// the ctor is called in some cases, so we set a global variable.
87static const char *const EsanWhichToolName = "__esan_which_tool";
88
Derek Bruening5662b932016-05-25 00:17:24 +000089// We must keep these Shadow* constants consistent with the esan runtime.
90// FIXME: Try to place these shadow constants, the names of the __esan_*
91// interface functions, and the ToolType enum into a header shared between
92// llvm and compiler-rt.
93static const uint64_t ShadowMask = 0x00000fffffffffffull;
94static const uint64_t ShadowOffs[3] = { // Indexed by scale
95 0x0000130000000000ull,
96 0x0000220000000000ull,
97 0x0000440000000000ull,
98};
99// This array is indexed by the ToolType enum.
100static const int ShadowScale[] = {
101 0, // ESAN_None.
102 2, // ESAN_CacheFrag: 4B:1B, so 4 to 1 == >>2.
103 6, // ESAN_WorkingSet: 64B:1B, so 64 to 1 == >>6.
104};
105
Qin Zhao6d3bd682016-06-02 17:30:47 +0000106// MaxStructCounterNameSize is a soft size limit to avoid insanely long
107// names for those extremely large structs.
108static const unsigned MaxStructCounterNameSize = 512;
109
Derek Brueningd862c172016-04-21 21:30:22 +0000110namespace {
111
112static EfficiencySanitizerOptions
113OverrideOptionsFromCL(EfficiencySanitizerOptions Options) {
114 if (ClToolCacheFrag)
115 Options.ToolType = EfficiencySanitizerOptions::ESAN_CacheFrag;
Derek Bruening5662b932016-05-25 00:17:24 +0000116 else if (ClToolWorkingSet)
117 Options.ToolType = EfficiencySanitizerOptions::ESAN_WorkingSet;
Derek Brueningd862c172016-04-21 21:30:22 +0000118
119 // Direct opt invocation with no params will have the default ESAN_None.
120 // We run the default tool in that case.
121 if (Options.ToolType == EfficiencySanitizerOptions::ESAN_None)
122 Options.ToolType = EfficiencySanitizerOptions::ESAN_CacheFrag;
123
124 return Options;
125}
126
Qin Zhao1762eef2016-05-31 17:14:02 +0000127// Create a constant for Str so that we can pass it to the run-time lib.
128static GlobalVariable *createPrivateGlobalForString(Module &M, StringRef Str,
129 bool AllowMerging) {
130 Constant *StrConst = ConstantDataArray::getString(M.getContext(), Str);
131 // We use private linkage for module-local strings. If they can be merged
132 // with another one, we set the unnamed_addr attribute.
133 GlobalVariable *GV =
134 new GlobalVariable(M, StrConst->getType(), true,
135 GlobalValue::PrivateLinkage, StrConst, "");
136 if (AllowMerging)
137 GV->setUnnamedAddr(true);
138 GV->setAlignment(1); // Strings may not be merged w/o setting align 1.
139 return GV;
140}
141
Derek Brueningd862c172016-04-21 21:30:22 +0000142/// EfficiencySanitizer: instrument each module to find performance issues.
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000143class EfficiencySanitizer : public ModulePass {
Derek Brueningd862c172016-04-21 21:30:22 +0000144public:
145 EfficiencySanitizer(
146 const EfficiencySanitizerOptions &Opts = EfficiencySanitizerOptions())
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000147 : ModulePass(ID), Options(OverrideOptionsFromCL(Opts)) {}
Derek Brueningd862c172016-04-21 21:30:22 +0000148 const char *getPassName() const override;
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000149 bool runOnModule(Module &M) override;
Derek Brueningd862c172016-04-21 21:30:22 +0000150 static char ID;
151
152private:
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000153 bool initOnModule(Module &M);
Derek Brueningd862c172016-04-21 21:30:22 +0000154 void initializeCallbacks(Module &M);
Qin Zhao6d3bd682016-06-02 17:30:47 +0000155 bool shouldIgnoreStructType(StructType *StructTy);
156 void createStructCounterName(
157 StructType *StructTy, SmallString<MaxStructCounterNameSize> &NameStr);
Qin Zhao1762eef2016-05-31 17:14:02 +0000158 GlobalVariable *createCacheFragInfoGV(Module &M, Constant *UnitName);
159 Constant *createEsanInitToolInfoArg(Module &M);
160 void createDestructor(Module &M, Constant *ToolInfoArg);
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000161 bool runOnFunction(Function &F, Module &M);
Derek Brueningd862c172016-04-21 21:30:22 +0000162 bool instrumentLoadOrStore(Instruction *I, const DataLayout &DL);
163 bool instrumentMemIntrinsic(MemIntrinsic *MI);
Qin Zhaoc14c2492016-06-03 02:33:04 +0000164 bool instrumentGetElementPtr(Instruction *I, Module &M);
Derek Brueningd862c172016-04-21 21:30:22 +0000165 bool shouldIgnoreMemoryAccess(Instruction *I);
166 int getMemoryAccessFuncIndex(Value *Addr, const DataLayout &DL);
Derek Bruening5662b932016-05-25 00:17:24 +0000167 Value *appToShadow(Value *Shadow, IRBuilder<> &IRB);
Derek Brueningd862c172016-04-21 21:30:22 +0000168 bool instrumentFastpath(Instruction *I, const DataLayout &DL, bool IsStore,
169 Value *Addr, unsigned Alignment);
170 // Each tool has its own fastpath routine:
171 bool instrumentFastpathCacheFrag(Instruction *I, const DataLayout &DL,
172 Value *Addr, unsigned Alignment);
Derek Bruening5662b932016-05-25 00:17:24 +0000173 bool instrumentFastpathWorkingSet(Instruction *I, const DataLayout &DL,
174 Value *Addr, unsigned Alignment);
Derek Brueningd862c172016-04-21 21:30:22 +0000175
176 EfficiencySanitizerOptions Options;
177 LLVMContext *Ctx;
178 Type *IntptrTy;
179 // Our slowpath involves callouts to the runtime library.
180 // Access sizes are powers of two: 1, 2, 4, 8, 16.
181 static const size_t NumberOfAccessSizes = 5;
182 Function *EsanAlignedLoad[NumberOfAccessSizes];
183 Function *EsanAlignedStore[NumberOfAccessSizes];
184 Function *EsanUnalignedLoad[NumberOfAccessSizes];
185 Function *EsanUnalignedStore[NumberOfAccessSizes];
186 // For irregular sizes of any alignment:
187 Function *EsanUnalignedLoadN, *EsanUnalignedStoreN;
188 Function *MemmoveFn, *MemcpyFn, *MemsetFn;
189 Function *EsanCtorFunction;
Derek Bruening0b872d92016-05-24 22:48:24 +0000190 Function *EsanDtorFunction;
Qin Zhaoc14c2492016-06-03 02:33:04 +0000191 // Remember the counter variable for each struct type to avoid
192 // recomputing the variable name later during instrumentation.
193 std::map<Type *, GlobalVariable *> StructTyMap;
Derek Brueningd862c172016-04-21 21:30:22 +0000194};
195} // namespace
196
197char EfficiencySanitizer::ID = 0;
198INITIALIZE_PASS(EfficiencySanitizer, "esan",
199 "EfficiencySanitizer: finds performance issues.", false, false)
200
201const char *EfficiencySanitizer::getPassName() const {
202 return "EfficiencySanitizer";
203}
204
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000205ModulePass *
Derek Brueningd862c172016-04-21 21:30:22 +0000206llvm::createEfficiencySanitizerPass(const EfficiencySanitizerOptions &Options) {
207 return new EfficiencySanitizer(Options);
208}
209
210void EfficiencySanitizer::initializeCallbacks(Module &M) {
211 IRBuilder<> IRB(M.getContext());
212 // Initialize the callbacks.
213 for (size_t Idx = 0; Idx < NumberOfAccessSizes; ++Idx) {
214 const unsigned ByteSize = 1U << Idx;
215 std::string ByteSizeStr = utostr(ByteSize);
216 // We'll inline the most common (i.e., aligned and frequent sizes)
217 // load + store instrumentation: these callouts are for the slowpath.
218 SmallString<32> AlignedLoadName("__esan_aligned_load" + ByteSizeStr);
219 EsanAlignedLoad[Idx] =
220 checkSanitizerInterfaceFunction(M.getOrInsertFunction(
221 AlignedLoadName, IRB.getVoidTy(), IRB.getInt8PtrTy(), nullptr));
222 SmallString<32> AlignedStoreName("__esan_aligned_store" + ByteSizeStr);
223 EsanAlignedStore[Idx] =
224 checkSanitizerInterfaceFunction(M.getOrInsertFunction(
225 AlignedStoreName, IRB.getVoidTy(), IRB.getInt8PtrTy(), nullptr));
226 SmallString<32> UnalignedLoadName("__esan_unaligned_load" + ByteSizeStr);
227 EsanUnalignedLoad[Idx] =
228 checkSanitizerInterfaceFunction(M.getOrInsertFunction(
229 UnalignedLoadName, IRB.getVoidTy(), IRB.getInt8PtrTy(), nullptr));
230 SmallString<32> UnalignedStoreName("__esan_unaligned_store" + ByteSizeStr);
231 EsanUnalignedStore[Idx] =
232 checkSanitizerInterfaceFunction(M.getOrInsertFunction(
233 UnalignedStoreName, IRB.getVoidTy(), IRB.getInt8PtrTy(), nullptr));
234 }
235 EsanUnalignedLoadN = checkSanitizerInterfaceFunction(
236 M.getOrInsertFunction("__esan_unaligned_loadN", IRB.getVoidTy(),
237 IRB.getInt8PtrTy(), IntptrTy, nullptr));
238 EsanUnalignedStoreN = checkSanitizerInterfaceFunction(
239 M.getOrInsertFunction("__esan_unaligned_storeN", IRB.getVoidTy(),
240 IRB.getInt8PtrTy(), IntptrTy, nullptr));
241 MemmoveFn = checkSanitizerInterfaceFunction(
242 M.getOrInsertFunction("memmove", IRB.getInt8PtrTy(), IRB.getInt8PtrTy(),
243 IRB.getInt8PtrTy(), IntptrTy, nullptr));
244 MemcpyFn = checkSanitizerInterfaceFunction(
245 M.getOrInsertFunction("memcpy", IRB.getInt8PtrTy(), IRB.getInt8PtrTy(),
246 IRB.getInt8PtrTy(), IntptrTy, nullptr));
247 MemsetFn = checkSanitizerInterfaceFunction(
248 M.getOrInsertFunction("memset", IRB.getInt8PtrTy(), IRB.getInt8PtrTy(),
249 IRB.getInt32Ty(), IntptrTy, nullptr));
250}
251
Qin Zhao6d3bd682016-06-02 17:30:47 +0000252bool EfficiencySanitizer::shouldIgnoreStructType(StructType *StructTy) {
253 if (StructTy == nullptr || StructTy->isOpaque() /* no struct body */)
254 return true;
255 return false;
256}
257
258void EfficiencySanitizer::createStructCounterName(
259 StructType *StructTy, SmallString<MaxStructCounterNameSize> &NameStr) {
260 // Append NumFields and field type ids to avoid struct conflicts
261 // with the same name but different fields.
262 if (StructTy->hasName())
263 NameStr += StructTy->getName();
264 else
265 NameStr += "struct.anon";
266 // We allow the actual size of the StructCounterName to be larger than
267 // MaxStructCounterNameSize and append #NumFields and at least one
268 // field type id.
269 // Append #NumFields.
270 NameStr += "#";
271 Twine(StructTy->getNumElements()).toVector(NameStr);
272 // Append struct field type ids in the reverse order.
273 for (int i = StructTy->getNumElements() - 1; i >= 0; --i) {
274 NameStr += "#";
275 Twine(StructTy->getElementType(i)->getTypeID()).toVector(NameStr);
276 if (NameStr.size() >= MaxStructCounterNameSize)
277 break;
278 }
279 if (StructTy->isLiteral()) {
280 // End with # for literal struct.
281 NameStr += "#";
282 }
283}
284
Qin Zhao1762eef2016-05-31 17:14:02 +0000285// Create the global variable for the cache-fragmentation tool.
286GlobalVariable *EfficiencySanitizer::createCacheFragInfoGV(
287 Module &M, Constant *UnitName) {
288 assert(Options.ToolType == EfficiencySanitizerOptions::ESAN_CacheFrag);
289
290 auto *Int8PtrTy = Type::getInt8PtrTy(*Ctx);
291 auto *Int8PtrPtrTy = Int8PtrTy->getPointerTo();
292 auto *Int32Ty = Type::getInt32Ty(*Ctx);
Qin Zhao6d3bd682016-06-02 17:30:47 +0000293 auto *Int64Ty = Type::getInt64Ty(*Ctx);
Qin Zhao1762eef2016-05-31 17:14:02 +0000294 auto *Int64PtrTy = Type::getInt64PtrTy(*Ctx);
295 // This structure should be kept consistent with the StructInfo struct
296 // in the runtime library.
297 // struct StructInfo {
298 // const char *StructName;
Qin Zhao6d3bd682016-06-02 17:30:47 +0000299 // u32 NumFields;
Qin Zhao1762eef2016-05-31 17:14:02 +0000300 // u64 *FieldCounters;
301 // const char **FieldTypeNames;
302 // };
303 auto *StructInfoTy =
304 StructType::get(Int8PtrTy, Int32Ty, Int64PtrTy, Int8PtrPtrTy, nullptr);
305 auto *StructInfoPtrTy = StructInfoTy->getPointerTo();
306 // This structure should be kept consistent with the CacheFragInfo struct
307 // in the runtime library.
308 // struct CacheFragInfo {
309 // const char *UnitName;
Qin Zhao6d3bd682016-06-02 17:30:47 +0000310 // u32 NumStructs;
Qin Zhao1762eef2016-05-31 17:14:02 +0000311 // StructInfo *Structs;
312 // };
313 auto *CacheFragInfoTy =
314 StructType::get(Int8PtrTy, Int32Ty, StructInfoPtrTy, nullptr);
315
316 std::vector<StructType *> Vec = M.getIdentifiedStructTypes();
Qin Zhao6d3bd682016-06-02 17:30:47 +0000317 unsigned NumStructs = 0;
318 SmallVector<Constant *, 16> Initializers;
319
320 for (auto &StructTy : Vec) {
321 if (shouldIgnoreStructType(StructTy)) {
322 ++NumIgnoredStructs;
323 continue;
324 }
325 ++NumStructs;
326
327 // StructName.
328 SmallString<MaxStructCounterNameSize> CounterNameStr;
329 createStructCounterName(StructTy, CounterNameStr);
330 GlobalVariable *StructCounterName = createPrivateGlobalForString(
331 M, CounterNameStr, /*AllowMerging*/true);
332
333 // FieldCounters.
334 // We create the counter array with StructCounterName and weak linkage
335 // so that the structs with the same name and layout from different
336 // compilation units will be merged into one.
337 auto *CounterArrayTy = ArrayType::get(Int64Ty, StructTy->getNumElements());
338 GlobalVariable *Counters =
339 new GlobalVariable(M, CounterArrayTy, false,
340 GlobalVariable::WeakAnyLinkage,
341 ConstantAggregateZero::get(CounterArrayTy),
342 CounterNameStr);
343
Qin Zhaoc14c2492016-06-03 02:33:04 +0000344 // Remember the counter variable for each struct type.
345 StructTyMap.insert(std::pair<Type *, GlobalVariable *>(StructTy, Counters));
346
Qin Zhao6d3bd682016-06-02 17:30:47 +0000347 // FieldTypeNames.
348 // We pass the field type name array to the runtime for better reporting.
349 auto *TypeNameArrayTy = ArrayType::get(Int8PtrTy, StructTy->getNumElements());
350 GlobalVariable *TypeName =
351 new GlobalVariable(M, TypeNameArrayTy, true,
352 GlobalVariable::InternalLinkage, nullptr);
353 SmallVector<Constant *, 16> TypeNameVec;
354 for (unsigned i = 0; i < StructTy->getNumElements(); ++i) {
355 Type *Ty = StructTy->getElementType(i);
356 std::string Str;
357 raw_string_ostream StrOS(Str);
358 Ty->print(StrOS);
359 TypeNameVec.push_back(
360 ConstantExpr::getPointerCast(
361 createPrivateGlobalForString(M, StrOS.str(), true),
362 Int8PtrTy));
363 }
364 TypeName->setInitializer(ConstantArray::get(TypeNameArrayTy, TypeNameVec));
365
366 Initializers.push_back(
367 ConstantStruct::get(
368 StructInfoTy,
369 ConstantExpr::getPointerCast(StructCounterName, Int8PtrTy),
370 ConstantInt::get(Int32Ty, StructTy->getNumElements()),
371 ConstantExpr::getPointerCast(Counters, Int64PtrTy),
372 ConstantExpr::getPointerCast(TypeName, Int8PtrPtrTy),
373 nullptr));
374 }
375 // Structs.
376 Constant *StructInfo;
377 if (NumStructs == 0) {
378 StructInfo = ConstantPointerNull::get(StructInfoPtrTy);
379 } else {
380 auto *StructInfoArrayTy = ArrayType::get(StructInfoTy, NumStructs);
381 StructInfo = ConstantExpr::getPointerCast(
382 new GlobalVariable(M, StructInfoArrayTy, false,
383 GlobalVariable::InternalLinkage,
384 ConstantArray::get(StructInfoArrayTy, Initializers)),
385 StructInfoPtrTy);
386 }
387
Qin Zhao1762eef2016-05-31 17:14:02 +0000388 auto *CacheFragInfoGV = new GlobalVariable(
389 M, CacheFragInfoTy, true, GlobalVariable::InternalLinkage,
390 ConstantStruct::get(CacheFragInfoTy,
391 UnitName,
Qin Zhao6d3bd682016-06-02 17:30:47 +0000392 ConstantInt::get(Int32Ty, NumStructs),
393 StructInfo,
Qin Zhao1762eef2016-05-31 17:14:02 +0000394 nullptr));
395 return CacheFragInfoGV;
Derek Bruening0b872d92016-05-24 22:48:24 +0000396}
397
Qin Zhao1762eef2016-05-31 17:14:02 +0000398// Create the tool-specific argument passed to EsanInit and EsanExit.
399Constant *EfficiencySanitizer::createEsanInitToolInfoArg(Module &M) {
400 // This structure contains tool-specific information about each compilation
401 // unit (module) and is passed to the runtime library.
402 GlobalVariable *ToolInfoGV = nullptr;
403
404 auto *Int8PtrTy = Type::getInt8PtrTy(*Ctx);
405 // Compilation unit name.
406 auto *UnitName = ConstantExpr::getPointerCast(
407 createPrivateGlobalForString(M, M.getModuleIdentifier(), true),
408 Int8PtrTy);
409
410 // Create the tool-specific variable.
411 if (Options.ToolType == EfficiencySanitizerOptions::ESAN_CacheFrag)
412 ToolInfoGV = createCacheFragInfoGV(M, UnitName);
413
414 if (ToolInfoGV != nullptr)
415 return ConstantExpr::getPointerCast(ToolInfoGV, Int8PtrTy);
416
417 // Create the null pointer if no tool-specific variable created.
418 return ConstantPointerNull::get(Int8PtrTy);
419}
420
421void EfficiencySanitizer::createDestructor(Module &M, Constant *ToolInfoArg) {
422 PointerType *Int8PtrTy = Type::getInt8PtrTy(*Ctx);
Derek Bruening0b872d92016-05-24 22:48:24 +0000423 EsanDtorFunction = Function::Create(FunctionType::get(Type::getVoidTy(*Ctx),
424 false),
425 GlobalValue::InternalLinkage,
426 EsanModuleDtorName, &M);
427 ReturnInst::Create(*Ctx, BasicBlock::Create(*Ctx, "", EsanDtorFunction));
428 IRBuilder<> IRB_Dtor(EsanDtorFunction->getEntryBlock().getTerminator());
429 Function *EsanExit = checkSanitizerInterfaceFunction(
430 M.getOrInsertFunction(EsanExitName, IRB_Dtor.getVoidTy(),
Qin Zhao1762eef2016-05-31 17:14:02 +0000431 Int8PtrTy, nullptr));
Derek Bruening0b872d92016-05-24 22:48:24 +0000432 EsanExit->setLinkage(Function::ExternalLinkage);
Qin Zhao1762eef2016-05-31 17:14:02 +0000433 IRB_Dtor.CreateCall(EsanExit, {ToolInfoArg});
Derek Bruening0b872d92016-05-24 22:48:24 +0000434 appendToGlobalDtors(M, EsanDtorFunction, EsanCtorAndDtorPriority);
435}
436
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000437bool EfficiencySanitizer::initOnModule(Module &M) {
Derek Brueningd862c172016-04-21 21:30:22 +0000438 Ctx = &M.getContext();
439 const DataLayout &DL = M.getDataLayout();
440 IRBuilder<> IRB(M.getContext());
441 IntegerType *OrdTy = IRB.getInt32Ty();
Qin Zhao1762eef2016-05-31 17:14:02 +0000442 PointerType *Int8PtrTy = Type::getInt8PtrTy(*Ctx);
Derek Brueningd862c172016-04-21 21:30:22 +0000443 IntptrTy = DL.getIntPtrType(M.getContext());
Derek Bruening0b872d92016-05-24 22:48:24 +0000444 // Create the variable passed to EsanInit and EsanExit.
Qin Zhao1762eef2016-05-31 17:14:02 +0000445 Constant *ToolInfoArg = createEsanInitToolInfoArg(M);
Derek Bruening0b872d92016-05-24 22:48:24 +0000446 // Constructor
Derek Bruening4252a162016-06-03 19:40:37 +0000447 // We specify the tool type both in the EsanWhichToolName global
448 // and as an arg to the init routine as a sanity check.
Derek Brueningd862c172016-04-21 21:30:22 +0000449 std::tie(EsanCtorFunction, std::ignore) = createSanitizerCtorAndInitFunctions(
Qin Zhao1762eef2016-05-31 17:14:02 +0000450 M, EsanModuleCtorName, EsanInitName, /*InitArgTypes=*/{OrdTy, Int8PtrTy},
Derek Brueningd862c172016-04-21 21:30:22 +0000451 /*InitArgs=*/{
Derek Bruening0b872d92016-05-24 22:48:24 +0000452 ConstantInt::get(OrdTy, static_cast<int>(Options.ToolType)),
Qin Zhao1762eef2016-05-31 17:14:02 +0000453 ToolInfoArg});
Derek Bruening0b872d92016-05-24 22:48:24 +0000454 appendToGlobalCtors(M, EsanCtorFunction, EsanCtorAndDtorPriority);
Derek Brueningd862c172016-04-21 21:30:22 +0000455
Qin Zhao1762eef2016-05-31 17:14:02 +0000456 createDestructor(M, ToolInfoArg);
Derek Bruening4252a162016-06-03 19:40:37 +0000457
458 new GlobalVariable(M, OrdTy, true,
459 GlobalValue::WeakAnyLinkage,
460 ConstantInt::get(OrdTy,
461 static_cast<int>(Options.ToolType)),
462 EsanWhichToolName);
463
Derek Brueningd862c172016-04-21 21:30:22 +0000464 return true;
465}
466
Derek Bruening5662b932016-05-25 00:17:24 +0000467Value *EfficiencySanitizer::appToShadow(Value *Shadow, IRBuilder<> &IRB) {
468 // Shadow = ((App & Mask) + Offs) >> Scale
469 Shadow = IRB.CreateAnd(Shadow, ConstantInt::get(IntptrTy, ShadowMask));
470 uint64_t Offs;
471 int Scale = ShadowScale[Options.ToolType];
472 if (Scale <= 2)
473 Offs = ShadowOffs[Scale];
474 else
475 Offs = ShadowOffs[0] << Scale;
476 Shadow = IRB.CreateAdd(Shadow, ConstantInt::get(IntptrTy, Offs));
477 if (Scale > 0)
478 Shadow = IRB.CreateLShr(Shadow, Scale);
479 return Shadow;
480}
481
Derek Brueningd862c172016-04-21 21:30:22 +0000482bool EfficiencySanitizer::shouldIgnoreMemoryAccess(Instruction *I) {
483 if (Options.ToolType == EfficiencySanitizerOptions::ESAN_CacheFrag) {
484 // We'd like to know about cache fragmentation in vtable accesses and
485 // constant data references, so we do not currently ignore anything.
486 return false;
Derek Bruening5662b932016-05-25 00:17:24 +0000487 } else if (Options.ToolType == EfficiencySanitizerOptions::ESAN_WorkingSet) {
488 // TODO: the instrumentation disturbs the data layout on the stack, so we
489 // may want to add an option to ignore stack references (if we can
490 // distinguish them) to reduce overhead.
Derek Brueningd862c172016-04-21 21:30:22 +0000491 }
492 // TODO(bruening): future tools will be returning true for some cases.
493 return false;
494}
495
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000496bool EfficiencySanitizer::runOnModule(Module &M) {
497 bool Res = initOnModule(M);
498 initializeCallbacks(M);
499 for (auto &F : M) {
500 Res |= runOnFunction(F, M);
501 }
502 return Res;
503}
504
505bool EfficiencySanitizer::runOnFunction(Function &F, Module &M) {
Derek Brueningd862c172016-04-21 21:30:22 +0000506 // This is required to prevent instrumenting the call to __esan_init from
507 // within the module constructor.
508 if (&F == EsanCtorFunction)
509 return false;
Derek Brueningd862c172016-04-21 21:30:22 +0000510 SmallVector<Instruction *, 8> LoadsAndStores;
511 SmallVector<Instruction *, 8> MemIntrinCalls;
Qin Zhaoc14c2492016-06-03 02:33:04 +0000512 SmallVector<Instruction *, 8> GetElementPtrs;
Derek Brueningd862c172016-04-21 21:30:22 +0000513 bool Res = false;
Derek Bruening0b872d92016-05-24 22:48:24 +0000514 const DataLayout &DL = M.getDataLayout();
Derek Brueningd862c172016-04-21 21:30:22 +0000515
516 for (auto &BB : F) {
517 for (auto &Inst : BB) {
518 if ((isa<LoadInst>(Inst) || isa<StoreInst>(Inst) ||
519 isa<AtomicRMWInst>(Inst) || isa<AtomicCmpXchgInst>(Inst)) &&
520 !shouldIgnoreMemoryAccess(&Inst))
521 LoadsAndStores.push_back(&Inst);
522 else if (isa<MemIntrinsic>(Inst))
523 MemIntrinCalls.push_back(&Inst);
Qin Zhaoc14c2492016-06-03 02:33:04 +0000524 else if (isa<GetElementPtrInst>(Inst))
525 GetElementPtrs.push_back(&Inst);
Derek Brueningd862c172016-04-21 21:30:22 +0000526 }
527 }
528
529 if (ClInstrumentLoadsAndStores) {
530 for (auto Inst : LoadsAndStores) {
531 Res |= instrumentLoadOrStore(Inst, DL);
532 }
533 }
534
535 if (ClInstrumentMemIntrinsics) {
536 for (auto Inst : MemIntrinCalls) {
537 Res |= instrumentMemIntrinsic(cast<MemIntrinsic>(Inst));
538 }
539 }
540
Qin Zhaoc14c2492016-06-03 02:33:04 +0000541 if (Options.ToolType == EfficiencySanitizerOptions::ESAN_CacheFrag) {
542 for (auto Inst : GetElementPtrs) {
543 Res |= instrumentGetElementPtr(Inst, M);
544 }
545 }
546
Derek Brueningd862c172016-04-21 21:30:22 +0000547 return Res;
548}
549
550bool EfficiencySanitizer::instrumentLoadOrStore(Instruction *I,
551 const DataLayout &DL) {
552 IRBuilder<> IRB(I);
553 bool IsStore;
554 Value *Addr;
555 unsigned Alignment;
556 if (LoadInst *Load = dyn_cast<LoadInst>(I)) {
557 IsStore = false;
558 Alignment = Load->getAlignment();
559 Addr = Load->getPointerOperand();
560 } else if (StoreInst *Store = dyn_cast<StoreInst>(I)) {
561 IsStore = true;
562 Alignment = Store->getAlignment();
563 Addr = Store->getPointerOperand();
564 } else if (AtomicRMWInst *RMW = dyn_cast<AtomicRMWInst>(I)) {
565 IsStore = true;
566 Alignment = 0;
567 Addr = RMW->getPointerOperand();
568 } else if (AtomicCmpXchgInst *Xchg = dyn_cast<AtomicCmpXchgInst>(I)) {
569 IsStore = true;
570 Alignment = 0;
571 Addr = Xchg->getPointerOperand();
572 } else
573 llvm_unreachable("Unsupported mem access type");
574
575 Type *OrigTy = cast<PointerType>(Addr->getType())->getElementType();
576 const uint32_t TypeSizeBytes = DL.getTypeStoreSizeInBits(OrigTy) / 8;
577 Value *OnAccessFunc = nullptr;
Derek Bruening5662b932016-05-25 00:17:24 +0000578
579 // Convert 0 to the default alignment.
580 if (Alignment == 0)
581 Alignment = DL.getPrefTypeAlignment(OrigTy);
582
Derek Brueningd862c172016-04-21 21:30:22 +0000583 if (IsStore)
584 NumInstrumentedStores++;
585 else
586 NumInstrumentedLoads++;
587 int Idx = getMemoryAccessFuncIndex(Addr, DL);
588 if (Idx < 0) {
589 OnAccessFunc = IsStore ? EsanUnalignedStoreN : EsanUnalignedLoadN;
590 IRB.CreateCall(OnAccessFunc,
591 {IRB.CreatePointerCast(Addr, IRB.getInt8PtrTy()),
592 ConstantInt::get(IntptrTy, TypeSizeBytes)});
593 } else {
594 if (instrumentFastpath(I, DL, IsStore, Addr, Alignment)) {
595 NumFastpaths++;
596 return true;
597 }
598 if (Alignment == 0 || Alignment >= 8 || (Alignment % TypeSizeBytes) == 0)
599 OnAccessFunc = IsStore ? EsanAlignedStore[Idx] : EsanAlignedLoad[Idx];
600 else
601 OnAccessFunc = IsStore ? EsanUnalignedStore[Idx] : EsanUnalignedLoad[Idx];
602 IRB.CreateCall(OnAccessFunc,
603 IRB.CreatePointerCast(Addr, IRB.getInt8PtrTy()));
604 }
605 return true;
606}
607
608// It's simplest to replace the memset/memmove/memcpy intrinsics with
609// calls that the runtime library intercepts.
610// Our pass is late enough that calls should not turn back into intrinsics.
611bool EfficiencySanitizer::instrumentMemIntrinsic(MemIntrinsic *MI) {
612 IRBuilder<> IRB(MI);
613 bool Res = false;
614 if (isa<MemSetInst>(MI)) {
615 IRB.CreateCall(
616 MemsetFn,
617 {IRB.CreatePointerCast(MI->getArgOperand(0), IRB.getInt8PtrTy()),
618 IRB.CreateIntCast(MI->getArgOperand(1), IRB.getInt32Ty(), false),
619 IRB.CreateIntCast(MI->getArgOperand(2), IntptrTy, false)});
620 MI->eraseFromParent();
621 Res = true;
622 } else if (isa<MemTransferInst>(MI)) {
623 IRB.CreateCall(
624 isa<MemCpyInst>(MI) ? MemcpyFn : MemmoveFn,
625 {IRB.CreatePointerCast(MI->getArgOperand(0), IRB.getInt8PtrTy()),
626 IRB.CreatePointerCast(MI->getArgOperand(1), IRB.getInt8PtrTy()),
627 IRB.CreateIntCast(MI->getArgOperand(2), IntptrTy, false)});
628 MI->eraseFromParent();
629 Res = true;
630 } else
631 llvm_unreachable("Unsupported mem intrinsic type");
632 return Res;
633}
634
Qin Zhaoc14c2492016-06-03 02:33:04 +0000635bool EfficiencySanitizer::instrumentGetElementPtr(Instruction *I, Module &M) {
636 GetElementPtrInst *GepInst = dyn_cast<GetElementPtrInst>(I);
637 if (GepInst == nullptr || !isa<StructType>(GepInst->getSourceElementType()) ||
638 StructTyMap.count(GepInst->getSourceElementType()) == 0 ||
639 !GepInst->hasAllConstantIndices() ||
640 // Only handle simple struct field GEP.
641 GepInst->getNumIndices() != 2) {
642 ++NumIgnoredGEPs;
643 return false;
644 }
645 StructType *StructTy = dyn_cast<StructType>(GepInst->getSourceElementType());
646 if (shouldIgnoreStructType(StructTy)) {
647 ++NumIgnoredGEPs;
648 return false;
649 }
650 ++NumInstrumentedGEPs;
651 // Use the last index as the index within the struct.
652 ConstantInt *Idx = dyn_cast<ConstantInt>(GepInst->getOperand(2));
653 if (Idx == nullptr || Idx->getZExtValue() > StructTy->getNumElements())
654 return false;
655
656 GlobalVariable *CounterArray = StructTyMap[StructTy];
657 if (CounterArray == nullptr)
658 return false;
659 IRBuilder<> IRB(I);
660 Constant *Indices[2];
661 // Xref http://llvm.org/docs/LangRef.html#i-getelementptr and
662 // http://llvm.org/docs/GetElementPtr.html.
663 // The first index of the GEP instruction steps through the first operand,
664 // i.e., the array itself.
665 Indices[0] = ConstantInt::get(IRB.getInt32Ty(), 0);
666 // The second index is the index within the array.
667 Indices[1] = ConstantInt::get(IRB.getInt32Ty(), Idx->getZExtValue());
668 Constant *Counter =
669 ConstantExpr::getGetElementPtr(ArrayType::get(IRB.getInt64Ty(),
670 StructTy->getNumElements()),
671 CounterArray, Indices);
672 Value *Load = IRB.CreateLoad(Counter);
673 IRB.CreateStore(IRB.CreateAdd(Load, ConstantInt::get(IRB.getInt64Ty(), 1)),
674 Counter);
675 return true;
676}
677
Derek Brueningd862c172016-04-21 21:30:22 +0000678int EfficiencySanitizer::getMemoryAccessFuncIndex(Value *Addr,
679 const DataLayout &DL) {
680 Type *OrigPtrTy = Addr->getType();
681 Type *OrigTy = cast<PointerType>(OrigPtrTy)->getElementType();
682 assert(OrigTy->isSized());
683 // The size is always a multiple of 8.
684 uint32_t TypeSizeBytes = DL.getTypeStoreSizeInBits(OrigTy) / 8;
685 if (TypeSizeBytes != 1 && TypeSizeBytes != 2 && TypeSizeBytes != 4 &&
686 TypeSizeBytes != 8 && TypeSizeBytes != 16) {
687 // Irregular sizes do not have per-size call targets.
688 NumAccessesWithIrregularSize++;
689 return -1;
690 }
691 size_t Idx = countTrailingZeros(TypeSizeBytes);
692 assert(Idx < NumberOfAccessSizes);
693 return Idx;
694}
695
696bool EfficiencySanitizer::instrumentFastpath(Instruction *I,
697 const DataLayout &DL, bool IsStore,
698 Value *Addr, unsigned Alignment) {
699 if (Options.ToolType == EfficiencySanitizerOptions::ESAN_CacheFrag) {
700 return instrumentFastpathCacheFrag(I, DL, Addr, Alignment);
Derek Bruening5662b932016-05-25 00:17:24 +0000701 } else if (Options.ToolType == EfficiencySanitizerOptions::ESAN_WorkingSet) {
702 return instrumentFastpathWorkingSet(I, DL, Addr, Alignment);
Derek Brueningd862c172016-04-21 21:30:22 +0000703 }
704 return false;
705}
706
707bool EfficiencySanitizer::instrumentFastpathCacheFrag(Instruction *I,
708 const DataLayout &DL,
709 Value *Addr,
710 unsigned Alignment) {
711 // TODO(bruening): implement a fastpath for aligned accesses
712 return false;
713}
Derek Bruening5662b932016-05-25 00:17:24 +0000714
715bool EfficiencySanitizer::instrumentFastpathWorkingSet(
716 Instruction *I, const DataLayout &DL, Value *Addr, unsigned Alignment) {
717 assert(ShadowScale[Options.ToolType] == 6); // The code below assumes this
718 IRBuilder<> IRB(I);
719 Type *OrigTy = cast<PointerType>(Addr->getType())->getElementType();
720 const uint32_t TypeSize = DL.getTypeStoreSizeInBits(OrigTy);
721 // Bail to the slowpath if the access might touch multiple cache lines.
722 // An access aligned to its size is guaranteed to be intra-cache-line.
723 // getMemoryAccessFuncIndex has already ruled out a size larger than 16
724 // and thus larger than a cache line for platforms this tool targets
725 // (and our shadow memory setup assumes 64-byte cache lines).
726 assert(TypeSize <= 64);
727 if (!(TypeSize == 8 ||
Derek Bruening9ef57722016-06-03 22:29:52 +0000728 (Alignment % (TypeSize / 8)) == 0)) {
729 if (ClAssumeIntraCacheLine)
730 ++NumAssumedIntraCacheLine;
731 else
732 return false;
733 }
Derek Bruening5662b932016-05-25 00:17:24 +0000734
735 // We inline instrumentation to set the corresponding shadow bits for
736 // each cache line touched by the application. Here we handle a single
737 // load or store where we've already ruled out the possibility that it
738 // might touch more than one cache line and thus we simply update the
739 // shadow memory for a single cache line.
740 // Our shadow memory model is fine with races when manipulating shadow values.
741 // We generate the following code:
742 //
743 // const char BitMask = 0x81;
744 // char *ShadowAddr = appToShadow(AppAddr);
745 // if ((*ShadowAddr & BitMask) != BitMask)
746 // *ShadowAddr |= Bitmask;
747 //
748 Value *AddrPtr = IRB.CreatePointerCast(Addr, IntptrTy);
749 Value *ShadowPtr = appToShadow(AddrPtr, IRB);
750 Type *ShadowTy = IntegerType::get(*Ctx, 8U);
751 Type *ShadowPtrTy = PointerType::get(ShadowTy, 0);
752 // The bottom bit is used for the current sampling period's working set.
753 // The top bit is used for the total working set. We set both on each
754 // memory access, if they are not already set.
755 Value *ValueMask = ConstantInt::get(ShadowTy, 0x81); // 10000001B
756
757 Value *OldValue = IRB.CreateLoad(IRB.CreateIntToPtr(ShadowPtr, ShadowPtrTy));
758 // The AND and CMP will be turned into a TEST instruction by the compiler.
759 Value *Cmp = IRB.CreateICmpNE(IRB.CreateAnd(OldValue, ValueMask), ValueMask);
760 TerminatorInst *CmpTerm = SplitBlockAndInsertIfThen(Cmp, I, false);
761 // FIXME: do I need to call SetCurrentDebugLocation?
762 IRB.SetInsertPoint(CmpTerm);
763 // We use OR to set the shadow bits to avoid corrupting the middle 6 bits,
764 // which are used by the runtime library.
765 Value *NewVal = IRB.CreateOr(OldValue, ValueMask);
766 IRB.CreateStore(NewVal, IRB.CreateIntToPtr(ShadowPtr, ShadowPtrTy));
767 IRB.SetInsertPoint(I);
768
769 return true;
770}