blob: 40a9fce7b0b3f0d36f4043ef145792f3974fb5d3 [file] [log] [blame]
Derek Brueningd862c172016-04-21 21:30:22 +00001//===-- EfficiencySanitizer.cpp - performance tuner -----------------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file is a part of EfficiencySanitizer, a family of performance tuners
11// that detects multiple performance issues via separate sub-tools.
12//
13// The instrumentation phase is straightforward:
14// - Take action on every memory access: either inlined instrumentation,
15// or Inserted calls to our run-time library.
16// - Optimizations may apply to avoid instrumenting some of the accesses.
17// - Turn mem{set,cpy,move} instrinsics into library calls.
18// The rest is handled by the run-time library.
19//===----------------------------------------------------------------------===//
20
21#include "llvm/Transforms/Instrumentation.h"
22#include "llvm/ADT/SmallString.h"
23#include "llvm/ADT/SmallVector.h"
24#include "llvm/ADT/Statistic.h"
25#include "llvm/ADT/StringExtras.h"
26#include "llvm/IR/Function.h"
27#include "llvm/IR/IRBuilder.h"
28#include "llvm/IR/IntrinsicInst.h"
29#include "llvm/IR/Module.h"
30#include "llvm/IR/Type.h"
31#include "llvm/Support/CommandLine.h"
32#include "llvm/Support/Debug.h"
Qin Zhao6d3bd682016-06-02 17:30:47 +000033#include "llvm/Support/raw_ostream.h"
Derek Brueningd862c172016-04-21 21:30:22 +000034#include "llvm/Transforms/Utils/BasicBlockUtils.h"
35#include "llvm/Transforms/Utils/ModuleUtils.h"
36
37using namespace llvm;
38
39#define DEBUG_TYPE "esan"
40
41// The tool type must be just one of these ClTool* options, as the tools
42// cannot be combined due to shadow memory constraints.
43static cl::opt<bool>
44 ClToolCacheFrag("esan-cache-frag", cl::init(false),
45 cl::desc("Detect data cache fragmentation"), cl::Hidden);
Derek Bruening5662b932016-05-25 00:17:24 +000046static cl::opt<bool>
47 ClToolWorkingSet("esan-working-set", cl::init(false),
48 cl::desc("Measure the working set size"), cl::Hidden);
Derek Brueningd862c172016-04-21 21:30:22 +000049// Each new tool will get its own opt flag here.
50// These are converted to EfficiencySanitizerOptions for use
51// in the code.
52
53static cl::opt<bool> ClInstrumentLoadsAndStores(
54 "esan-instrument-loads-and-stores", cl::init(true),
55 cl::desc("Instrument loads and stores"), cl::Hidden);
56static cl::opt<bool> ClInstrumentMemIntrinsics(
57 "esan-instrument-memintrinsics", cl::init(true),
58 cl::desc("Instrument memintrinsics (memset/memcpy/memmove)"), cl::Hidden);
Qin Zhaod677d882016-06-10 00:48:53 +000059static cl::opt<bool> ClInstrumentFastpath(
60 "esan-instrument-fastpath", cl::init(true),
61 cl::desc("Instrument fastpath"), cl::Hidden);
Derek Brueningd862c172016-04-21 21:30:22 +000062
Derek Bruening9ef57722016-06-03 22:29:52 +000063// Experiments show that the performance difference can be 2x or more,
64// and accuracy loss is typically negligible, so we turn this on by default.
65static cl::opt<bool> ClAssumeIntraCacheLine(
66 "esan-assume-intra-cache-line", cl::init(true),
67 cl::desc("Assume each memory access touches just one cache line, for "
68 "better performance but with a potential loss of accuracy."),
69 cl::Hidden);
70
Derek Brueningd862c172016-04-21 21:30:22 +000071STATISTIC(NumInstrumentedLoads, "Number of instrumented loads");
72STATISTIC(NumInstrumentedStores, "Number of instrumented stores");
73STATISTIC(NumFastpaths, "Number of instrumented fastpaths");
74STATISTIC(NumAccessesWithIrregularSize,
75 "Number of accesses with a size outside our targeted callout sizes");
Qin Zhao6d3bd682016-06-02 17:30:47 +000076STATISTIC(NumIgnoredStructs, "Number of ignored structs");
Qin Zhaoc14c2492016-06-03 02:33:04 +000077STATISTIC(NumIgnoredGEPs, "Number of ignored GEP instructions");
78STATISTIC(NumInstrumentedGEPs, "Number of instrumented GEP instructions");
Derek Bruening9ef57722016-06-03 22:29:52 +000079STATISTIC(NumAssumedIntraCacheLine,
80 "Number of accesses assumed to be intra-cache-line");
Derek Brueningd862c172016-04-21 21:30:22 +000081
Derek Bruening0b872d92016-05-24 22:48:24 +000082static const uint64_t EsanCtorAndDtorPriority = 0;
Derek Brueningd862c172016-04-21 21:30:22 +000083static const char *const EsanModuleCtorName = "esan.module_ctor";
Derek Bruening0b872d92016-05-24 22:48:24 +000084static const char *const EsanModuleDtorName = "esan.module_dtor";
Derek Brueningd862c172016-04-21 21:30:22 +000085static const char *const EsanInitName = "__esan_init";
Derek Bruening0b872d92016-05-24 22:48:24 +000086static const char *const EsanExitName = "__esan_exit";
Derek Brueningd862c172016-04-21 21:30:22 +000087
Derek Bruening4252a162016-06-03 19:40:37 +000088// We need to specify the tool to the runtime earlier than
89// the ctor is called in some cases, so we set a global variable.
90static const char *const EsanWhichToolName = "__esan_which_tool";
91
Derek Bruening5662b932016-05-25 00:17:24 +000092// We must keep these Shadow* constants consistent with the esan runtime.
93// FIXME: Try to place these shadow constants, the names of the __esan_*
94// interface functions, and the ToolType enum into a header shared between
95// llvm and compiler-rt.
96static const uint64_t ShadowMask = 0x00000fffffffffffull;
97static const uint64_t ShadowOffs[3] = { // Indexed by scale
98 0x0000130000000000ull,
99 0x0000220000000000ull,
100 0x0000440000000000ull,
101};
102// This array is indexed by the ToolType enum.
103static const int ShadowScale[] = {
104 0, // ESAN_None.
105 2, // ESAN_CacheFrag: 4B:1B, so 4 to 1 == >>2.
106 6, // ESAN_WorkingSet: 64B:1B, so 64 to 1 == >>6.
107};
108
Qin Zhao6d3bd682016-06-02 17:30:47 +0000109// MaxStructCounterNameSize is a soft size limit to avoid insanely long
110// names for those extremely large structs.
111static const unsigned MaxStructCounterNameSize = 512;
112
Derek Brueningd862c172016-04-21 21:30:22 +0000113namespace {
114
115static EfficiencySanitizerOptions
116OverrideOptionsFromCL(EfficiencySanitizerOptions Options) {
117 if (ClToolCacheFrag)
118 Options.ToolType = EfficiencySanitizerOptions::ESAN_CacheFrag;
Derek Bruening5662b932016-05-25 00:17:24 +0000119 else if (ClToolWorkingSet)
120 Options.ToolType = EfficiencySanitizerOptions::ESAN_WorkingSet;
Derek Brueningd862c172016-04-21 21:30:22 +0000121
122 // Direct opt invocation with no params will have the default ESAN_None.
123 // We run the default tool in that case.
124 if (Options.ToolType == EfficiencySanitizerOptions::ESAN_None)
125 Options.ToolType = EfficiencySanitizerOptions::ESAN_CacheFrag;
126
127 return Options;
128}
129
Qin Zhao1762eef2016-05-31 17:14:02 +0000130// Create a constant for Str so that we can pass it to the run-time lib.
131static GlobalVariable *createPrivateGlobalForString(Module &M, StringRef Str,
132 bool AllowMerging) {
133 Constant *StrConst = ConstantDataArray::getString(M.getContext(), Str);
134 // We use private linkage for module-local strings. If they can be merged
135 // with another one, we set the unnamed_addr attribute.
136 GlobalVariable *GV =
137 new GlobalVariable(M, StrConst->getType(), true,
138 GlobalValue::PrivateLinkage, StrConst, "");
139 if (AllowMerging)
140 GV->setUnnamedAddr(true);
141 GV->setAlignment(1); // Strings may not be merged w/o setting align 1.
142 return GV;
143}
144
Derek Brueningd862c172016-04-21 21:30:22 +0000145/// EfficiencySanitizer: instrument each module to find performance issues.
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000146class EfficiencySanitizer : public ModulePass {
Derek Brueningd862c172016-04-21 21:30:22 +0000147public:
148 EfficiencySanitizer(
149 const EfficiencySanitizerOptions &Opts = EfficiencySanitizerOptions())
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000150 : ModulePass(ID), Options(OverrideOptionsFromCL(Opts)) {}
Derek Brueningd862c172016-04-21 21:30:22 +0000151 const char *getPassName() const override;
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000152 bool runOnModule(Module &M) override;
Derek Brueningd862c172016-04-21 21:30:22 +0000153 static char ID;
154
155private:
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000156 bool initOnModule(Module &M);
Derek Brueningd862c172016-04-21 21:30:22 +0000157 void initializeCallbacks(Module &M);
Qin Zhao6d3bd682016-06-02 17:30:47 +0000158 bool shouldIgnoreStructType(StructType *StructTy);
159 void createStructCounterName(
160 StructType *StructTy, SmallString<MaxStructCounterNameSize> &NameStr);
Qin Zhao0b96aa72016-06-10 02:10:06 +0000161 GlobalVariable *createCacheFragInfoGV(Module &M, const DataLayout &DL,
162 Constant *UnitName);
163 Constant *createEsanInitToolInfoArg(Module &M, const DataLayout &DL);
Qin Zhao1762eef2016-05-31 17:14:02 +0000164 void createDestructor(Module &M, Constant *ToolInfoArg);
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000165 bool runOnFunction(Function &F, Module &M);
Derek Brueningd862c172016-04-21 21:30:22 +0000166 bool instrumentLoadOrStore(Instruction *I, const DataLayout &DL);
167 bool instrumentMemIntrinsic(MemIntrinsic *MI);
Qin Zhaoc14c2492016-06-03 02:33:04 +0000168 bool instrumentGetElementPtr(Instruction *I, Module &M);
Derek Brueningd862c172016-04-21 21:30:22 +0000169 bool shouldIgnoreMemoryAccess(Instruction *I);
170 int getMemoryAccessFuncIndex(Value *Addr, const DataLayout &DL);
Derek Bruening5662b932016-05-25 00:17:24 +0000171 Value *appToShadow(Value *Shadow, IRBuilder<> &IRB);
Derek Brueningd862c172016-04-21 21:30:22 +0000172 bool instrumentFastpath(Instruction *I, const DataLayout &DL, bool IsStore,
173 Value *Addr, unsigned Alignment);
174 // Each tool has its own fastpath routine:
175 bool instrumentFastpathCacheFrag(Instruction *I, const DataLayout &DL,
176 Value *Addr, unsigned Alignment);
Derek Bruening5662b932016-05-25 00:17:24 +0000177 bool instrumentFastpathWorkingSet(Instruction *I, const DataLayout &DL,
178 Value *Addr, unsigned Alignment);
Derek Brueningd862c172016-04-21 21:30:22 +0000179
180 EfficiencySanitizerOptions Options;
181 LLVMContext *Ctx;
182 Type *IntptrTy;
183 // Our slowpath involves callouts to the runtime library.
184 // Access sizes are powers of two: 1, 2, 4, 8, 16.
185 static const size_t NumberOfAccessSizes = 5;
186 Function *EsanAlignedLoad[NumberOfAccessSizes];
187 Function *EsanAlignedStore[NumberOfAccessSizes];
188 Function *EsanUnalignedLoad[NumberOfAccessSizes];
189 Function *EsanUnalignedStore[NumberOfAccessSizes];
190 // For irregular sizes of any alignment:
191 Function *EsanUnalignedLoadN, *EsanUnalignedStoreN;
192 Function *MemmoveFn, *MemcpyFn, *MemsetFn;
193 Function *EsanCtorFunction;
Derek Bruening0b872d92016-05-24 22:48:24 +0000194 Function *EsanDtorFunction;
Qin Zhaoc14c2492016-06-03 02:33:04 +0000195 // Remember the counter variable for each struct type to avoid
196 // recomputing the variable name later during instrumentation.
197 std::map<Type *, GlobalVariable *> StructTyMap;
Derek Brueningd862c172016-04-21 21:30:22 +0000198};
199} // namespace
200
201char EfficiencySanitizer::ID = 0;
202INITIALIZE_PASS(EfficiencySanitizer, "esan",
203 "EfficiencySanitizer: finds performance issues.", false, false)
204
205const char *EfficiencySanitizer::getPassName() const {
206 return "EfficiencySanitizer";
207}
208
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000209ModulePass *
Derek Brueningd862c172016-04-21 21:30:22 +0000210llvm::createEfficiencySanitizerPass(const EfficiencySanitizerOptions &Options) {
211 return new EfficiencySanitizer(Options);
212}
213
214void EfficiencySanitizer::initializeCallbacks(Module &M) {
215 IRBuilder<> IRB(M.getContext());
216 // Initialize the callbacks.
217 for (size_t Idx = 0; Idx < NumberOfAccessSizes; ++Idx) {
218 const unsigned ByteSize = 1U << Idx;
219 std::string ByteSizeStr = utostr(ByteSize);
220 // We'll inline the most common (i.e., aligned and frequent sizes)
221 // load + store instrumentation: these callouts are for the slowpath.
222 SmallString<32> AlignedLoadName("__esan_aligned_load" + ByteSizeStr);
223 EsanAlignedLoad[Idx] =
224 checkSanitizerInterfaceFunction(M.getOrInsertFunction(
225 AlignedLoadName, IRB.getVoidTy(), IRB.getInt8PtrTy(), nullptr));
226 SmallString<32> AlignedStoreName("__esan_aligned_store" + ByteSizeStr);
227 EsanAlignedStore[Idx] =
228 checkSanitizerInterfaceFunction(M.getOrInsertFunction(
229 AlignedStoreName, IRB.getVoidTy(), IRB.getInt8PtrTy(), nullptr));
230 SmallString<32> UnalignedLoadName("__esan_unaligned_load" + ByteSizeStr);
231 EsanUnalignedLoad[Idx] =
232 checkSanitizerInterfaceFunction(M.getOrInsertFunction(
233 UnalignedLoadName, IRB.getVoidTy(), IRB.getInt8PtrTy(), nullptr));
234 SmallString<32> UnalignedStoreName("__esan_unaligned_store" + ByteSizeStr);
235 EsanUnalignedStore[Idx] =
236 checkSanitizerInterfaceFunction(M.getOrInsertFunction(
237 UnalignedStoreName, IRB.getVoidTy(), IRB.getInt8PtrTy(), nullptr));
238 }
239 EsanUnalignedLoadN = checkSanitizerInterfaceFunction(
240 M.getOrInsertFunction("__esan_unaligned_loadN", IRB.getVoidTy(),
241 IRB.getInt8PtrTy(), IntptrTy, nullptr));
242 EsanUnalignedStoreN = checkSanitizerInterfaceFunction(
243 M.getOrInsertFunction("__esan_unaligned_storeN", IRB.getVoidTy(),
244 IRB.getInt8PtrTy(), IntptrTy, nullptr));
245 MemmoveFn = checkSanitizerInterfaceFunction(
246 M.getOrInsertFunction("memmove", IRB.getInt8PtrTy(), IRB.getInt8PtrTy(),
247 IRB.getInt8PtrTy(), IntptrTy, nullptr));
248 MemcpyFn = checkSanitizerInterfaceFunction(
249 M.getOrInsertFunction("memcpy", IRB.getInt8PtrTy(), IRB.getInt8PtrTy(),
250 IRB.getInt8PtrTy(), IntptrTy, nullptr));
251 MemsetFn = checkSanitizerInterfaceFunction(
252 M.getOrInsertFunction("memset", IRB.getInt8PtrTy(), IRB.getInt8PtrTy(),
253 IRB.getInt32Ty(), IntptrTy, nullptr));
254}
255
Qin Zhao6d3bd682016-06-02 17:30:47 +0000256bool EfficiencySanitizer::shouldIgnoreStructType(StructType *StructTy) {
257 if (StructTy == nullptr || StructTy->isOpaque() /* no struct body */)
258 return true;
259 return false;
260}
261
262void EfficiencySanitizer::createStructCounterName(
263 StructType *StructTy, SmallString<MaxStructCounterNameSize> &NameStr) {
264 // Append NumFields and field type ids to avoid struct conflicts
265 // with the same name but different fields.
266 if (StructTy->hasName())
267 NameStr += StructTy->getName();
268 else
269 NameStr += "struct.anon";
270 // We allow the actual size of the StructCounterName to be larger than
271 // MaxStructCounterNameSize and append #NumFields and at least one
272 // field type id.
273 // Append #NumFields.
274 NameStr += "#";
275 Twine(StructTy->getNumElements()).toVector(NameStr);
276 // Append struct field type ids in the reverse order.
277 for (int i = StructTy->getNumElements() - 1; i >= 0; --i) {
278 NameStr += "#";
279 Twine(StructTy->getElementType(i)->getTypeID()).toVector(NameStr);
280 if (NameStr.size() >= MaxStructCounterNameSize)
281 break;
282 }
283 if (StructTy->isLiteral()) {
284 // End with # for literal struct.
285 NameStr += "#";
286 }
287}
288
Qin Zhao1762eef2016-05-31 17:14:02 +0000289// Create the global variable for the cache-fragmentation tool.
290GlobalVariable *EfficiencySanitizer::createCacheFragInfoGV(
Qin Zhao0b96aa72016-06-10 02:10:06 +0000291 Module &M, const DataLayout &DL, Constant *UnitName) {
Qin Zhao1762eef2016-05-31 17:14:02 +0000292 assert(Options.ToolType == EfficiencySanitizerOptions::ESAN_CacheFrag);
293
294 auto *Int8PtrTy = Type::getInt8PtrTy(*Ctx);
295 auto *Int8PtrPtrTy = Int8PtrTy->getPointerTo();
296 auto *Int32Ty = Type::getInt32Ty(*Ctx);
Qin Zhao0b96aa72016-06-10 02:10:06 +0000297 auto *Int32PtrTy = Type::getInt32PtrTy(*Ctx);
Qin Zhao6d3bd682016-06-02 17:30:47 +0000298 auto *Int64Ty = Type::getInt64Ty(*Ctx);
Qin Zhao1762eef2016-05-31 17:14:02 +0000299 auto *Int64PtrTy = Type::getInt64PtrTy(*Ctx);
300 // This structure should be kept consistent with the StructInfo struct
301 // in the runtime library.
302 // struct StructInfo {
303 // const char *StructName;
Qin Zhao0b96aa72016-06-10 02:10:06 +0000304 // u32 Size;
Qin Zhao6d3bd682016-06-02 17:30:47 +0000305 // u32 NumFields;
Qin Zhao0b96aa72016-06-10 02:10:06 +0000306 // u32 *FieldOffsets;
Qin Zhao1762eef2016-05-31 17:14:02 +0000307 // u64 *FieldCounters;
308 // const char **FieldTypeNames;
309 // };
310 auto *StructInfoTy =
Qin Zhao0b96aa72016-06-10 02:10:06 +0000311 StructType::get(Int8PtrTy, Int32Ty, Int32Ty, Int32PtrTy, Int64PtrTy,
312 Int8PtrPtrTy, nullptr);
Qin Zhao1762eef2016-05-31 17:14:02 +0000313 auto *StructInfoPtrTy = StructInfoTy->getPointerTo();
314 // This structure should be kept consistent with the CacheFragInfo struct
315 // in the runtime library.
316 // struct CacheFragInfo {
317 // const char *UnitName;
Qin Zhao6d3bd682016-06-02 17:30:47 +0000318 // u32 NumStructs;
Qin Zhao1762eef2016-05-31 17:14:02 +0000319 // StructInfo *Structs;
320 // };
321 auto *CacheFragInfoTy =
322 StructType::get(Int8PtrTy, Int32Ty, StructInfoPtrTy, nullptr);
323
324 std::vector<StructType *> Vec = M.getIdentifiedStructTypes();
Qin Zhao6d3bd682016-06-02 17:30:47 +0000325 unsigned NumStructs = 0;
326 SmallVector<Constant *, 16> Initializers;
327
328 for (auto &StructTy : Vec) {
329 if (shouldIgnoreStructType(StructTy)) {
330 ++NumIgnoredStructs;
331 continue;
332 }
333 ++NumStructs;
334
335 // StructName.
336 SmallString<MaxStructCounterNameSize> CounterNameStr;
337 createStructCounterName(StructTy, CounterNameStr);
338 GlobalVariable *StructCounterName = createPrivateGlobalForString(
339 M, CounterNameStr, /*AllowMerging*/true);
340
341 // FieldCounters.
342 // We create the counter array with StructCounterName and weak linkage
343 // so that the structs with the same name and layout from different
344 // compilation units will be merged into one.
345 auto *CounterArrayTy = ArrayType::get(Int64Ty, StructTy->getNumElements());
346 GlobalVariable *Counters =
347 new GlobalVariable(M, CounterArrayTy, false,
348 GlobalVariable::WeakAnyLinkage,
349 ConstantAggregateZero::get(CounterArrayTy),
350 CounterNameStr);
351
Qin Zhaoc14c2492016-06-03 02:33:04 +0000352 // Remember the counter variable for each struct type.
353 StructTyMap.insert(std::pair<Type *, GlobalVariable *>(StructTy, Counters));
354
Qin Zhao0b96aa72016-06-10 02:10:06 +0000355 // We pass the field type name array and offset array to the runtime for
356 // better reporting.
Qin Zhao6d3bd682016-06-02 17:30:47 +0000357 // FieldTypeNames.
Qin Zhao6d3bd682016-06-02 17:30:47 +0000358 auto *TypeNameArrayTy = ArrayType::get(Int8PtrTy, StructTy->getNumElements());
Qin Zhao0b96aa72016-06-10 02:10:06 +0000359 GlobalVariable *TypeNames =
Qin Zhao6d3bd682016-06-02 17:30:47 +0000360 new GlobalVariable(M, TypeNameArrayTy, true,
361 GlobalVariable::InternalLinkage, nullptr);
362 SmallVector<Constant *, 16> TypeNameVec;
Qin Zhao0b96aa72016-06-10 02:10:06 +0000363 // FieldOffsets.
364 const StructLayout *SL = DL.getStructLayout(StructTy);
365 auto *OffsetArrayTy = ArrayType::get(Int32Ty, StructTy->getNumElements());
366 GlobalVariable *Offsets =
367 new GlobalVariable(M, OffsetArrayTy, true,
368 GlobalVariable::InternalLinkage, nullptr);
369 SmallVector<Constant *, 16> OffsetVec;
Qin Zhao6d3bd682016-06-02 17:30:47 +0000370 for (unsigned i = 0; i < StructTy->getNumElements(); ++i) {
371 Type *Ty = StructTy->getElementType(i);
372 std::string Str;
373 raw_string_ostream StrOS(Str);
374 Ty->print(StrOS);
375 TypeNameVec.push_back(
376 ConstantExpr::getPointerCast(
377 createPrivateGlobalForString(M, StrOS.str(), true),
378 Int8PtrTy));
Qin Zhao0b96aa72016-06-10 02:10:06 +0000379 OffsetVec.push_back(ConstantInt::get(Int32Ty, SL->getElementOffset(i)));
Qin Zhao6d3bd682016-06-02 17:30:47 +0000380 }
Qin Zhao0b96aa72016-06-10 02:10:06 +0000381 TypeNames->setInitializer(ConstantArray::get(TypeNameArrayTy, TypeNameVec));
382 Offsets->setInitializer(ConstantArray::get(OffsetArrayTy, OffsetVec));
Qin Zhao6d3bd682016-06-02 17:30:47 +0000383
384 Initializers.push_back(
385 ConstantStruct::get(
386 StructInfoTy,
387 ConstantExpr::getPointerCast(StructCounterName, Int8PtrTy),
Qin Zhao0b96aa72016-06-10 02:10:06 +0000388 ConstantInt::get(Int32Ty, SL->getSizeInBytes()),
Qin Zhao6d3bd682016-06-02 17:30:47 +0000389 ConstantInt::get(Int32Ty, StructTy->getNumElements()),
Qin Zhao0b96aa72016-06-10 02:10:06 +0000390 ConstantExpr::getPointerCast(Offsets, Int32PtrTy),
Qin Zhao6d3bd682016-06-02 17:30:47 +0000391 ConstantExpr::getPointerCast(Counters, Int64PtrTy),
Qin Zhao0b96aa72016-06-10 02:10:06 +0000392 ConstantExpr::getPointerCast(TypeNames, Int8PtrPtrTy),
Qin Zhao6d3bd682016-06-02 17:30:47 +0000393 nullptr));
394 }
395 // Structs.
396 Constant *StructInfo;
397 if (NumStructs == 0) {
398 StructInfo = ConstantPointerNull::get(StructInfoPtrTy);
399 } else {
400 auto *StructInfoArrayTy = ArrayType::get(StructInfoTy, NumStructs);
401 StructInfo = ConstantExpr::getPointerCast(
402 new GlobalVariable(M, StructInfoArrayTy, false,
403 GlobalVariable::InternalLinkage,
404 ConstantArray::get(StructInfoArrayTy, Initializers)),
405 StructInfoPtrTy);
406 }
407
Qin Zhao1762eef2016-05-31 17:14:02 +0000408 auto *CacheFragInfoGV = new GlobalVariable(
409 M, CacheFragInfoTy, true, GlobalVariable::InternalLinkage,
410 ConstantStruct::get(CacheFragInfoTy,
411 UnitName,
Qin Zhao6d3bd682016-06-02 17:30:47 +0000412 ConstantInt::get(Int32Ty, NumStructs),
413 StructInfo,
Qin Zhao1762eef2016-05-31 17:14:02 +0000414 nullptr));
415 return CacheFragInfoGV;
Derek Bruening0b872d92016-05-24 22:48:24 +0000416}
417
Qin Zhao1762eef2016-05-31 17:14:02 +0000418// Create the tool-specific argument passed to EsanInit and EsanExit.
Qin Zhao0b96aa72016-06-10 02:10:06 +0000419Constant *EfficiencySanitizer::createEsanInitToolInfoArg(Module &M,
420 const DataLayout &DL) {
Qin Zhao1762eef2016-05-31 17:14:02 +0000421 // This structure contains tool-specific information about each compilation
422 // unit (module) and is passed to the runtime library.
423 GlobalVariable *ToolInfoGV = nullptr;
424
425 auto *Int8PtrTy = Type::getInt8PtrTy(*Ctx);
426 // Compilation unit name.
427 auto *UnitName = ConstantExpr::getPointerCast(
428 createPrivateGlobalForString(M, M.getModuleIdentifier(), true),
429 Int8PtrTy);
430
431 // Create the tool-specific variable.
432 if (Options.ToolType == EfficiencySanitizerOptions::ESAN_CacheFrag)
Qin Zhao0b96aa72016-06-10 02:10:06 +0000433 ToolInfoGV = createCacheFragInfoGV(M, DL, UnitName);
Qin Zhao1762eef2016-05-31 17:14:02 +0000434
435 if (ToolInfoGV != nullptr)
436 return ConstantExpr::getPointerCast(ToolInfoGV, Int8PtrTy);
437
438 // Create the null pointer if no tool-specific variable created.
439 return ConstantPointerNull::get(Int8PtrTy);
440}
441
442void EfficiencySanitizer::createDestructor(Module &M, Constant *ToolInfoArg) {
443 PointerType *Int8PtrTy = Type::getInt8PtrTy(*Ctx);
Derek Bruening0b872d92016-05-24 22:48:24 +0000444 EsanDtorFunction = Function::Create(FunctionType::get(Type::getVoidTy(*Ctx),
445 false),
446 GlobalValue::InternalLinkage,
447 EsanModuleDtorName, &M);
448 ReturnInst::Create(*Ctx, BasicBlock::Create(*Ctx, "", EsanDtorFunction));
449 IRBuilder<> IRB_Dtor(EsanDtorFunction->getEntryBlock().getTerminator());
450 Function *EsanExit = checkSanitizerInterfaceFunction(
451 M.getOrInsertFunction(EsanExitName, IRB_Dtor.getVoidTy(),
Qin Zhao1762eef2016-05-31 17:14:02 +0000452 Int8PtrTy, nullptr));
Derek Bruening0b872d92016-05-24 22:48:24 +0000453 EsanExit->setLinkage(Function::ExternalLinkage);
Qin Zhao1762eef2016-05-31 17:14:02 +0000454 IRB_Dtor.CreateCall(EsanExit, {ToolInfoArg});
Derek Bruening0b872d92016-05-24 22:48:24 +0000455 appendToGlobalDtors(M, EsanDtorFunction, EsanCtorAndDtorPriority);
456}
457
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000458bool EfficiencySanitizer::initOnModule(Module &M) {
Derek Brueningd862c172016-04-21 21:30:22 +0000459 Ctx = &M.getContext();
460 const DataLayout &DL = M.getDataLayout();
461 IRBuilder<> IRB(M.getContext());
462 IntegerType *OrdTy = IRB.getInt32Ty();
Qin Zhao1762eef2016-05-31 17:14:02 +0000463 PointerType *Int8PtrTy = Type::getInt8PtrTy(*Ctx);
Derek Brueningd862c172016-04-21 21:30:22 +0000464 IntptrTy = DL.getIntPtrType(M.getContext());
Derek Bruening0b872d92016-05-24 22:48:24 +0000465 // Create the variable passed to EsanInit and EsanExit.
Qin Zhao0b96aa72016-06-10 02:10:06 +0000466 Constant *ToolInfoArg = createEsanInitToolInfoArg(M, DL);
Derek Bruening0b872d92016-05-24 22:48:24 +0000467 // Constructor
Derek Bruening4252a162016-06-03 19:40:37 +0000468 // We specify the tool type both in the EsanWhichToolName global
469 // and as an arg to the init routine as a sanity check.
Derek Brueningd862c172016-04-21 21:30:22 +0000470 std::tie(EsanCtorFunction, std::ignore) = createSanitizerCtorAndInitFunctions(
Qin Zhao1762eef2016-05-31 17:14:02 +0000471 M, EsanModuleCtorName, EsanInitName, /*InitArgTypes=*/{OrdTy, Int8PtrTy},
Derek Brueningd862c172016-04-21 21:30:22 +0000472 /*InitArgs=*/{
Derek Bruening0b872d92016-05-24 22:48:24 +0000473 ConstantInt::get(OrdTy, static_cast<int>(Options.ToolType)),
Qin Zhao1762eef2016-05-31 17:14:02 +0000474 ToolInfoArg});
Derek Bruening0b872d92016-05-24 22:48:24 +0000475 appendToGlobalCtors(M, EsanCtorFunction, EsanCtorAndDtorPriority);
Derek Brueningd862c172016-04-21 21:30:22 +0000476
Qin Zhao1762eef2016-05-31 17:14:02 +0000477 createDestructor(M, ToolInfoArg);
Derek Bruening4252a162016-06-03 19:40:37 +0000478
479 new GlobalVariable(M, OrdTy, true,
480 GlobalValue::WeakAnyLinkage,
481 ConstantInt::get(OrdTy,
482 static_cast<int>(Options.ToolType)),
483 EsanWhichToolName);
484
Derek Brueningd862c172016-04-21 21:30:22 +0000485 return true;
486}
487
Derek Bruening5662b932016-05-25 00:17:24 +0000488Value *EfficiencySanitizer::appToShadow(Value *Shadow, IRBuilder<> &IRB) {
489 // Shadow = ((App & Mask) + Offs) >> Scale
490 Shadow = IRB.CreateAnd(Shadow, ConstantInt::get(IntptrTy, ShadowMask));
491 uint64_t Offs;
492 int Scale = ShadowScale[Options.ToolType];
493 if (Scale <= 2)
494 Offs = ShadowOffs[Scale];
495 else
496 Offs = ShadowOffs[0] << Scale;
497 Shadow = IRB.CreateAdd(Shadow, ConstantInt::get(IntptrTy, Offs));
498 if (Scale > 0)
499 Shadow = IRB.CreateLShr(Shadow, Scale);
500 return Shadow;
501}
502
Derek Brueningd862c172016-04-21 21:30:22 +0000503bool EfficiencySanitizer::shouldIgnoreMemoryAccess(Instruction *I) {
504 if (Options.ToolType == EfficiencySanitizerOptions::ESAN_CacheFrag) {
505 // We'd like to know about cache fragmentation in vtable accesses and
506 // constant data references, so we do not currently ignore anything.
507 return false;
Derek Bruening5662b932016-05-25 00:17:24 +0000508 } else if (Options.ToolType == EfficiencySanitizerOptions::ESAN_WorkingSet) {
509 // TODO: the instrumentation disturbs the data layout on the stack, so we
510 // may want to add an option to ignore stack references (if we can
511 // distinguish them) to reduce overhead.
Derek Brueningd862c172016-04-21 21:30:22 +0000512 }
513 // TODO(bruening): future tools will be returning true for some cases.
514 return false;
515}
516
Derek Brueningbc0a68e2016-05-20 20:00:05 +0000517bool EfficiencySanitizer::runOnModule(Module &M) {
518 bool Res = initOnModule(M);
519 initializeCallbacks(M);
520 for (auto &F : M) {
521 Res |= runOnFunction(F, M);
522 }
523 return Res;
524}
525
526bool EfficiencySanitizer::runOnFunction(Function &F, Module &M) {
Derek Brueningd862c172016-04-21 21:30:22 +0000527 // This is required to prevent instrumenting the call to __esan_init from
528 // within the module constructor.
529 if (&F == EsanCtorFunction)
530 return false;
Derek Brueningd862c172016-04-21 21:30:22 +0000531 SmallVector<Instruction *, 8> LoadsAndStores;
532 SmallVector<Instruction *, 8> MemIntrinCalls;
Qin Zhaoc14c2492016-06-03 02:33:04 +0000533 SmallVector<Instruction *, 8> GetElementPtrs;
Derek Brueningd862c172016-04-21 21:30:22 +0000534 bool Res = false;
Derek Bruening0b872d92016-05-24 22:48:24 +0000535 const DataLayout &DL = M.getDataLayout();
Derek Brueningd862c172016-04-21 21:30:22 +0000536
537 for (auto &BB : F) {
538 for (auto &Inst : BB) {
539 if ((isa<LoadInst>(Inst) || isa<StoreInst>(Inst) ||
540 isa<AtomicRMWInst>(Inst) || isa<AtomicCmpXchgInst>(Inst)) &&
541 !shouldIgnoreMemoryAccess(&Inst))
542 LoadsAndStores.push_back(&Inst);
543 else if (isa<MemIntrinsic>(Inst))
544 MemIntrinCalls.push_back(&Inst);
Qin Zhaoc14c2492016-06-03 02:33:04 +0000545 else if (isa<GetElementPtrInst>(Inst))
546 GetElementPtrs.push_back(&Inst);
Derek Brueningd862c172016-04-21 21:30:22 +0000547 }
548 }
549
550 if (ClInstrumentLoadsAndStores) {
551 for (auto Inst : LoadsAndStores) {
552 Res |= instrumentLoadOrStore(Inst, DL);
553 }
554 }
555
556 if (ClInstrumentMemIntrinsics) {
557 for (auto Inst : MemIntrinCalls) {
558 Res |= instrumentMemIntrinsic(cast<MemIntrinsic>(Inst));
559 }
560 }
561
Qin Zhaoc14c2492016-06-03 02:33:04 +0000562 if (Options.ToolType == EfficiencySanitizerOptions::ESAN_CacheFrag) {
563 for (auto Inst : GetElementPtrs) {
564 Res |= instrumentGetElementPtr(Inst, M);
565 }
566 }
567
Derek Brueningd862c172016-04-21 21:30:22 +0000568 return Res;
569}
570
571bool EfficiencySanitizer::instrumentLoadOrStore(Instruction *I,
572 const DataLayout &DL) {
573 IRBuilder<> IRB(I);
574 bool IsStore;
575 Value *Addr;
576 unsigned Alignment;
577 if (LoadInst *Load = dyn_cast<LoadInst>(I)) {
578 IsStore = false;
579 Alignment = Load->getAlignment();
580 Addr = Load->getPointerOperand();
581 } else if (StoreInst *Store = dyn_cast<StoreInst>(I)) {
582 IsStore = true;
583 Alignment = Store->getAlignment();
584 Addr = Store->getPointerOperand();
585 } else if (AtomicRMWInst *RMW = dyn_cast<AtomicRMWInst>(I)) {
586 IsStore = true;
587 Alignment = 0;
588 Addr = RMW->getPointerOperand();
589 } else if (AtomicCmpXchgInst *Xchg = dyn_cast<AtomicCmpXchgInst>(I)) {
590 IsStore = true;
591 Alignment = 0;
592 Addr = Xchg->getPointerOperand();
593 } else
594 llvm_unreachable("Unsupported mem access type");
595
596 Type *OrigTy = cast<PointerType>(Addr->getType())->getElementType();
597 const uint32_t TypeSizeBytes = DL.getTypeStoreSizeInBits(OrigTy) / 8;
598 Value *OnAccessFunc = nullptr;
Derek Bruening5662b932016-05-25 00:17:24 +0000599
600 // Convert 0 to the default alignment.
601 if (Alignment == 0)
602 Alignment = DL.getPrefTypeAlignment(OrigTy);
603
Derek Brueningd862c172016-04-21 21:30:22 +0000604 if (IsStore)
605 NumInstrumentedStores++;
606 else
607 NumInstrumentedLoads++;
608 int Idx = getMemoryAccessFuncIndex(Addr, DL);
609 if (Idx < 0) {
610 OnAccessFunc = IsStore ? EsanUnalignedStoreN : EsanUnalignedLoadN;
611 IRB.CreateCall(OnAccessFunc,
612 {IRB.CreatePointerCast(Addr, IRB.getInt8PtrTy()),
613 ConstantInt::get(IntptrTy, TypeSizeBytes)});
614 } else {
Qin Zhaod677d882016-06-10 00:48:53 +0000615 if (ClInstrumentFastpath &&
616 instrumentFastpath(I, DL, IsStore, Addr, Alignment)) {
Derek Brueningd862c172016-04-21 21:30:22 +0000617 NumFastpaths++;
618 return true;
619 }
620 if (Alignment == 0 || Alignment >= 8 || (Alignment % TypeSizeBytes) == 0)
621 OnAccessFunc = IsStore ? EsanAlignedStore[Idx] : EsanAlignedLoad[Idx];
622 else
623 OnAccessFunc = IsStore ? EsanUnalignedStore[Idx] : EsanUnalignedLoad[Idx];
624 IRB.CreateCall(OnAccessFunc,
625 IRB.CreatePointerCast(Addr, IRB.getInt8PtrTy()));
626 }
627 return true;
628}
629
630// It's simplest to replace the memset/memmove/memcpy intrinsics with
631// calls that the runtime library intercepts.
632// Our pass is late enough that calls should not turn back into intrinsics.
633bool EfficiencySanitizer::instrumentMemIntrinsic(MemIntrinsic *MI) {
634 IRBuilder<> IRB(MI);
635 bool Res = false;
636 if (isa<MemSetInst>(MI)) {
637 IRB.CreateCall(
638 MemsetFn,
639 {IRB.CreatePointerCast(MI->getArgOperand(0), IRB.getInt8PtrTy()),
640 IRB.CreateIntCast(MI->getArgOperand(1), IRB.getInt32Ty(), false),
641 IRB.CreateIntCast(MI->getArgOperand(2), IntptrTy, false)});
642 MI->eraseFromParent();
643 Res = true;
644 } else if (isa<MemTransferInst>(MI)) {
645 IRB.CreateCall(
646 isa<MemCpyInst>(MI) ? MemcpyFn : MemmoveFn,
647 {IRB.CreatePointerCast(MI->getArgOperand(0), IRB.getInt8PtrTy()),
648 IRB.CreatePointerCast(MI->getArgOperand(1), IRB.getInt8PtrTy()),
649 IRB.CreateIntCast(MI->getArgOperand(2), IntptrTy, false)});
650 MI->eraseFromParent();
651 Res = true;
652 } else
653 llvm_unreachable("Unsupported mem intrinsic type");
654 return Res;
655}
656
Qin Zhaoc14c2492016-06-03 02:33:04 +0000657bool EfficiencySanitizer::instrumentGetElementPtr(Instruction *I, Module &M) {
658 GetElementPtrInst *GepInst = dyn_cast<GetElementPtrInst>(I);
Qin Zhaobc8fbea2016-06-10 22:28:55 +0000659 bool Res = false;
660 if (GepInst == nullptr || GepInst->getNumIndices() == 1) {
Qin Zhaoc14c2492016-06-03 02:33:04 +0000661 ++NumIgnoredGEPs;
662 return false;
663 }
Qin Zhaobc8fbea2016-06-10 22:28:55 +0000664 Type *SourceTy = GepInst->getSourceElementType();
665 // Iterate all (except the first and the last) idx within each GEP instruction
666 // for possible nested struct field address calculation.
667 for (unsigned i = 1; i < GepInst->getNumIndices(); ++i) {
668 SmallVector<Value *, 8> IdxVec(GepInst->idx_begin(),
669 GepInst->idx_begin() + i);
670 StructType *StructTy = dyn_cast<StructType>(
671 GetElementPtrInst::getIndexedType(SourceTy, IdxVec));
672 if (StructTy == nullptr || shouldIgnoreStructType(StructTy) ||
673 StructTyMap.count(StructTy) == 0)
674 continue;
675 // Get the StructTy's subfield index.
676 ConstantInt *Idx = dyn_cast<ConstantInt>(GepInst->getOperand(i+1));
677 if (Idx == nullptr || Idx->getZExtValue() > StructTy->getNumElements())
678 continue;
679 GlobalVariable *CounterArray = StructTyMap[StructTy];
680 if (CounterArray == nullptr)
681 return false;
682 IRBuilder<> IRB(I);
683 Constant *Indices[2];
684 // Xref http://llvm.org/docs/LangRef.html#i-getelementptr and
685 // http://llvm.org/docs/GetElementPtr.html.
686 // The first index of the GEP instruction steps through the first operand,
687 // i.e., the array itself.
688 Indices[0] = ConstantInt::get(IRB.getInt32Ty(), 0);
689 // The second index is the index within the array.
690 Indices[1] = ConstantInt::get(IRB.getInt32Ty(), Idx->getZExtValue());
691 Constant *Counter =
692 ConstantExpr::getGetElementPtr(
693 ArrayType::get(IRB.getInt64Ty(), StructTy->getNumElements()),
694 CounterArray, Indices);
695 Value *Load = IRB.CreateLoad(Counter);
696 IRB.CreateStore(IRB.CreateAdd(Load, ConstantInt::get(IRB.getInt64Ty(), 1)),
697 Counter);
698 Res = true;
Qin Zhaoc14c2492016-06-03 02:33:04 +0000699 }
Qin Zhaobc8fbea2016-06-10 22:28:55 +0000700 if (Res)
701 ++NumInstrumentedGEPs;
702 else
703 ++NumIgnoredGEPs;
704 return Res;
Qin Zhaoc14c2492016-06-03 02:33:04 +0000705}
706
Derek Brueningd862c172016-04-21 21:30:22 +0000707int EfficiencySanitizer::getMemoryAccessFuncIndex(Value *Addr,
708 const DataLayout &DL) {
709 Type *OrigPtrTy = Addr->getType();
710 Type *OrigTy = cast<PointerType>(OrigPtrTy)->getElementType();
711 assert(OrigTy->isSized());
712 // The size is always a multiple of 8.
713 uint32_t TypeSizeBytes = DL.getTypeStoreSizeInBits(OrigTy) / 8;
714 if (TypeSizeBytes != 1 && TypeSizeBytes != 2 && TypeSizeBytes != 4 &&
715 TypeSizeBytes != 8 && TypeSizeBytes != 16) {
716 // Irregular sizes do not have per-size call targets.
717 NumAccessesWithIrregularSize++;
718 return -1;
719 }
720 size_t Idx = countTrailingZeros(TypeSizeBytes);
721 assert(Idx < NumberOfAccessSizes);
722 return Idx;
723}
724
725bool EfficiencySanitizer::instrumentFastpath(Instruction *I,
726 const DataLayout &DL, bool IsStore,
727 Value *Addr, unsigned Alignment) {
728 if (Options.ToolType == EfficiencySanitizerOptions::ESAN_CacheFrag) {
729 return instrumentFastpathCacheFrag(I, DL, Addr, Alignment);
Derek Bruening5662b932016-05-25 00:17:24 +0000730 } else if (Options.ToolType == EfficiencySanitizerOptions::ESAN_WorkingSet) {
731 return instrumentFastpathWorkingSet(I, DL, Addr, Alignment);
Derek Brueningd862c172016-04-21 21:30:22 +0000732 }
733 return false;
734}
735
736bool EfficiencySanitizer::instrumentFastpathCacheFrag(Instruction *I,
737 const DataLayout &DL,
738 Value *Addr,
739 unsigned Alignment) {
Qin Zhaod677d882016-06-10 00:48:53 +0000740 // Do nothing.
741 return true; // Return true to avoid slowpath instrumentation.
Derek Brueningd862c172016-04-21 21:30:22 +0000742}
Derek Bruening5662b932016-05-25 00:17:24 +0000743
744bool EfficiencySanitizer::instrumentFastpathWorkingSet(
745 Instruction *I, const DataLayout &DL, Value *Addr, unsigned Alignment) {
746 assert(ShadowScale[Options.ToolType] == 6); // The code below assumes this
747 IRBuilder<> IRB(I);
748 Type *OrigTy = cast<PointerType>(Addr->getType())->getElementType();
749 const uint32_t TypeSize = DL.getTypeStoreSizeInBits(OrigTy);
750 // Bail to the slowpath if the access might touch multiple cache lines.
751 // An access aligned to its size is guaranteed to be intra-cache-line.
752 // getMemoryAccessFuncIndex has already ruled out a size larger than 16
753 // and thus larger than a cache line for platforms this tool targets
754 // (and our shadow memory setup assumes 64-byte cache lines).
755 assert(TypeSize <= 64);
756 if (!(TypeSize == 8 ||
Derek Bruening9ef57722016-06-03 22:29:52 +0000757 (Alignment % (TypeSize / 8)) == 0)) {
758 if (ClAssumeIntraCacheLine)
759 ++NumAssumedIntraCacheLine;
760 else
761 return false;
762 }
Derek Bruening5662b932016-05-25 00:17:24 +0000763
764 // We inline instrumentation to set the corresponding shadow bits for
765 // each cache line touched by the application. Here we handle a single
766 // load or store where we've already ruled out the possibility that it
767 // might touch more than one cache line and thus we simply update the
768 // shadow memory for a single cache line.
769 // Our shadow memory model is fine with races when manipulating shadow values.
770 // We generate the following code:
771 //
772 // const char BitMask = 0x81;
773 // char *ShadowAddr = appToShadow(AppAddr);
774 // if ((*ShadowAddr & BitMask) != BitMask)
775 // *ShadowAddr |= Bitmask;
776 //
777 Value *AddrPtr = IRB.CreatePointerCast(Addr, IntptrTy);
778 Value *ShadowPtr = appToShadow(AddrPtr, IRB);
779 Type *ShadowTy = IntegerType::get(*Ctx, 8U);
780 Type *ShadowPtrTy = PointerType::get(ShadowTy, 0);
781 // The bottom bit is used for the current sampling period's working set.
782 // The top bit is used for the total working set. We set both on each
783 // memory access, if they are not already set.
784 Value *ValueMask = ConstantInt::get(ShadowTy, 0x81); // 10000001B
785
786 Value *OldValue = IRB.CreateLoad(IRB.CreateIntToPtr(ShadowPtr, ShadowPtrTy));
787 // The AND and CMP will be turned into a TEST instruction by the compiler.
788 Value *Cmp = IRB.CreateICmpNE(IRB.CreateAnd(OldValue, ValueMask), ValueMask);
789 TerminatorInst *CmpTerm = SplitBlockAndInsertIfThen(Cmp, I, false);
790 // FIXME: do I need to call SetCurrentDebugLocation?
791 IRB.SetInsertPoint(CmpTerm);
792 // We use OR to set the shadow bits to avoid corrupting the middle 6 bits,
793 // which are used by the runtime library.
794 Value *NewVal = IRB.CreateOr(OldValue, ValueMask);
795 IRB.CreateStore(NewVal, IRB.CreateIntToPtr(ShadowPtr, ShadowPtrTy));
796 IRB.SetInsertPoint(I);
797
798 return true;
799}