Index: lib/Transforms/Instrumentation/EfficiencySanitizer.cpp =================================================================== --- lib/Transforms/Instrumentation/EfficiencySanitizer.cpp +++ lib/Transforms/Instrumentation/EfficiencySanitizer.cpp @@ -30,6 +30,7 @@ #include "llvm/IR/Type.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Transforms/Utils/ModuleUtils.h" @@ -85,6 +86,8 @@ 6, // ESAN_WorkingSet: 64B:1B, so 64 to 1 == >>6. }; +static const unsigned MaxStructCounterNameSize = 512; + namespace { static EfficiencySanitizerOptions @@ -99,9 +102,29 @@ if (Options.ToolType == EfficiencySanitizerOptions::ESAN_None) Options.ToolType = EfficiencySanitizerOptions::ESAN_CacheFrag; + if (Options.ToolType == EfficiencySanitizerOptions::ESAN_CacheFrag) { + // FIXME: re-enable load/store instrumentation for the shadow based + // CacheFrag tool. + ClInstrumentLoadsAndStores = false; + } return Options; } +// Create a constant for Str so that we can pass it to the run-time lib. +static GlobalVariable *createPrivateGlobalForString(Module &M, StringRef Str, + bool AllowMerging) { + Constant *StrConst = ConstantDataArray::getString(M.getContext(), Str); + // We use private linkage for module-local strings. If they can be merged + // with another one, we set the unnamed_addr attribute. + GlobalVariable *GV = + new GlobalVariable(M, StrConst->getType(), true, + GlobalValue::PrivateLinkage, StrConst, ""); + if (AllowMerging) + GV->setUnnamedAddr(true); + GV->setAlignment(1); // Strings may not be merged w/o setting align 1. + return GV; +} + /// EfficiencySanitizer: instrument each module to find performance issues. class EfficiencySanitizer : public ModulePass { public: @@ -115,6 +138,10 @@ private: bool initOnModule(Module &M); void initializeCallbacks(Module &M); + bool shouldIgnoreStructType(StructType *StructTy); + StringRef createStructCounterName( + StructType *StructTy, SmallString &NameStr); + void createCacheFragGV(Module &M, SmallVector &ToolInfoVec); GlobalVariable *createEsanInitToolGV(Module &M); void createDestructor(Module &M, GlobalVariable *GV); bool runOnFunction(Function &F, Module &M); @@ -204,15 +231,158 @@ IRB.getInt32Ty(), IntptrTy, nullptr)); } +bool EfficiencySanitizer::shouldIgnoreStructType(StructType *StructTy) { + if (StructTy == nullptr || StructTy->isOpaque() || StructTy->isLiteral()) + return true; + // Ignore union or struct with only 1 field. + if (StructTy->getNumElements() <= 1) + return true; + return false; +} + +StringRef EfficiencySanitizer::createStructCounterName( + StructType *StructTy, SmallString &NameStr) { + // Append NumOfFields and field type ids to avoid struct conflicts + // with the same name but different fields. + if (StructTy->hasName()) + NameStr += StructTy->getName(); + else + NameStr += "struct.anon"; + // Append #NumOfFields + NameStr += "#"; + Twine(StructTy->getNumElements()).toVector(NameStr); + // Append struct field type ids in the reverse order. + for (int i = StructTy->getNumElements() - 1; i >= 0; --i) { + NameStr += "#"; + Twine(StructTy->getElementType(i)->getTypeID()).toVector(NameStr); + // MaxStructCounterNameSize is a soft size limit. + // We allow the actual size to be larger and append at least + // one field type id. + if (NameStr.size() >= MaxStructCounterNameSize) + break; + } + return StringRef(NameStr); +} + +// Create the global variable for the cache-fragmentation tool. +void EfficiencySanitizer::createCacheFragGV( + Module &M, SmallVector &ToolInfoVec) { + assert(Options.ToolType == EfficiencySanitizerOptions::ESAN_CacheFrag); + // This structure should be kept consistent with the tool-specific struct + // in the runtime library. + // struct ToolInfo { + // const char *UnitName; + // // Expanding for the cache-fragmentation tool. + // unsigned NumOfStructs; + // struct StructInfoTy { + // const char *StructName; + // unsigned NumOfFields; + // uint64_t *Counters; + // const char **TypeName; + // } *Structs; + // }; + ArrayType *ArrayTy; + IntegerType *Int32Ty = Type::getInt32Ty(*Ctx); + IntegerType *Int64Ty = Type::getInt64Ty(*Ctx); + PointerType *Int8PtrTy = Type::getInt8PtrTy(*Ctx); + PointerType *Int8PtrPtrTy = Int8PtrTy->getPointerTo(); + PointerType *Int64PtrTy = Type::getInt64PtrTy(*Ctx); + StructType *StructInfoTy = + StructType::get(Int8PtrTy, Int32Ty, Int64PtrTy, Int8PtrPtrTy, nullptr); + PointerType *StructInfoPtrTy = StructInfoTy->getPointerTo(); + + unsigned NumOfStructs = 0; + SmallVector Initializers; + std::vector Vec = M.getIdentifiedStructTypes(); + for (auto &StructTy : Vec) { + if (shouldIgnoreStructType(StructTy)) + continue; + ++NumOfStructs; + + // StructName. + SmallString NameStr; + StringRef NameRef = createStructCounterName(StructTy, NameStr); + GlobalVariable *StructCounterName = createPrivateGlobalForString( + M, NameRef, /*AllowMerging*/true); + + // Counters. + // We create the counter array with StructCounterName and weak linkage + // so that the structs with the same name and layout from different + // compilation units are merged into one. + ArrayTy = ArrayType::get(Int64Ty, StructTy->getNumElements()); + GlobalVariable *Counters = + new GlobalVariable(M, ArrayTy, false, + GlobalVariable::WeakAnyLinkage, + ConstantAggregateZero::get(ArrayTy), + NameRef); + + // Field type name array. + // We pass field type names to runtime for better user report. + ArrayTy = ArrayType::get(Int8PtrTy, StructTy->getNumElements()); + GlobalVariable *TypeName = + new GlobalVariable(M, ArrayTy, true, + GlobalVariable::InternalLinkage, nullptr); + SmallVector TypeNameVec; + for (unsigned i = 0; i < StructTy->getNumElements(); ++i) { + Type *Ty = StructTy->getElementType(i); + std::string Str; + raw_string_ostream StrOS(Str); + Ty->print(StrOS); + TypeNameVec.push_back( + ConstantExpr::getPointerCast( + createPrivateGlobalForString(M, StrOS.str(), true), + Int8PtrTy)); + } + TypeName->setInitializer(ConstantArray::get(ArrayTy, TypeNameVec)); + + Initializers.push_back(ConstantStruct::get( + StructInfoTy, + ConstantExpr::getPointerCast(StructCounterName, Int8PtrTy), + ConstantInt::get(Int32Ty, StructTy->getNumElements()), + ConstantExpr::getPointerCast(Counters, Int64PtrTy), + ConstantExpr::getPointerCast(TypeName, Int8PtrPtrTy), + nullptr)); + } + + // Structs: StructInfo array + ArrayTy = ArrayType::get(StructInfoTy, NumOfStructs); + GlobalVariable *StructInfo = new GlobalVariable( + M, ArrayTy, false ,GlobalVariable::InternalLinkage, + ConstantArray::get(ArrayTy, Initializers)); + + // NumOfStructs + ToolInfoVec.push_back(ConstantInt::get(Int32Ty, NumOfStructs)); + // Structs + ToolInfoVec.push_back(ConstantExpr::getPointerCast(StructInfo, + StructInfoPtrTy)); +} + // Create the tool-specific global variable passed to EsanInit and EsanExit. GlobalVariable *EfficiencySanitizer::createEsanInitToolGV(Module &M) { - GlobalVariable *GV = nullptr; - // FIXME: create the tool specific global variable. - if (GV == nullptr) { - GV = new GlobalVariable(M, IntptrTy, true, GlobalVariable::InternalLinkage, - Constant::getNullValue(IntptrTy)); - } - return GV; + // This structure contains tool-specific information about each compilation + // unit (module) and is passed to the runtime library. + // This structure should be kept consistent with the tool-specific struct + // in the runtime library. + // struct ToolInfo { + // const char *UnitName; + // // expanding for the specific tool. + // ... + // }; + SmallVector ToolInfoVec; + // Compilation unit name. + ToolInfoVec.push_back( + ConstantExpr::getPointerCast( + createPrivateGlobalForString(M, M.getModuleIdentifier(), true), + Type::getInt8PtrTy(*Ctx))); + // Expanding ToolInfo for the specific tool. + if (Options.ToolType == EfficiencySanitizerOptions::ESAN_CacheFrag) + createCacheFragGV(M, ToolInfoVec); + // Create the global variable. + Constant *ToolInfo = ConstantStruct::getAnon(ToolInfoVec); + auto *ToolInfoGV = new GlobalVariable(M, ToolInfo->getType(), true, + GlobalVariable::InternalLinkage, + ToolInfo); + return ToolInfoGV; } void EfficiencySanitizer::createDestructor(Module &M, GlobalVariable *GV) {