blob: 36f361d74594376f8563b16cf148869090ce55ac [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");
Derek Brueningd862c172016-04-21 21:30:22 +000066
Derek Bruening0b872d92016-05-24 22:48:24 +000067static const uint64_t EsanCtorAndDtorPriority = 0;
Derek Brueningd862c172016-04-21 21:30:22 +000068static const char *const EsanModuleCtorName = "esan.module_ctor";
Derek Bruening0b872d92016-05-24 22:48:24 +000069static const char *const EsanModuleDtorName = "esan.module_dtor";
Derek Brueningd862c172016-04-21 21:30:22 +000070static const char *const EsanInitName = "__esan_init";
Derek Bruening0b872d92016-05-24 22:48:24 +000071static const char *const EsanExitName = "__esan_exit";
Derek Brueningd862c172016-04-21 21:30:22 +000072
Derek Bruening5662b932016-05-25 00:17:24 +000073// We must keep these Shadow* constants consistent with the esan runtime.
74// FIXME: Try to place these shadow constants, the names of the __esan_*
75// interface functions, and the ToolType enum into a header shared between
76// llvm and compiler-rt.
77static const uint64_t ShadowMask = 0x00000fffffffffffull;
78static const uint64_t ShadowOffs[3] = { // Indexed by scale
79 0x0000130000000000ull,
80 0x0000220000000000ull,
81 0x0000440000000000ull,
82};
83// This array is indexed by the ToolType enum.
84static const int ShadowScale[] = {
85 0, // ESAN_None.
86 2, // ESAN_CacheFrag: 4B:1B, so 4 to 1 == >>2.
87 6, // ESAN_WorkingSet: 64B:1B, so 64 to 1 == >>6.
88};
89
Qin Zhao6d3bd682016-06-02 17:30:47 +000090// MaxStructCounterNameSize is a soft size limit to avoid insanely long
91// names for those extremely large structs.
92static const unsigned MaxStructCounterNameSize = 512;
93
Derek Brueningd862c172016-04-21 21:30:22 +000094namespace {
95
96static EfficiencySanitizerOptions
97OverrideOptionsFromCL(EfficiencySanitizerOptions Options) {
98 if (ClToolCacheFrag)
99 Options.ToolType = EfficiencySanitizerOptions::ESAN_CacheFrag;
Derek Bruening5662b932016-05-25 00:17:24 +0000100 else if (ClToolWorkingSet)
101 Options.ToolType = EfficiencySanitizerOptions::ESAN_WorkingSet;
Derek Brueningd862c172016-04-21 21:30:22 +0000102
103 // Direct opt invocation with no params will have the default ESAN_None.
104 // We run the default tool in that case.
105 if (Options.ToolType == EfficiencySanitizerOptions::ESAN_None)
106 Options.ToolType = EfficiencySanitizerOptions::ESAN_CacheFrag;
107
108 return Options;
109}
110
Qin Zhao1762eef2016-05-31 17:14:02 +0000111// Create a constant for Str so that we can pass it to the run-time lib.
112static GlobalVariable *createPrivateGlobalForString(Module &M, StringRef Str,
113 bool AllowMerging) {
114 Constant *StrConst = ConstantDataArray::getString(M.getContext(), Str);
115 // We use private linkage for module-local strings. If they can be merged
116 // with another one, we set the unnamed_addr attribute.
117 GlobalVariable *GV =
118 new GlobalVariable(M, StrConst->getType(), true,
119 GlobalValue::PrivateLinkage, StrConst, "");
120 if (AllowMerging)
121 GV->setUnnamedAddr(true);
122 GV->setAlignment(1); // Strings may not be merged w/o setting align 1.
123 return GV;
124}
125
Derek Brueningd862c172016-04-21 21:30:22 +0000126/// EfficiencySanitizer: instrument each module to find performance issues.
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000127class EfficiencySanitizer : public ModulePass {
Derek Brueningd862c172016-04-21 21:30:22 +0000128public:
129 EfficiencySanitizer(
130 const EfficiencySanitizerOptions &Opts = EfficiencySanitizerOptions())
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000131 : ModulePass(ID), Options(OverrideOptionsFromCL(Opts)) {}
Derek Brueningd862c172016-04-21 21:30:22 +0000132 const char *getPassName() const override;
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000133 bool runOnModule(Module &M) override;
Derek Brueningd862c172016-04-21 21:30:22 +0000134 static char ID;
135
136private:
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000137 bool initOnModule(Module &M);
Derek Brueningd862c172016-04-21 21:30:22 +0000138 void initializeCallbacks(Module &M);
Qin Zhao6d3bd682016-06-02 17:30:47 +0000139 bool shouldIgnoreStructType(StructType *StructTy);
140 void createStructCounterName(
141 StructType *StructTy, SmallString<MaxStructCounterNameSize> &NameStr);
Qin Zhao1762eef2016-05-31 17:14:02 +0000142 GlobalVariable *createCacheFragInfoGV(Module &M, Constant *UnitName);
143 Constant *createEsanInitToolInfoArg(Module &M);
144 void createDestructor(Module &M, Constant *ToolInfoArg);
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000145 bool runOnFunction(Function &F, Module &M);
Derek Brueningd862c172016-04-21 21:30:22 +0000146 bool instrumentLoadOrStore(Instruction *I, const DataLayout &DL);
147 bool instrumentMemIntrinsic(MemIntrinsic *MI);
148 bool shouldIgnoreMemoryAccess(Instruction *I);
149 int getMemoryAccessFuncIndex(Value *Addr, const DataLayout &DL);
Derek Bruening5662b932016-05-25 00:17:24 +0000150 Value *appToShadow(Value *Shadow, IRBuilder<> &IRB);
Derek Brueningd862c172016-04-21 21:30:22 +0000151 bool instrumentFastpath(Instruction *I, const DataLayout &DL, bool IsStore,
152 Value *Addr, unsigned Alignment);
153 // Each tool has its own fastpath routine:
154 bool instrumentFastpathCacheFrag(Instruction *I, const DataLayout &DL,
155 Value *Addr, unsigned Alignment);
Derek Bruening5662b932016-05-25 00:17:24 +0000156 bool instrumentFastpathWorkingSet(Instruction *I, const DataLayout &DL,
157 Value *Addr, unsigned Alignment);
Derek Brueningd862c172016-04-21 21:30:22 +0000158
159 EfficiencySanitizerOptions Options;
160 LLVMContext *Ctx;
161 Type *IntptrTy;
162 // Our slowpath involves callouts to the runtime library.
163 // Access sizes are powers of two: 1, 2, 4, 8, 16.
164 static const size_t NumberOfAccessSizes = 5;
165 Function *EsanAlignedLoad[NumberOfAccessSizes];
166 Function *EsanAlignedStore[NumberOfAccessSizes];
167 Function *EsanUnalignedLoad[NumberOfAccessSizes];
168 Function *EsanUnalignedStore[NumberOfAccessSizes];
169 // For irregular sizes of any alignment:
170 Function *EsanUnalignedLoadN, *EsanUnalignedStoreN;
171 Function *MemmoveFn, *MemcpyFn, *MemsetFn;
172 Function *EsanCtorFunction;
Derek Bruening0b872d92016-05-24 22:48:24 +0000173 Function *EsanDtorFunction;
Derek Brueningd862c172016-04-21 21:30:22 +0000174};
175} // namespace
176
177char EfficiencySanitizer::ID = 0;
178INITIALIZE_PASS(EfficiencySanitizer, "esan",
179 "EfficiencySanitizer: finds performance issues.", false, false)
180
181const char *EfficiencySanitizer::getPassName() const {
182 return "EfficiencySanitizer";
183}
184
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000185ModulePass *
Derek Brueningd862c172016-04-21 21:30:22 +0000186llvm::createEfficiencySanitizerPass(const EfficiencySanitizerOptions &Options) {
187 return new EfficiencySanitizer(Options);
188}
189
190void EfficiencySanitizer::initializeCallbacks(Module &M) {
191 IRBuilder<> IRB(M.getContext());
192 // Initialize the callbacks.
193 for (size_t Idx = 0; Idx < NumberOfAccessSizes; ++Idx) {
194 const unsigned ByteSize = 1U << Idx;
195 std::string ByteSizeStr = utostr(ByteSize);
196 // We'll inline the most common (i.e., aligned and frequent sizes)
197 // load + store instrumentation: these callouts are for the slowpath.
198 SmallString<32> AlignedLoadName("__esan_aligned_load" + ByteSizeStr);
199 EsanAlignedLoad[Idx] =
200 checkSanitizerInterfaceFunction(M.getOrInsertFunction(
201 AlignedLoadName, IRB.getVoidTy(), IRB.getInt8PtrTy(), nullptr));
202 SmallString<32> AlignedStoreName("__esan_aligned_store" + ByteSizeStr);
203 EsanAlignedStore[Idx] =
204 checkSanitizerInterfaceFunction(M.getOrInsertFunction(
205 AlignedStoreName, IRB.getVoidTy(), IRB.getInt8PtrTy(), nullptr));
206 SmallString<32> UnalignedLoadName("__esan_unaligned_load" + ByteSizeStr);
207 EsanUnalignedLoad[Idx] =
208 checkSanitizerInterfaceFunction(M.getOrInsertFunction(
209 UnalignedLoadName, IRB.getVoidTy(), IRB.getInt8PtrTy(), nullptr));
210 SmallString<32> UnalignedStoreName("__esan_unaligned_store" + ByteSizeStr);
211 EsanUnalignedStore[Idx] =
212 checkSanitizerInterfaceFunction(M.getOrInsertFunction(
213 UnalignedStoreName, IRB.getVoidTy(), IRB.getInt8PtrTy(), nullptr));
214 }
215 EsanUnalignedLoadN = checkSanitizerInterfaceFunction(
216 M.getOrInsertFunction("__esan_unaligned_loadN", IRB.getVoidTy(),
217 IRB.getInt8PtrTy(), IntptrTy, nullptr));
218 EsanUnalignedStoreN = checkSanitizerInterfaceFunction(
219 M.getOrInsertFunction("__esan_unaligned_storeN", IRB.getVoidTy(),
220 IRB.getInt8PtrTy(), IntptrTy, nullptr));
221 MemmoveFn = checkSanitizerInterfaceFunction(
222 M.getOrInsertFunction("memmove", IRB.getInt8PtrTy(), IRB.getInt8PtrTy(),
223 IRB.getInt8PtrTy(), IntptrTy, nullptr));
224 MemcpyFn = checkSanitizerInterfaceFunction(
225 M.getOrInsertFunction("memcpy", IRB.getInt8PtrTy(), IRB.getInt8PtrTy(),
226 IRB.getInt8PtrTy(), IntptrTy, nullptr));
227 MemsetFn = checkSanitizerInterfaceFunction(
228 M.getOrInsertFunction("memset", IRB.getInt8PtrTy(), IRB.getInt8PtrTy(),
229 IRB.getInt32Ty(), IntptrTy, nullptr));
230}
231
Qin Zhao6d3bd682016-06-02 17:30:47 +0000232bool EfficiencySanitizer::shouldIgnoreStructType(StructType *StructTy) {
233 if (StructTy == nullptr || StructTy->isOpaque() /* no struct body */)
234 return true;
235 return false;
236}
237
238void EfficiencySanitizer::createStructCounterName(
239 StructType *StructTy, SmallString<MaxStructCounterNameSize> &NameStr) {
240 // Append NumFields and field type ids to avoid struct conflicts
241 // with the same name but different fields.
242 if (StructTy->hasName())
243 NameStr += StructTy->getName();
244 else
245 NameStr += "struct.anon";
246 // We allow the actual size of the StructCounterName to be larger than
247 // MaxStructCounterNameSize and append #NumFields and at least one
248 // field type id.
249 // Append #NumFields.
250 NameStr += "#";
251 Twine(StructTy->getNumElements()).toVector(NameStr);
252 // Append struct field type ids in the reverse order.
253 for (int i = StructTy->getNumElements() - 1; i >= 0; --i) {
254 NameStr += "#";
255 Twine(StructTy->getElementType(i)->getTypeID()).toVector(NameStr);
256 if (NameStr.size() >= MaxStructCounterNameSize)
257 break;
258 }
259 if (StructTy->isLiteral()) {
260 // End with # for literal struct.
261 NameStr += "#";
262 }
263}
264
Qin Zhao1762eef2016-05-31 17:14:02 +0000265// Create the global variable for the cache-fragmentation tool.
266GlobalVariable *EfficiencySanitizer::createCacheFragInfoGV(
267 Module &M, Constant *UnitName) {
268 assert(Options.ToolType == EfficiencySanitizerOptions::ESAN_CacheFrag);
269
270 auto *Int8PtrTy = Type::getInt8PtrTy(*Ctx);
271 auto *Int8PtrPtrTy = Int8PtrTy->getPointerTo();
272 auto *Int32Ty = Type::getInt32Ty(*Ctx);
Qin Zhao6d3bd682016-06-02 17:30:47 +0000273 auto *Int64Ty = Type::getInt64Ty(*Ctx);
Qin Zhao1762eef2016-05-31 17:14:02 +0000274 auto *Int64PtrTy = Type::getInt64PtrTy(*Ctx);
275 // This structure should be kept consistent with the StructInfo struct
276 // in the runtime library.
277 // struct StructInfo {
278 // const char *StructName;
Qin Zhao6d3bd682016-06-02 17:30:47 +0000279 // u32 NumFields;
Qin Zhao1762eef2016-05-31 17:14:02 +0000280 // u64 *FieldCounters;
281 // const char **FieldTypeNames;
282 // };
283 auto *StructInfoTy =
284 StructType::get(Int8PtrTy, Int32Ty, Int64PtrTy, Int8PtrPtrTy, nullptr);
285 auto *StructInfoPtrTy = StructInfoTy->getPointerTo();
286 // This structure should be kept consistent with the CacheFragInfo struct
287 // in the runtime library.
288 // struct CacheFragInfo {
289 // const char *UnitName;
Qin Zhao6d3bd682016-06-02 17:30:47 +0000290 // u32 NumStructs;
Qin Zhao1762eef2016-05-31 17:14:02 +0000291 // StructInfo *Structs;
292 // };
293 auto *CacheFragInfoTy =
294 StructType::get(Int8PtrTy, Int32Ty, StructInfoPtrTy, nullptr);
295
296 std::vector<StructType *> Vec = M.getIdentifiedStructTypes();
Qin Zhao6d3bd682016-06-02 17:30:47 +0000297 unsigned NumStructs = 0;
298 SmallVector<Constant *, 16> Initializers;
299
300 for (auto &StructTy : Vec) {
301 if (shouldIgnoreStructType(StructTy)) {
302 ++NumIgnoredStructs;
303 continue;
304 }
305 ++NumStructs;
306
307 // StructName.
308 SmallString<MaxStructCounterNameSize> CounterNameStr;
309 createStructCounterName(StructTy, CounterNameStr);
310 GlobalVariable *StructCounterName = createPrivateGlobalForString(
311 M, CounterNameStr, /*AllowMerging*/true);
312
313 // FieldCounters.
314 // We create the counter array with StructCounterName and weak linkage
315 // so that the structs with the same name and layout from different
316 // compilation units will be merged into one.
317 auto *CounterArrayTy = ArrayType::get(Int64Ty, StructTy->getNumElements());
318 GlobalVariable *Counters =
319 new GlobalVariable(M, CounterArrayTy, false,
320 GlobalVariable::WeakAnyLinkage,
321 ConstantAggregateZero::get(CounterArrayTy),
322 CounterNameStr);
323
324 // FieldTypeNames.
325 // We pass the field type name array to the runtime for better reporting.
326 auto *TypeNameArrayTy = ArrayType::get(Int8PtrTy, StructTy->getNumElements());
327 GlobalVariable *TypeName =
328 new GlobalVariable(M, TypeNameArrayTy, true,
329 GlobalVariable::InternalLinkage, nullptr);
330 SmallVector<Constant *, 16> TypeNameVec;
331 for (unsigned i = 0; i < StructTy->getNumElements(); ++i) {
332 Type *Ty = StructTy->getElementType(i);
333 std::string Str;
334 raw_string_ostream StrOS(Str);
335 Ty->print(StrOS);
336 TypeNameVec.push_back(
337 ConstantExpr::getPointerCast(
338 createPrivateGlobalForString(M, StrOS.str(), true),
339 Int8PtrTy));
340 }
341 TypeName->setInitializer(ConstantArray::get(TypeNameArrayTy, TypeNameVec));
342
343 Initializers.push_back(
344 ConstantStruct::get(
345 StructInfoTy,
346 ConstantExpr::getPointerCast(StructCounterName, Int8PtrTy),
347 ConstantInt::get(Int32Ty, StructTy->getNumElements()),
348 ConstantExpr::getPointerCast(Counters, Int64PtrTy),
349 ConstantExpr::getPointerCast(TypeName, Int8PtrPtrTy),
350 nullptr));
351 }
352 // Structs.
353 Constant *StructInfo;
354 if (NumStructs == 0) {
355 StructInfo = ConstantPointerNull::get(StructInfoPtrTy);
356 } else {
357 auto *StructInfoArrayTy = ArrayType::get(StructInfoTy, NumStructs);
358 StructInfo = ConstantExpr::getPointerCast(
359 new GlobalVariable(M, StructInfoArrayTy, false,
360 GlobalVariable::InternalLinkage,
361 ConstantArray::get(StructInfoArrayTy, Initializers)),
362 StructInfoPtrTy);
363 }
364
Qin Zhao1762eef2016-05-31 17:14:02 +0000365 auto *CacheFragInfoGV = new GlobalVariable(
366 M, CacheFragInfoTy, true, GlobalVariable::InternalLinkage,
367 ConstantStruct::get(CacheFragInfoTy,
368 UnitName,
Qin Zhao6d3bd682016-06-02 17:30:47 +0000369 ConstantInt::get(Int32Ty, NumStructs),
370 StructInfo,
Qin Zhao1762eef2016-05-31 17:14:02 +0000371 nullptr));
372 return CacheFragInfoGV;
Derek Bruening0b872d92016-05-24 22:48:24 +0000373}
374
Qin Zhao1762eef2016-05-31 17:14:02 +0000375// Create the tool-specific argument passed to EsanInit and EsanExit.
376Constant *EfficiencySanitizer::createEsanInitToolInfoArg(Module &M) {
377 // This structure contains tool-specific information about each compilation
378 // unit (module) and is passed to the runtime library.
379 GlobalVariable *ToolInfoGV = nullptr;
380
381 auto *Int8PtrTy = Type::getInt8PtrTy(*Ctx);
382 // Compilation unit name.
383 auto *UnitName = ConstantExpr::getPointerCast(
384 createPrivateGlobalForString(M, M.getModuleIdentifier(), true),
385 Int8PtrTy);
386
387 // Create the tool-specific variable.
388 if (Options.ToolType == EfficiencySanitizerOptions::ESAN_CacheFrag)
389 ToolInfoGV = createCacheFragInfoGV(M, UnitName);
390
391 if (ToolInfoGV != nullptr)
392 return ConstantExpr::getPointerCast(ToolInfoGV, Int8PtrTy);
393
394 // Create the null pointer if no tool-specific variable created.
395 return ConstantPointerNull::get(Int8PtrTy);
396}
397
398void EfficiencySanitizer::createDestructor(Module &M, Constant *ToolInfoArg) {
399 PointerType *Int8PtrTy = Type::getInt8PtrTy(*Ctx);
Derek Bruening0b872d92016-05-24 22:48:24 +0000400 EsanDtorFunction = Function::Create(FunctionType::get(Type::getVoidTy(*Ctx),
401 false),
402 GlobalValue::InternalLinkage,
403 EsanModuleDtorName, &M);
404 ReturnInst::Create(*Ctx, BasicBlock::Create(*Ctx, "", EsanDtorFunction));
405 IRBuilder<> IRB_Dtor(EsanDtorFunction->getEntryBlock().getTerminator());
406 Function *EsanExit = checkSanitizerInterfaceFunction(
407 M.getOrInsertFunction(EsanExitName, IRB_Dtor.getVoidTy(),
Qin Zhao1762eef2016-05-31 17:14:02 +0000408 Int8PtrTy, nullptr));
Derek Bruening0b872d92016-05-24 22:48:24 +0000409 EsanExit->setLinkage(Function::ExternalLinkage);
Qin Zhao1762eef2016-05-31 17:14:02 +0000410 IRB_Dtor.CreateCall(EsanExit, {ToolInfoArg});
Derek Bruening0b872d92016-05-24 22:48:24 +0000411 appendToGlobalDtors(M, EsanDtorFunction, EsanCtorAndDtorPriority);
412}
413
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000414bool EfficiencySanitizer::initOnModule(Module &M) {
Derek Brueningd862c172016-04-21 21:30:22 +0000415 Ctx = &M.getContext();
416 const DataLayout &DL = M.getDataLayout();
417 IRBuilder<> IRB(M.getContext());
418 IntegerType *OrdTy = IRB.getInt32Ty();
Qin Zhao1762eef2016-05-31 17:14:02 +0000419 PointerType *Int8PtrTy = Type::getInt8PtrTy(*Ctx);
Derek Brueningd862c172016-04-21 21:30:22 +0000420 IntptrTy = DL.getIntPtrType(M.getContext());
Derek Bruening0b872d92016-05-24 22:48:24 +0000421 // Create the variable passed to EsanInit and EsanExit.
Qin Zhao1762eef2016-05-31 17:14:02 +0000422 Constant *ToolInfoArg = createEsanInitToolInfoArg(M);
Derek Bruening0b872d92016-05-24 22:48:24 +0000423 // Constructor
Derek Brueningd862c172016-04-21 21:30:22 +0000424 std::tie(EsanCtorFunction, std::ignore) = createSanitizerCtorAndInitFunctions(
Qin Zhao1762eef2016-05-31 17:14:02 +0000425 M, EsanModuleCtorName, EsanInitName, /*InitArgTypes=*/{OrdTy, Int8PtrTy},
Derek Brueningd862c172016-04-21 21:30:22 +0000426 /*InitArgs=*/{
Derek Bruening0b872d92016-05-24 22:48:24 +0000427 ConstantInt::get(OrdTy, static_cast<int>(Options.ToolType)),
Qin Zhao1762eef2016-05-31 17:14:02 +0000428 ToolInfoArg});
Derek Bruening0b872d92016-05-24 22:48:24 +0000429 appendToGlobalCtors(M, EsanCtorFunction, EsanCtorAndDtorPriority);
Derek Brueningd862c172016-04-21 21:30:22 +0000430
Qin Zhao1762eef2016-05-31 17:14:02 +0000431 createDestructor(M, ToolInfoArg);
Derek Brueningd862c172016-04-21 21:30:22 +0000432 return true;
433}
434
Derek Bruening5662b932016-05-25 00:17:24 +0000435Value *EfficiencySanitizer::appToShadow(Value *Shadow, IRBuilder<> &IRB) {
436 // Shadow = ((App & Mask) + Offs) >> Scale
437 Shadow = IRB.CreateAnd(Shadow, ConstantInt::get(IntptrTy, ShadowMask));
438 uint64_t Offs;
439 int Scale = ShadowScale[Options.ToolType];
440 if (Scale <= 2)
441 Offs = ShadowOffs[Scale];
442 else
443 Offs = ShadowOffs[0] << Scale;
444 Shadow = IRB.CreateAdd(Shadow, ConstantInt::get(IntptrTy, Offs));
445 if (Scale > 0)
446 Shadow = IRB.CreateLShr(Shadow, Scale);
447 return Shadow;
448}
449
Derek Brueningd862c172016-04-21 21:30:22 +0000450bool EfficiencySanitizer::shouldIgnoreMemoryAccess(Instruction *I) {
451 if (Options.ToolType == EfficiencySanitizerOptions::ESAN_CacheFrag) {
452 // We'd like to know about cache fragmentation in vtable accesses and
453 // constant data references, so we do not currently ignore anything.
454 return false;
Derek Bruening5662b932016-05-25 00:17:24 +0000455 } else if (Options.ToolType == EfficiencySanitizerOptions::ESAN_WorkingSet) {
456 // TODO: the instrumentation disturbs the data layout on the stack, so we
457 // may want to add an option to ignore stack references (if we can
458 // distinguish them) to reduce overhead.
Derek Brueningd862c172016-04-21 21:30:22 +0000459 }
460 // TODO(bruening): future tools will be returning true for some cases.
461 return false;
462}
463
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000464bool EfficiencySanitizer::runOnModule(Module &M) {
465 bool Res = initOnModule(M);
466 initializeCallbacks(M);
467 for (auto &F : M) {
468 Res |= runOnFunction(F, M);
469 }
470 return Res;
471}
472
473bool EfficiencySanitizer::runOnFunction(Function &F, Module &M) {
Derek Brueningd862c172016-04-21 21:30:22 +0000474 // This is required to prevent instrumenting the call to __esan_init from
475 // within the module constructor.
476 if (&F == EsanCtorFunction)
477 return false;
Derek Brueningd862c172016-04-21 21:30:22 +0000478 SmallVector<Instruction *, 8> LoadsAndStores;
479 SmallVector<Instruction *, 8> MemIntrinCalls;
480 bool Res = false;
Derek Bruening0b872d92016-05-24 22:48:24 +0000481 const DataLayout &DL = M.getDataLayout();
Derek Brueningd862c172016-04-21 21:30:22 +0000482
483 for (auto &BB : F) {
484 for (auto &Inst : BB) {
485 if ((isa<LoadInst>(Inst) || isa<StoreInst>(Inst) ||
486 isa<AtomicRMWInst>(Inst) || isa<AtomicCmpXchgInst>(Inst)) &&
487 !shouldIgnoreMemoryAccess(&Inst))
488 LoadsAndStores.push_back(&Inst);
489 else if (isa<MemIntrinsic>(Inst))
490 MemIntrinCalls.push_back(&Inst);
491 }
492 }
493
494 if (ClInstrumentLoadsAndStores) {
495 for (auto Inst : LoadsAndStores) {
496 Res |= instrumentLoadOrStore(Inst, DL);
497 }
498 }
499
500 if (ClInstrumentMemIntrinsics) {
501 for (auto Inst : MemIntrinCalls) {
502 Res |= instrumentMemIntrinsic(cast<MemIntrinsic>(Inst));
503 }
504 }
505
506 return Res;
507}
508
509bool EfficiencySanitizer::instrumentLoadOrStore(Instruction *I,
510 const DataLayout &DL) {
511 IRBuilder<> IRB(I);
512 bool IsStore;
513 Value *Addr;
514 unsigned Alignment;
515 if (LoadInst *Load = dyn_cast<LoadInst>(I)) {
516 IsStore = false;
517 Alignment = Load->getAlignment();
518 Addr = Load->getPointerOperand();
519 } else if (StoreInst *Store = dyn_cast<StoreInst>(I)) {
520 IsStore = true;
521 Alignment = Store->getAlignment();
522 Addr = Store->getPointerOperand();
523 } else if (AtomicRMWInst *RMW = dyn_cast<AtomicRMWInst>(I)) {
524 IsStore = true;
525 Alignment = 0;
526 Addr = RMW->getPointerOperand();
527 } else if (AtomicCmpXchgInst *Xchg = dyn_cast<AtomicCmpXchgInst>(I)) {
528 IsStore = true;
529 Alignment = 0;
530 Addr = Xchg->getPointerOperand();
531 } else
532 llvm_unreachable("Unsupported mem access type");
533
534 Type *OrigTy = cast<PointerType>(Addr->getType())->getElementType();
535 const uint32_t TypeSizeBytes = DL.getTypeStoreSizeInBits(OrigTy) / 8;
536 Value *OnAccessFunc = nullptr;
Derek Bruening5662b932016-05-25 00:17:24 +0000537
538 // Convert 0 to the default alignment.
539 if (Alignment == 0)
540 Alignment = DL.getPrefTypeAlignment(OrigTy);
541
Derek Brueningd862c172016-04-21 21:30:22 +0000542 if (IsStore)
543 NumInstrumentedStores++;
544 else
545 NumInstrumentedLoads++;
546 int Idx = getMemoryAccessFuncIndex(Addr, DL);
547 if (Idx < 0) {
548 OnAccessFunc = IsStore ? EsanUnalignedStoreN : EsanUnalignedLoadN;
549 IRB.CreateCall(OnAccessFunc,
550 {IRB.CreatePointerCast(Addr, IRB.getInt8PtrTy()),
551 ConstantInt::get(IntptrTy, TypeSizeBytes)});
552 } else {
553 if (instrumentFastpath(I, DL, IsStore, Addr, Alignment)) {
554 NumFastpaths++;
555 return true;
556 }
557 if (Alignment == 0 || Alignment >= 8 || (Alignment % TypeSizeBytes) == 0)
558 OnAccessFunc = IsStore ? EsanAlignedStore[Idx] : EsanAlignedLoad[Idx];
559 else
560 OnAccessFunc = IsStore ? EsanUnalignedStore[Idx] : EsanUnalignedLoad[Idx];
561 IRB.CreateCall(OnAccessFunc,
562 IRB.CreatePointerCast(Addr, IRB.getInt8PtrTy()));
563 }
564 return true;
565}
566
567// It's simplest to replace the memset/memmove/memcpy intrinsics with
568// calls that the runtime library intercepts.
569// Our pass is late enough that calls should not turn back into intrinsics.
570bool EfficiencySanitizer::instrumentMemIntrinsic(MemIntrinsic *MI) {
571 IRBuilder<> IRB(MI);
572 bool Res = false;
573 if (isa<MemSetInst>(MI)) {
574 IRB.CreateCall(
575 MemsetFn,
576 {IRB.CreatePointerCast(MI->getArgOperand(0), IRB.getInt8PtrTy()),
577 IRB.CreateIntCast(MI->getArgOperand(1), IRB.getInt32Ty(), false),
578 IRB.CreateIntCast(MI->getArgOperand(2), IntptrTy, false)});
579 MI->eraseFromParent();
580 Res = true;
581 } else if (isa<MemTransferInst>(MI)) {
582 IRB.CreateCall(
583 isa<MemCpyInst>(MI) ? MemcpyFn : MemmoveFn,
584 {IRB.CreatePointerCast(MI->getArgOperand(0), IRB.getInt8PtrTy()),
585 IRB.CreatePointerCast(MI->getArgOperand(1), IRB.getInt8PtrTy()),
586 IRB.CreateIntCast(MI->getArgOperand(2), IntptrTy, false)});
587 MI->eraseFromParent();
588 Res = true;
589 } else
590 llvm_unreachable("Unsupported mem intrinsic type");
591 return Res;
592}
593
594int EfficiencySanitizer::getMemoryAccessFuncIndex(Value *Addr,
595 const DataLayout &DL) {
596 Type *OrigPtrTy = Addr->getType();
597 Type *OrigTy = cast<PointerType>(OrigPtrTy)->getElementType();
598 assert(OrigTy->isSized());
599 // The size is always a multiple of 8.
600 uint32_t TypeSizeBytes = DL.getTypeStoreSizeInBits(OrigTy) / 8;
601 if (TypeSizeBytes != 1 && TypeSizeBytes != 2 && TypeSizeBytes != 4 &&
602 TypeSizeBytes != 8 && TypeSizeBytes != 16) {
603 // Irregular sizes do not have per-size call targets.
604 NumAccessesWithIrregularSize++;
605 return -1;
606 }
607 size_t Idx = countTrailingZeros(TypeSizeBytes);
608 assert(Idx < NumberOfAccessSizes);
609 return Idx;
610}
611
612bool EfficiencySanitizer::instrumentFastpath(Instruction *I,
613 const DataLayout &DL, bool IsStore,
614 Value *Addr, unsigned Alignment) {
615 if (Options.ToolType == EfficiencySanitizerOptions::ESAN_CacheFrag) {
616 return instrumentFastpathCacheFrag(I, DL, Addr, Alignment);
Derek Bruening5662b932016-05-25 00:17:24 +0000617 } else if (Options.ToolType == EfficiencySanitizerOptions::ESAN_WorkingSet) {
618 return instrumentFastpathWorkingSet(I, DL, Addr, Alignment);
Derek Brueningd862c172016-04-21 21:30:22 +0000619 }
620 return false;
621}
622
623bool EfficiencySanitizer::instrumentFastpathCacheFrag(Instruction *I,
624 const DataLayout &DL,
625 Value *Addr,
626 unsigned Alignment) {
627 // TODO(bruening): implement a fastpath for aligned accesses
628 return false;
629}
Derek Bruening5662b932016-05-25 00:17:24 +0000630
631bool EfficiencySanitizer::instrumentFastpathWorkingSet(
632 Instruction *I, const DataLayout &DL, Value *Addr, unsigned Alignment) {
633 assert(ShadowScale[Options.ToolType] == 6); // The code below assumes this
634 IRBuilder<> IRB(I);
635 Type *OrigTy = cast<PointerType>(Addr->getType())->getElementType();
636 const uint32_t TypeSize = DL.getTypeStoreSizeInBits(OrigTy);
637 // Bail to the slowpath if the access might touch multiple cache lines.
638 // An access aligned to its size is guaranteed to be intra-cache-line.
639 // getMemoryAccessFuncIndex has already ruled out a size larger than 16
640 // and thus larger than a cache line for platforms this tool targets
641 // (and our shadow memory setup assumes 64-byte cache lines).
642 assert(TypeSize <= 64);
643 if (!(TypeSize == 8 ||
644 (Alignment % (TypeSize / 8)) == 0))
645 return false;
646
647 // We inline instrumentation to set the corresponding shadow bits for
648 // each cache line touched by the application. Here we handle a single
649 // load or store where we've already ruled out the possibility that it
650 // might touch more than one cache line and thus we simply update the
651 // shadow memory for a single cache line.
652 // Our shadow memory model is fine with races when manipulating shadow values.
653 // We generate the following code:
654 //
655 // const char BitMask = 0x81;
656 // char *ShadowAddr = appToShadow(AppAddr);
657 // if ((*ShadowAddr & BitMask) != BitMask)
658 // *ShadowAddr |= Bitmask;
659 //
660 Value *AddrPtr = IRB.CreatePointerCast(Addr, IntptrTy);
661 Value *ShadowPtr = appToShadow(AddrPtr, IRB);
662 Type *ShadowTy = IntegerType::get(*Ctx, 8U);
663 Type *ShadowPtrTy = PointerType::get(ShadowTy, 0);
664 // The bottom bit is used for the current sampling period's working set.
665 // The top bit is used for the total working set. We set both on each
666 // memory access, if they are not already set.
667 Value *ValueMask = ConstantInt::get(ShadowTy, 0x81); // 10000001B
668
669 Value *OldValue = IRB.CreateLoad(IRB.CreateIntToPtr(ShadowPtr, ShadowPtrTy));
670 // The AND and CMP will be turned into a TEST instruction by the compiler.
671 Value *Cmp = IRB.CreateICmpNE(IRB.CreateAnd(OldValue, ValueMask), ValueMask);
672 TerminatorInst *CmpTerm = SplitBlockAndInsertIfThen(Cmp, I, false);
673 // FIXME: do I need to call SetCurrentDebugLocation?
674 IRB.SetInsertPoint(CmpTerm);
675 // We use OR to set the shadow bits to avoid corrupting the middle 6 bits,
676 // which are used by the runtime library.
677 Value *NewVal = IRB.CreateOr(OldValue, ValueMask);
678 IRB.CreateStore(NewVal, IRB.CreateIntToPtr(ShadowPtr, ShadowPtrTy));
679 IRB.SetInsertPoint(I);
680
681 return true;
682}