blob: d29d400e72ed9b9a0c5c5b7f5405ee7db10e7428 [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
60STATISTIC(NumInstrumentedLoads, "Number of instrumented loads");
61STATISTIC(NumInstrumentedStores, "Number of instrumented stores");
62STATISTIC(NumFastpaths, "Number of instrumented fastpaths");
63STATISTIC(NumAccessesWithIrregularSize,
64 "Number of accesses with a size outside our targeted callout sizes");
Qin Zhao6d3bd682016-06-02 17:30:47 +000065STATISTIC(NumIgnoredStructs, "Number of ignored structs");
Qin Zhaoc14c2492016-06-03 02:33:04 +000066STATISTIC(NumIgnoredGEPs, "Number of ignored GEP instructions");
67STATISTIC(NumInstrumentedGEPs, "Number of instrumented GEP instructions");
Derek Brueningd862c172016-04-21 21:30:22 +000068
Derek Bruening0b872d92016-05-24 22:48:24 +000069static const uint64_t EsanCtorAndDtorPriority = 0;
Derek Brueningd862c172016-04-21 21:30:22 +000070static const char *const EsanModuleCtorName = "esan.module_ctor";
Derek Bruening0b872d92016-05-24 22:48:24 +000071static const char *const EsanModuleDtorName = "esan.module_dtor";
Derek Brueningd862c172016-04-21 21:30:22 +000072static const char *const EsanInitName = "__esan_init";
Derek Bruening0b872d92016-05-24 22:48:24 +000073static const char *const EsanExitName = "__esan_exit";
Derek Brueningd862c172016-04-21 21:30:22 +000074
Derek Bruening5662b932016-05-25 00:17:24 +000075// We must keep these Shadow* constants consistent with the esan runtime.
76// FIXME: Try to place these shadow constants, the names of the __esan_*
77// interface functions, and the ToolType enum into a header shared between
78// llvm and compiler-rt.
79static const uint64_t ShadowMask = 0x00000fffffffffffull;
80static const uint64_t ShadowOffs[3] = { // Indexed by scale
81 0x0000130000000000ull,
82 0x0000220000000000ull,
83 0x0000440000000000ull,
84};
85// This array is indexed by the ToolType enum.
86static const int ShadowScale[] = {
87 0, // ESAN_None.
88 2, // ESAN_CacheFrag: 4B:1B, so 4 to 1 == >>2.
89 6, // ESAN_WorkingSet: 64B:1B, so 64 to 1 == >>6.
90};
91
Qin Zhao6d3bd682016-06-02 17:30:47 +000092// MaxStructCounterNameSize is a soft size limit to avoid insanely long
93// names for those extremely large structs.
94static const unsigned MaxStructCounterNameSize = 512;
95
Derek Brueningd862c172016-04-21 21:30:22 +000096namespace {
97
98static EfficiencySanitizerOptions
99OverrideOptionsFromCL(EfficiencySanitizerOptions Options) {
100 if (ClToolCacheFrag)
101 Options.ToolType = EfficiencySanitizerOptions::ESAN_CacheFrag;
Derek Bruening5662b932016-05-25 00:17:24 +0000102 else if (ClToolWorkingSet)
103 Options.ToolType = EfficiencySanitizerOptions::ESAN_WorkingSet;
Derek Brueningd862c172016-04-21 21:30:22 +0000104
105 // Direct opt invocation with no params will have the default ESAN_None.
106 // We run the default tool in that case.
107 if (Options.ToolType == EfficiencySanitizerOptions::ESAN_None)
108 Options.ToolType = EfficiencySanitizerOptions::ESAN_CacheFrag;
109
110 return Options;
111}
112
Qin Zhao1762eef2016-05-31 17:14:02 +0000113// Create a constant for Str so that we can pass it to the run-time lib.
114static GlobalVariable *createPrivateGlobalForString(Module &M, StringRef Str,
115 bool AllowMerging) {
116 Constant *StrConst = ConstantDataArray::getString(M.getContext(), Str);
117 // We use private linkage for module-local strings. If they can be merged
118 // with another one, we set the unnamed_addr attribute.
119 GlobalVariable *GV =
120 new GlobalVariable(M, StrConst->getType(), true,
121 GlobalValue::PrivateLinkage, StrConst, "");
122 if (AllowMerging)
123 GV->setUnnamedAddr(true);
124 GV->setAlignment(1); // Strings may not be merged w/o setting align 1.
125 return GV;
126}
127
Derek Brueningd862c172016-04-21 21:30:22 +0000128/// EfficiencySanitizer: instrument each module to find performance issues.
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000129class EfficiencySanitizer : public ModulePass {
Derek Brueningd862c172016-04-21 21:30:22 +0000130public:
131 EfficiencySanitizer(
132 const EfficiencySanitizerOptions &Opts = EfficiencySanitizerOptions())
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000133 : ModulePass(ID), Options(OverrideOptionsFromCL(Opts)) {}
Derek Brueningd862c172016-04-21 21:30:22 +0000134 const char *getPassName() const override;
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000135 bool runOnModule(Module &M) override;
Derek Brueningd862c172016-04-21 21:30:22 +0000136 static char ID;
137
138private:
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000139 bool initOnModule(Module &M);
Derek Brueningd862c172016-04-21 21:30:22 +0000140 void initializeCallbacks(Module &M);
Qin Zhao6d3bd682016-06-02 17:30:47 +0000141 bool shouldIgnoreStructType(StructType *StructTy);
142 void createStructCounterName(
143 StructType *StructTy, SmallString<MaxStructCounterNameSize> &NameStr);
Qin Zhao1762eef2016-05-31 17:14:02 +0000144 GlobalVariable *createCacheFragInfoGV(Module &M, Constant *UnitName);
145 Constant *createEsanInitToolInfoArg(Module &M);
146 void createDestructor(Module &M, Constant *ToolInfoArg);
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000147 bool runOnFunction(Function &F, Module &M);
Derek Brueningd862c172016-04-21 21:30:22 +0000148 bool instrumentLoadOrStore(Instruction *I, const DataLayout &DL);
149 bool instrumentMemIntrinsic(MemIntrinsic *MI);
Qin Zhaoc14c2492016-06-03 02:33:04 +0000150 bool instrumentGetElementPtr(Instruction *I, Module &M);
Derek Brueningd862c172016-04-21 21:30:22 +0000151 bool shouldIgnoreMemoryAccess(Instruction *I);
152 int getMemoryAccessFuncIndex(Value *Addr, const DataLayout &DL);
Derek Bruening5662b932016-05-25 00:17:24 +0000153 Value *appToShadow(Value *Shadow, IRBuilder<> &IRB);
Derek Brueningd862c172016-04-21 21:30:22 +0000154 bool instrumentFastpath(Instruction *I, const DataLayout &DL, bool IsStore,
155 Value *Addr, unsigned Alignment);
156 // Each tool has its own fastpath routine:
157 bool instrumentFastpathCacheFrag(Instruction *I, const DataLayout &DL,
158 Value *Addr, unsigned Alignment);
Derek Bruening5662b932016-05-25 00:17:24 +0000159 bool instrumentFastpathWorkingSet(Instruction *I, const DataLayout &DL,
160 Value *Addr, unsigned Alignment);
Derek Brueningd862c172016-04-21 21:30:22 +0000161
162 EfficiencySanitizerOptions Options;
163 LLVMContext *Ctx;
164 Type *IntptrTy;
165 // Our slowpath involves callouts to the runtime library.
166 // Access sizes are powers of two: 1, 2, 4, 8, 16.
167 static const size_t NumberOfAccessSizes = 5;
168 Function *EsanAlignedLoad[NumberOfAccessSizes];
169 Function *EsanAlignedStore[NumberOfAccessSizes];
170 Function *EsanUnalignedLoad[NumberOfAccessSizes];
171 Function *EsanUnalignedStore[NumberOfAccessSizes];
172 // For irregular sizes of any alignment:
173 Function *EsanUnalignedLoadN, *EsanUnalignedStoreN;
174 Function *MemmoveFn, *MemcpyFn, *MemsetFn;
175 Function *EsanCtorFunction;
Derek Bruening0b872d92016-05-24 22:48:24 +0000176 Function *EsanDtorFunction;
Qin Zhaoc14c2492016-06-03 02:33:04 +0000177 // Remember the counter variable for each struct type to avoid
178 // recomputing the variable name later during instrumentation.
179 std::map<Type *, GlobalVariable *> StructTyMap;
Derek Brueningd862c172016-04-21 21:30:22 +0000180};
181} // namespace
182
183char EfficiencySanitizer::ID = 0;
184INITIALIZE_PASS(EfficiencySanitizer, "esan",
185 "EfficiencySanitizer: finds performance issues.", false, false)
186
187const char *EfficiencySanitizer::getPassName() const {
188 return "EfficiencySanitizer";
189}
190
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000191ModulePass *
Derek Brueningd862c172016-04-21 21:30:22 +0000192llvm::createEfficiencySanitizerPass(const EfficiencySanitizerOptions &Options) {
193 return new EfficiencySanitizer(Options);
194}
195
196void EfficiencySanitizer::initializeCallbacks(Module &M) {
197 IRBuilder<> IRB(M.getContext());
198 // Initialize the callbacks.
199 for (size_t Idx = 0; Idx < NumberOfAccessSizes; ++Idx) {
200 const unsigned ByteSize = 1U << Idx;
201 std::string ByteSizeStr = utostr(ByteSize);
202 // We'll inline the most common (i.e., aligned and frequent sizes)
203 // load + store instrumentation: these callouts are for the slowpath.
204 SmallString<32> AlignedLoadName("__esan_aligned_load" + ByteSizeStr);
205 EsanAlignedLoad[Idx] =
206 checkSanitizerInterfaceFunction(M.getOrInsertFunction(
207 AlignedLoadName, IRB.getVoidTy(), IRB.getInt8PtrTy(), nullptr));
208 SmallString<32> AlignedStoreName("__esan_aligned_store" + ByteSizeStr);
209 EsanAlignedStore[Idx] =
210 checkSanitizerInterfaceFunction(M.getOrInsertFunction(
211 AlignedStoreName, IRB.getVoidTy(), IRB.getInt8PtrTy(), nullptr));
212 SmallString<32> UnalignedLoadName("__esan_unaligned_load" + ByteSizeStr);
213 EsanUnalignedLoad[Idx] =
214 checkSanitizerInterfaceFunction(M.getOrInsertFunction(
215 UnalignedLoadName, IRB.getVoidTy(), IRB.getInt8PtrTy(), nullptr));
216 SmallString<32> UnalignedStoreName("__esan_unaligned_store" + ByteSizeStr);
217 EsanUnalignedStore[Idx] =
218 checkSanitizerInterfaceFunction(M.getOrInsertFunction(
219 UnalignedStoreName, IRB.getVoidTy(), IRB.getInt8PtrTy(), nullptr));
220 }
221 EsanUnalignedLoadN = checkSanitizerInterfaceFunction(
222 M.getOrInsertFunction("__esan_unaligned_loadN", IRB.getVoidTy(),
223 IRB.getInt8PtrTy(), IntptrTy, nullptr));
224 EsanUnalignedStoreN = checkSanitizerInterfaceFunction(
225 M.getOrInsertFunction("__esan_unaligned_storeN", IRB.getVoidTy(),
226 IRB.getInt8PtrTy(), IntptrTy, nullptr));
227 MemmoveFn = checkSanitizerInterfaceFunction(
228 M.getOrInsertFunction("memmove", IRB.getInt8PtrTy(), IRB.getInt8PtrTy(),
229 IRB.getInt8PtrTy(), IntptrTy, nullptr));
230 MemcpyFn = checkSanitizerInterfaceFunction(
231 M.getOrInsertFunction("memcpy", IRB.getInt8PtrTy(), IRB.getInt8PtrTy(),
232 IRB.getInt8PtrTy(), IntptrTy, nullptr));
233 MemsetFn = checkSanitizerInterfaceFunction(
234 M.getOrInsertFunction("memset", IRB.getInt8PtrTy(), IRB.getInt8PtrTy(),
235 IRB.getInt32Ty(), IntptrTy, nullptr));
236}
237
Qin Zhao6d3bd682016-06-02 17:30:47 +0000238bool EfficiencySanitizer::shouldIgnoreStructType(StructType *StructTy) {
239 if (StructTy == nullptr || StructTy->isOpaque() /* no struct body */)
240 return true;
241 return false;
242}
243
244void EfficiencySanitizer::createStructCounterName(
245 StructType *StructTy, SmallString<MaxStructCounterNameSize> &NameStr) {
246 // Append NumFields and field type ids to avoid struct conflicts
247 // with the same name but different fields.
248 if (StructTy->hasName())
249 NameStr += StructTy->getName();
250 else
251 NameStr += "struct.anon";
252 // We allow the actual size of the StructCounterName to be larger than
253 // MaxStructCounterNameSize and append #NumFields and at least one
254 // field type id.
255 // Append #NumFields.
256 NameStr += "#";
257 Twine(StructTy->getNumElements()).toVector(NameStr);
258 // Append struct field type ids in the reverse order.
259 for (int i = StructTy->getNumElements() - 1; i >= 0; --i) {
260 NameStr += "#";
261 Twine(StructTy->getElementType(i)->getTypeID()).toVector(NameStr);
262 if (NameStr.size() >= MaxStructCounterNameSize)
263 break;
264 }
265 if (StructTy->isLiteral()) {
266 // End with # for literal struct.
267 NameStr += "#";
268 }
269}
270
Qin Zhao1762eef2016-05-31 17:14:02 +0000271// Create the global variable for the cache-fragmentation tool.
272GlobalVariable *EfficiencySanitizer::createCacheFragInfoGV(
273 Module &M, Constant *UnitName) {
274 assert(Options.ToolType == EfficiencySanitizerOptions::ESAN_CacheFrag);
275
276 auto *Int8PtrTy = Type::getInt8PtrTy(*Ctx);
277 auto *Int8PtrPtrTy = Int8PtrTy->getPointerTo();
278 auto *Int32Ty = Type::getInt32Ty(*Ctx);
Qin Zhao6d3bd682016-06-02 17:30:47 +0000279 auto *Int64Ty = Type::getInt64Ty(*Ctx);
Qin Zhao1762eef2016-05-31 17:14:02 +0000280 auto *Int64PtrTy = Type::getInt64PtrTy(*Ctx);
281 // This structure should be kept consistent with the StructInfo struct
282 // in the runtime library.
283 // struct StructInfo {
284 // const char *StructName;
Qin Zhao6d3bd682016-06-02 17:30:47 +0000285 // u32 NumFields;
Qin Zhao1762eef2016-05-31 17:14:02 +0000286 // u64 *FieldCounters;
287 // const char **FieldTypeNames;
288 // };
289 auto *StructInfoTy =
290 StructType::get(Int8PtrTy, Int32Ty, Int64PtrTy, Int8PtrPtrTy, nullptr);
291 auto *StructInfoPtrTy = StructInfoTy->getPointerTo();
292 // This structure should be kept consistent with the CacheFragInfo struct
293 // in the runtime library.
294 // struct CacheFragInfo {
295 // const char *UnitName;
Qin Zhao6d3bd682016-06-02 17:30:47 +0000296 // u32 NumStructs;
Qin Zhao1762eef2016-05-31 17:14:02 +0000297 // StructInfo *Structs;
298 // };
299 auto *CacheFragInfoTy =
300 StructType::get(Int8PtrTy, Int32Ty, StructInfoPtrTy, nullptr);
301
302 std::vector<StructType *> Vec = M.getIdentifiedStructTypes();
Qin Zhao6d3bd682016-06-02 17:30:47 +0000303 unsigned NumStructs = 0;
304 SmallVector<Constant *, 16> Initializers;
305
306 for (auto &StructTy : Vec) {
307 if (shouldIgnoreStructType(StructTy)) {
308 ++NumIgnoredStructs;
309 continue;
310 }
311 ++NumStructs;
312
313 // StructName.
314 SmallString<MaxStructCounterNameSize> CounterNameStr;
315 createStructCounterName(StructTy, CounterNameStr);
316 GlobalVariable *StructCounterName = createPrivateGlobalForString(
317 M, CounterNameStr, /*AllowMerging*/true);
318
319 // FieldCounters.
320 // We create the counter array with StructCounterName and weak linkage
321 // so that the structs with the same name and layout from different
322 // compilation units will be merged into one.
323 auto *CounterArrayTy = ArrayType::get(Int64Ty, StructTy->getNumElements());
324 GlobalVariable *Counters =
325 new GlobalVariable(M, CounterArrayTy, false,
326 GlobalVariable::WeakAnyLinkage,
327 ConstantAggregateZero::get(CounterArrayTy),
328 CounterNameStr);
329
Qin Zhaoc14c2492016-06-03 02:33:04 +0000330 // Remember the counter variable for each struct type.
331 StructTyMap.insert(std::pair<Type *, GlobalVariable *>(StructTy, Counters));
332
Qin Zhao6d3bd682016-06-02 17:30:47 +0000333 // FieldTypeNames.
334 // We pass the field type name array to the runtime for better reporting.
335 auto *TypeNameArrayTy = ArrayType::get(Int8PtrTy, StructTy->getNumElements());
336 GlobalVariable *TypeName =
337 new GlobalVariable(M, TypeNameArrayTy, true,
338 GlobalVariable::InternalLinkage, nullptr);
339 SmallVector<Constant *, 16> TypeNameVec;
340 for (unsigned i = 0; i < StructTy->getNumElements(); ++i) {
341 Type *Ty = StructTy->getElementType(i);
342 std::string Str;
343 raw_string_ostream StrOS(Str);
344 Ty->print(StrOS);
345 TypeNameVec.push_back(
346 ConstantExpr::getPointerCast(
347 createPrivateGlobalForString(M, StrOS.str(), true),
348 Int8PtrTy));
349 }
350 TypeName->setInitializer(ConstantArray::get(TypeNameArrayTy, TypeNameVec));
351
352 Initializers.push_back(
353 ConstantStruct::get(
354 StructInfoTy,
355 ConstantExpr::getPointerCast(StructCounterName, Int8PtrTy),
356 ConstantInt::get(Int32Ty, StructTy->getNumElements()),
357 ConstantExpr::getPointerCast(Counters, Int64PtrTy),
358 ConstantExpr::getPointerCast(TypeName, Int8PtrPtrTy),
359 nullptr));
360 }
361 // Structs.
362 Constant *StructInfo;
363 if (NumStructs == 0) {
364 StructInfo = ConstantPointerNull::get(StructInfoPtrTy);
365 } else {
366 auto *StructInfoArrayTy = ArrayType::get(StructInfoTy, NumStructs);
367 StructInfo = ConstantExpr::getPointerCast(
368 new GlobalVariable(M, StructInfoArrayTy, false,
369 GlobalVariable::InternalLinkage,
370 ConstantArray::get(StructInfoArrayTy, Initializers)),
371 StructInfoPtrTy);
372 }
373
Qin Zhao1762eef2016-05-31 17:14:02 +0000374 auto *CacheFragInfoGV = new GlobalVariable(
375 M, CacheFragInfoTy, true, GlobalVariable::InternalLinkage,
376 ConstantStruct::get(CacheFragInfoTy,
377 UnitName,
Qin Zhao6d3bd682016-06-02 17:30:47 +0000378 ConstantInt::get(Int32Ty, NumStructs),
379 StructInfo,
Qin Zhao1762eef2016-05-31 17:14:02 +0000380 nullptr));
381 return CacheFragInfoGV;
Derek Bruening0b872d92016-05-24 22:48:24 +0000382}
383
Qin Zhao1762eef2016-05-31 17:14:02 +0000384// Create the tool-specific argument passed to EsanInit and EsanExit.
385Constant *EfficiencySanitizer::createEsanInitToolInfoArg(Module &M) {
386 // This structure contains tool-specific information about each compilation
387 // unit (module) and is passed to the runtime library.
388 GlobalVariable *ToolInfoGV = nullptr;
389
390 auto *Int8PtrTy = Type::getInt8PtrTy(*Ctx);
391 // Compilation unit name.
392 auto *UnitName = ConstantExpr::getPointerCast(
393 createPrivateGlobalForString(M, M.getModuleIdentifier(), true),
394 Int8PtrTy);
395
396 // Create the tool-specific variable.
397 if (Options.ToolType == EfficiencySanitizerOptions::ESAN_CacheFrag)
398 ToolInfoGV = createCacheFragInfoGV(M, UnitName);
399
400 if (ToolInfoGV != nullptr)
401 return ConstantExpr::getPointerCast(ToolInfoGV, Int8PtrTy);
402
403 // Create the null pointer if no tool-specific variable created.
404 return ConstantPointerNull::get(Int8PtrTy);
405}
406
407void EfficiencySanitizer::createDestructor(Module &M, Constant *ToolInfoArg) {
408 PointerType *Int8PtrTy = Type::getInt8PtrTy(*Ctx);
Derek Bruening0b872d92016-05-24 22:48:24 +0000409 EsanDtorFunction = Function::Create(FunctionType::get(Type::getVoidTy(*Ctx),
410 false),
411 GlobalValue::InternalLinkage,
412 EsanModuleDtorName, &M);
413 ReturnInst::Create(*Ctx, BasicBlock::Create(*Ctx, "", EsanDtorFunction));
414 IRBuilder<> IRB_Dtor(EsanDtorFunction->getEntryBlock().getTerminator());
415 Function *EsanExit = checkSanitizerInterfaceFunction(
416 M.getOrInsertFunction(EsanExitName, IRB_Dtor.getVoidTy(),
Qin Zhao1762eef2016-05-31 17:14:02 +0000417 Int8PtrTy, nullptr));
Derek Bruening0b872d92016-05-24 22:48:24 +0000418 EsanExit->setLinkage(Function::ExternalLinkage);
Qin Zhao1762eef2016-05-31 17:14:02 +0000419 IRB_Dtor.CreateCall(EsanExit, {ToolInfoArg});
Derek Bruening0b872d92016-05-24 22:48:24 +0000420 appendToGlobalDtors(M, EsanDtorFunction, EsanCtorAndDtorPriority);
421}
422
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000423bool EfficiencySanitizer::initOnModule(Module &M) {
Derek Brueningd862c172016-04-21 21:30:22 +0000424 Ctx = &M.getContext();
425 const DataLayout &DL = M.getDataLayout();
426 IRBuilder<> IRB(M.getContext());
427 IntegerType *OrdTy = IRB.getInt32Ty();
Qin Zhao1762eef2016-05-31 17:14:02 +0000428 PointerType *Int8PtrTy = Type::getInt8PtrTy(*Ctx);
Derek Brueningd862c172016-04-21 21:30:22 +0000429 IntptrTy = DL.getIntPtrType(M.getContext());
Derek Bruening0b872d92016-05-24 22:48:24 +0000430 // Create the variable passed to EsanInit and EsanExit.
Qin Zhao1762eef2016-05-31 17:14:02 +0000431 Constant *ToolInfoArg = createEsanInitToolInfoArg(M);
Derek Bruening0b872d92016-05-24 22:48:24 +0000432 // Constructor
Derek Brueningd862c172016-04-21 21:30:22 +0000433 std::tie(EsanCtorFunction, std::ignore) = createSanitizerCtorAndInitFunctions(
Qin Zhao1762eef2016-05-31 17:14:02 +0000434 M, EsanModuleCtorName, EsanInitName, /*InitArgTypes=*/{OrdTy, Int8PtrTy},
Derek Brueningd862c172016-04-21 21:30:22 +0000435 /*InitArgs=*/{
Derek Bruening0b872d92016-05-24 22:48:24 +0000436 ConstantInt::get(OrdTy, static_cast<int>(Options.ToolType)),
Qin Zhao1762eef2016-05-31 17:14:02 +0000437 ToolInfoArg});
Derek Bruening0b872d92016-05-24 22:48:24 +0000438 appendToGlobalCtors(M, EsanCtorFunction, EsanCtorAndDtorPriority);
Derek Brueningd862c172016-04-21 21:30:22 +0000439
Qin Zhao1762eef2016-05-31 17:14:02 +0000440 createDestructor(M, ToolInfoArg);
Derek Brueningd862c172016-04-21 21:30:22 +0000441 return true;
442}
443
Derek Bruening5662b932016-05-25 00:17:24 +0000444Value *EfficiencySanitizer::appToShadow(Value *Shadow, IRBuilder<> &IRB) {
445 // Shadow = ((App & Mask) + Offs) >> Scale
446 Shadow = IRB.CreateAnd(Shadow, ConstantInt::get(IntptrTy, ShadowMask));
447 uint64_t Offs;
448 int Scale = ShadowScale[Options.ToolType];
449 if (Scale <= 2)
450 Offs = ShadowOffs[Scale];
451 else
452 Offs = ShadowOffs[0] << Scale;
453 Shadow = IRB.CreateAdd(Shadow, ConstantInt::get(IntptrTy, Offs));
454 if (Scale > 0)
455 Shadow = IRB.CreateLShr(Shadow, Scale);
456 return Shadow;
457}
458
Derek Brueningd862c172016-04-21 21:30:22 +0000459bool EfficiencySanitizer::shouldIgnoreMemoryAccess(Instruction *I) {
460 if (Options.ToolType == EfficiencySanitizerOptions::ESAN_CacheFrag) {
461 // We'd like to know about cache fragmentation in vtable accesses and
462 // constant data references, so we do not currently ignore anything.
463 return false;
Derek Bruening5662b932016-05-25 00:17:24 +0000464 } else if (Options.ToolType == EfficiencySanitizerOptions::ESAN_WorkingSet) {
465 // TODO: the instrumentation disturbs the data layout on the stack, so we
466 // may want to add an option to ignore stack references (if we can
467 // distinguish them) to reduce overhead.
Derek Brueningd862c172016-04-21 21:30:22 +0000468 }
469 // TODO(bruening): future tools will be returning true for some cases.
470 return false;
471}
472
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000473bool EfficiencySanitizer::runOnModule(Module &M) {
474 bool Res = initOnModule(M);
475 initializeCallbacks(M);
476 for (auto &F : M) {
477 Res |= runOnFunction(F, M);
478 }
479 return Res;
480}
481
482bool EfficiencySanitizer::runOnFunction(Function &F, Module &M) {
Derek Brueningd862c172016-04-21 21:30:22 +0000483 // This is required to prevent instrumenting the call to __esan_init from
484 // within the module constructor.
485 if (&F == EsanCtorFunction)
486 return false;
Derek Brueningd862c172016-04-21 21:30:22 +0000487 SmallVector<Instruction *, 8> LoadsAndStores;
488 SmallVector<Instruction *, 8> MemIntrinCalls;
Qin Zhaoc14c2492016-06-03 02:33:04 +0000489 SmallVector<Instruction *, 8> GetElementPtrs;
Derek Brueningd862c172016-04-21 21:30:22 +0000490 bool Res = false;
Derek Bruening0b872d92016-05-24 22:48:24 +0000491 const DataLayout &DL = M.getDataLayout();
Derek Brueningd862c172016-04-21 21:30:22 +0000492
493 for (auto &BB : F) {
494 for (auto &Inst : BB) {
495 if ((isa<LoadInst>(Inst) || isa<StoreInst>(Inst) ||
496 isa<AtomicRMWInst>(Inst) || isa<AtomicCmpXchgInst>(Inst)) &&
497 !shouldIgnoreMemoryAccess(&Inst))
498 LoadsAndStores.push_back(&Inst);
499 else if (isa<MemIntrinsic>(Inst))
500 MemIntrinCalls.push_back(&Inst);
Qin Zhaoc14c2492016-06-03 02:33:04 +0000501 else if (isa<GetElementPtrInst>(Inst))
502 GetElementPtrs.push_back(&Inst);
Derek Brueningd862c172016-04-21 21:30:22 +0000503 }
504 }
505
506 if (ClInstrumentLoadsAndStores) {
507 for (auto Inst : LoadsAndStores) {
508 Res |= instrumentLoadOrStore(Inst, DL);
509 }
510 }
511
512 if (ClInstrumentMemIntrinsics) {
513 for (auto Inst : MemIntrinCalls) {
514 Res |= instrumentMemIntrinsic(cast<MemIntrinsic>(Inst));
515 }
516 }
517
Qin Zhaoc14c2492016-06-03 02:33:04 +0000518 if (Options.ToolType == EfficiencySanitizerOptions::ESAN_CacheFrag) {
519 for (auto Inst : GetElementPtrs) {
520 Res |= instrumentGetElementPtr(Inst, M);
521 }
522 }
523
Derek Brueningd862c172016-04-21 21:30:22 +0000524 return Res;
525}
526
527bool EfficiencySanitizer::instrumentLoadOrStore(Instruction *I,
528 const DataLayout &DL) {
529 IRBuilder<> IRB(I);
530 bool IsStore;
531 Value *Addr;
532 unsigned Alignment;
533 if (LoadInst *Load = dyn_cast<LoadInst>(I)) {
534 IsStore = false;
535 Alignment = Load->getAlignment();
536 Addr = Load->getPointerOperand();
537 } else if (StoreInst *Store = dyn_cast<StoreInst>(I)) {
538 IsStore = true;
539 Alignment = Store->getAlignment();
540 Addr = Store->getPointerOperand();
541 } else if (AtomicRMWInst *RMW = dyn_cast<AtomicRMWInst>(I)) {
542 IsStore = true;
543 Alignment = 0;
544 Addr = RMW->getPointerOperand();
545 } else if (AtomicCmpXchgInst *Xchg = dyn_cast<AtomicCmpXchgInst>(I)) {
546 IsStore = true;
547 Alignment = 0;
548 Addr = Xchg->getPointerOperand();
549 } else
550 llvm_unreachable("Unsupported mem access type");
551
552 Type *OrigTy = cast<PointerType>(Addr->getType())->getElementType();
553 const uint32_t TypeSizeBytes = DL.getTypeStoreSizeInBits(OrigTy) / 8;
554 Value *OnAccessFunc = nullptr;
Derek Bruening5662b932016-05-25 00:17:24 +0000555
556 // Convert 0 to the default alignment.
557 if (Alignment == 0)
558 Alignment = DL.getPrefTypeAlignment(OrigTy);
559
Derek Brueningd862c172016-04-21 21:30:22 +0000560 if (IsStore)
561 NumInstrumentedStores++;
562 else
563 NumInstrumentedLoads++;
564 int Idx = getMemoryAccessFuncIndex(Addr, DL);
565 if (Idx < 0) {
566 OnAccessFunc = IsStore ? EsanUnalignedStoreN : EsanUnalignedLoadN;
567 IRB.CreateCall(OnAccessFunc,
568 {IRB.CreatePointerCast(Addr, IRB.getInt8PtrTy()),
569 ConstantInt::get(IntptrTy, TypeSizeBytes)});
570 } else {
571 if (instrumentFastpath(I, DL, IsStore, Addr, Alignment)) {
572 NumFastpaths++;
573 return true;
574 }
575 if (Alignment == 0 || Alignment >= 8 || (Alignment % TypeSizeBytes) == 0)
576 OnAccessFunc = IsStore ? EsanAlignedStore[Idx] : EsanAlignedLoad[Idx];
577 else
578 OnAccessFunc = IsStore ? EsanUnalignedStore[Idx] : EsanUnalignedLoad[Idx];
579 IRB.CreateCall(OnAccessFunc,
580 IRB.CreatePointerCast(Addr, IRB.getInt8PtrTy()));
581 }
582 return true;
583}
584
585// It's simplest to replace the memset/memmove/memcpy intrinsics with
586// calls that the runtime library intercepts.
587// Our pass is late enough that calls should not turn back into intrinsics.
588bool EfficiencySanitizer::instrumentMemIntrinsic(MemIntrinsic *MI) {
589 IRBuilder<> IRB(MI);
590 bool Res = false;
591 if (isa<MemSetInst>(MI)) {
592 IRB.CreateCall(
593 MemsetFn,
594 {IRB.CreatePointerCast(MI->getArgOperand(0), IRB.getInt8PtrTy()),
595 IRB.CreateIntCast(MI->getArgOperand(1), IRB.getInt32Ty(), false),
596 IRB.CreateIntCast(MI->getArgOperand(2), IntptrTy, false)});
597 MI->eraseFromParent();
598 Res = true;
599 } else if (isa<MemTransferInst>(MI)) {
600 IRB.CreateCall(
601 isa<MemCpyInst>(MI) ? MemcpyFn : MemmoveFn,
602 {IRB.CreatePointerCast(MI->getArgOperand(0), IRB.getInt8PtrTy()),
603 IRB.CreatePointerCast(MI->getArgOperand(1), IRB.getInt8PtrTy()),
604 IRB.CreateIntCast(MI->getArgOperand(2), IntptrTy, false)});
605 MI->eraseFromParent();
606 Res = true;
607 } else
608 llvm_unreachable("Unsupported mem intrinsic type");
609 return Res;
610}
611
Qin Zhaoc14c2492016-06-03 02:33:04 +0000612bool EfficiencySanitizer::instrumentGetElementPtr(Instruction *I, Module &M) {
613 GetElementPtrInst *GepInst = dyn_cast<GetElementPtrInst>(I);
614 if (GepInst == nullptr || !isa<StructType>(GepInst->getSourceElementType()) ||
615 StructTyMap.count(GepInst->getSourceElementType()) == 0 ||
616 !GepInst->hasAllConstantIndices() ||
617 // Only handle simple struct field GEP.
618 GepInst->getNumIndices() != 2) {
619 ++NumIgnoredGEPs;
620 return false;
621 }
622 StructType *StructTy = dyn_cast<StructType>(GepInst->getSourceElementType());
623 if (shouldIgnoreStructType(StructTy)) {
624 ++NumIgnoredGEPs;
625 return false;
626 }
627 ++NumInstrumentedGEPs;
628 // Use the last index as the index within the struct.
629 ConstantInt *Idx = dyn_cast<ConstantInt>(GepInst->getOperand(2));
630 if (Idx == nullptr || Idx->getZExtValue() > StructTy->getNumElements())
631 return false;
632
633 GlobalVariable *CounterArray = StructTyMap[StructTy];
634 if (CounterArray == nullptr)
635 return false;
636 IRBuilder<> IRB(I);
637 Constant *Indices[2];
638 // Xref http://llvm.org/docs/LangRef.html#i-getelementptr and
639 // http://llvm.org/docs/GetElementPtr.html.
640 // The first index of the GEP instruction steps through the first operand,
641 // i.e., the array itself.
642 Indices[0] = ConstantInt::get(IRB.getInt32Ty(), 0);
643 // The second index is the index within the array.
644 Indices[1] = ConstantInt::get(IRB.getInt32Ty(), Idx->getZExtValue());
645 Constant *Counter =
646 ConstantExpr::getGetElementPtr(ArrayType::get(IRB.getInt64Ty(),
647 StructTy->getNumElements()),
648 CounterArray, Indices);
649 Value *Load = IRB.CreateLoad(Counter);
650 IRB.CreateStore(IRB.CreateAdd(Load, ConstantInt::get(IRB.getInt64Ty(), 1)),
651 Counter);
652 return true;
653}
654
Derek Brueningd862c172016-04-21 21:30:22 +0000655int EfficiencySanitizer::getMemoryAccessFuncIndex(Value *Addr,
656 const DataLayout &DL) {
657 Type *OrigPtrTy = Addr->getType();
658 Type *OrigTy = cast<PointerType>(OrigPtrTy)->getElementType();
659 assert(OrigTy->isSized());
660 // The size is always a multiple of 8.
661 uint32_t TypeSizeBytes = DL.getTypeStoreSizeInBits(OrigTy) / 8;
662 if (TypeSizeBytes != 1 && TypeSizeBytes != 2 && TypeSizeBytes != 4 &&
663 TypeSizeBytes != 8 && TypeSizeBytes != 16) {
664 // Irregular sizes do not have per-size call targets.
665 NumAccessesWithIrregularSize++;
666 return -1;
667 }
668 size_t Idx = countTrailingZeros(TypeSizeBytes);
669 assert(Idx < NumberOfAccessSizes);
670 return Idx;
671}
672
673bool EfficiencySanitizer::instrumentFastpath(Instruction *I,
674 const DataLayout &DL, bool IsStore,
675 Value *Addr, unsigned Alignment) {
676 if (Options.ToolType == EfficiencySanitizerOptions::ESAN_CacheFrag) {
677 return instrumentFastpathCacheFrag(I, DL, Addr, Alignment);
Derek Bruening5662b932016-05-25 00:17:24 +0000678 } else if (Options.ToolType == EfficiencySanitizerOptions::ESAN_WorkingSet) {
679 return instrumentFastpathWorkingSet(I, DL, Addr, Alignment);
Derek Brueningd862c172016-04-21 21:30:22 +0000680 }
681 return false;
682}
683
684bool EfficiencySanitizer::instrumentFastpathCacheFrag(Instruction *I,
685 const DataLayout &DL,
686 Value *Addr,
687 unsigned Alignment) {
688 // TODO(bruening): implement a fastpath for aligned accesses
689 return false;
690}
Derek Bruening5662b932016-05-25 00:17:24 +0000691
692bool EfficiencySanitizer::instrumentFastpathWorkingSet(
693 Instruction *I, const DataLayout &DL, Value *Addr, unsigned Alignment) {
694 assert(ShadowScale[Options.ToolType] == 6); // The code below assumes this
695 IRBuilder<> IRB(I);
696 Type *OrigTy = cast<PointerType>(Addr->getType())->getElementType();
697 const uint32_t TypeSize = DL.getTypeStoreSizeInBits(OrigTy);
698 // Bail to the slowpath if the access might touch multiple cache lines.
699 // An access aligned to its size is guaranteed to be intra-cache-line.
700 // getMemoryAccessFuncIndex has already ruled out a size larger than 16
701 // and thus larger than a cache line for platforms this tool targets
702 // (and our shadow memory setup assumes 64-byte cache lines).
703 assert(TypeSize <= 64);
704 if (!(TypeSize == 8 ||
705 (Alignment % (TypeSize / 8)) == 0))
706 return false;
707
708 // We inline instrumentation to set the corresponding shadow bits for
709 // each cache line touched by the application. Here we handle a single
710 // load or store where we've already ruled out the possibility that it
711 // might touch more than one cache line and thus we simply update the
712 // shadow memory for a single cache line.
713 // Our shadow memory model is fine with races when manipulating shadow values.
714 // We generate the following code:
715 //
716 // const char BitMask = 0x81;
717 // char *ShadowAddr = appToShadow(AppAddr);
718 // if ((*ShadowAddr & BitMask) != BitMask)
719 // *ShadowAddr |= Bitmask;
720 //
721 Value *AddrPtr = IRB.CreatePointerCast(Addr, IntptrTy);
722 Value *ShadowPtr = appToShadow(AddrPtr, IRB);
723 Type *ShadowTy = IntegerType::get(*Ctx, 8U);
724 Type *ShadowPtrTy = PointerType::get(ShadowTy, 0);
725 // The bottom bit is used for the current sampling period's working set.
726 // The top bit is used for the total working set. We set both on each
727 // memory access, if they are not already set.
728 Value *ValueMask = ConstantInt::get(ShadowTy, 0x81); // 10000001B
729
730 Value *OldValue = IRB.CreateLoad(IRB.CreateIntToPtr(ShadowPtr, ShadowPtrTy));
731 // The AND and CMP will be turned into a TEST instruction by the compiler.
732 Value *Cmp = IRB.CreateICmpNE(IRB.CreateAnd(OldValue, ValueMask), ValueMask);
733 TerminatorInst *CmpTerm = SplitBlockAndInsertIfThen(Cmp, I, false);
734 // FIXME: do I need to call SetCurrentDebugLocation?
735 IRB.SetInsertPoint(CmpTerm);
736 // We use OR to set the shadow bits to avoid corrupting the middle 6 bits,
737 // which are used by the runtime library.
738 Value *NewVal = IRB.CreateOr(OldValue, ValueMask);
739 IRB.CreateStore(NewVal, IRB.CreateIntToPtr(ShadowPtr, ShadowPtrTy));
740 IRB.SetInsertPoint(I);
741
742 return true;
743}