Index: llvm/include/llvm/InitializePasses.h =================================================================== --- llvm/include/llvm/InitializePasses.h +++ llvm/include/llvm/InitializePasses.h @@ -177,6 +177,8 @@ void initializeHardwareLoopsPass(PassRegistry&); void initializeHotColdSplittingLegacyPassPass(PassRegistry&); void initializeHWAddressSanitizerLegacyPassPass(PassRegistry &); +void initializeInstrFuncEntryPass(PassRegistry&); +void initializeInstrFuncEntryLegacyPassPass(PassRegistry&); void initializeIPCPPass(PassRegistry&); void initializeIPSCCPLegacyPassPass(PassRegistry&); void initializeIRCELegacyPassPass(PassRegistry&); Index: llvm/include/llvm/ProfileData/InstrProfData.inc =================================================================== --- llvm/include/llvm/ProfileData/InstrProfData.inc +++ llvm/include/llvm/ProfileData/InstrProfData.inc @@ -295,7 +295,9 @@ INSTR_PROF_SECT_ENTRY(IPSK_orderfile, \ INSTR_PROF_QUOTE(INSTR_PROF_ORDERFILE_COMMON), \ INSTR_PROF_QUOTE(INSTR_PROF_ORDERFILE_COFF), "__DATA,") - +INSTR_PROF_SECT_ENTRY(IPSK_funcentry, \ + INSTR_PROF_QUOTE(INSTR_PROF_FUNCENTRY_COMMON), \ + INSTR_PROF_QUOTE(INSTR_PROF_FUNCENTRY_COFF), "__DATA,") #undef INSTR_PROF_SECT_ENTRY #endif @@ -688,6 +690,7 @@ #define INSTR_PROF_COVMAP_COMMON __llvm_covmap #define INSTR_PROF_COVFUN_COMMON __llvm_covfun #define INSTR_PROF_ORDERFILE_COMMON __llvm_orderfile +#define INSTR_PROF_FUNCENTRY_COMMON __llvm_funcentry /* Windows section names. Because these section names contain dollar characters, * they must be quoted. */ @@ -699,6 +702,7 @@ #define INSTR_PROF_COVMAP_COFF ".lcovmap$M" #define INSTR_PROF_COVFUN_COFF ".lcovfun$M" #define INSTR_PROF_ORDERFILE_COFF ".lorderfile$M" +#define INSTR_PROF_FUNCENTRY_COFF ".lfuncentry$M" #ifdef _WIN32 /* Runtime section names and name strings. */ Index: llvm/include/llvm/Transforms/Instrumentation.h =================================================================== --- llvm/include/llvm/Transforms/Instrumentation.h +++ llvm/include/llvm/Transforms/Instrumentation.h @@ -138,6 +138,7 @@ ModulePass *createInstrProfilingLegacyPass( const InstrProfOptions &Options = InstrProfOptions(), bool IsCS = false); +ModulePass *createInstrFuncEntryPass(); ModulePass *createInstrOrderFilePass(); // Insert DataFlowSanitizer (dynamic data flow analysis) instrumentation Index: llvm/include/llvm/Transforms/Instrumentation/InstrFuncEntry.h =================================================================== --- /dev/null +++ llvm/include/llvm/Transforms/Instrumentation/InstrFuncEntry.h @@ -0,0 +1,30 @@ +//===- InstrFuncEntry.h ------- Instrument Function entry for DCE etc. ----===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TRANSFORMS_INSTRFUNCENTRY_H +#define LLVM_TRANSFORMS_INSTRFUNCENTRY_H + +#include "llvm/IR/PassManager.h" +#include + +namespace llvm { +class Module; + +class InstrFuncEntryPass : public PassInfoMixin { + std::unordered_set BlackList; + +public: + PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); +}; + +} // end namespace llvm + +#endif // LLVM_TRANSFORMS_INSTRFUNCENTRY_H Index: llvm/lib/Transforms/IPO/PassManagerBuilder.cpp =================================================================== --- llvm/lib/Transforms/IPO/PassManagerBuilder.cpp +++ llvm/lib/Transforms/IPO/PassManagerBuilder.cpp @@ -167,6 +167,10 @@ extern cl::opt EnableKnowledgeRetention; +cl::opt EnableFuncEntryInstrumentation( + "enable-func-entry-instrumentation", cl::init(false), cl::Hidden, + cl::desc("Enable function entry instrumentation (default = off)")); + PassManagerBuilder::PassManagerBuilder() { OptLevel = 2; SizeLevel = 0; @@ -655,6 +659,9 @@ if (!(PrepareForLTO || PrepareForThinLTO)) addPGOInstrPasses(MPM, /* IsCS */ true); + if (EnableFuncEntryInstrumentation) + MPM.add(createInstrFuncEntryPass()); + if (EnableOrderFileInstrumentation) MPM.add(createInstrOrderFilePass()); Index: llvm/lib/Transforms/Instrumentation/CMakeLists.txt =================================================================== --- llvm/lib/Transforms/Instrumentation/CMakeLists.txt +++ llvm/lib/Transforms/Instrumentation/CMakeLists.txt @@ -8,6 +8,7 @@ MemorySanitizer.cpp IndirectCallPromotion.cpp Instrumentation.cpp + InstrFuncEntry.cpp InstrOrderFile.cpp InstrProfiling.cpp PGOInstrumentation.cpp Index: llvm/lib/Transforms/Instrumentation/InstrFuncEntry.cpp =================================================================== --- /dev/null +++ llvm/lib/Transforms/Instrumentation/InstrFuncEntry.cpp @@ -0,0 +1,397 @@ +//===- InstrFuncEntry.cpp ---- Instrument functions to mark executed code--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// 64 bit for module ID +// 32 bit for array size +// +// Each module has an array where each function has 1 byte of storage. +// Each function 'knows' its byte from compile time and writes information +// there. +// Function entry instrumentation generates one array for each module. +// Name of the array: _llvm_funcentry_buffer_MODULE_HASH +// where MODULE_HASH is the SHA of each module the variable is in. +// Each array has the following format: +// |-------64bit------|------32bit------|--- Entry for each function --------| +// |--Module hash-----|---#of functions-|---Byte array, with 0 or 1 ---------| +// +// The module-hash in the prefix can be enabled with a flag. +// +// TODO: This can be potentially used to 'count' the number of times function +// was called. But that's for future. +//===----------------------------------------------------------------------===// + +#include "llvm/Transforms/Instrumentation/InstrFuncEntry.h" + +#include "llvm/ADT/Statistic.h" +#include "llvm/InitializePasses.h" +#include "llvm/IR/CallSite.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/GlobalValue.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Metadata.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/ModuleSummaryIndex.h" +#include "llvm/Pass.h" +#include "llvm/PassRegistry.h" +#include "llvm/ProfileData/InstrProf.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Transforms/Instrumentation.h" +#include +#include +#include +#include +#include + +using namespace llvm; +#define DEBUG_TYPE "instrfuncentry" + +static cl::opt CLFuncEntryWriteMapping( + "func-entry-write-mapping", cl::init("/tmp/mapping"), cl::ZeroOrMore, + cl::desc("Dump functions and their MD5 hash to deobfuscate profile data"), + cl::Hidden); + +static cl::opt CLFuncEntryBlacklist( + "func-entry-blacklist", cl::init("/tmp/MDCD.blacklist"), cl::ZeroOrMore, + cl::desc("File with list of function names as blacklist"), cl::Hidden); + +static cl::opt UseBlacklist( + "func-entry-use-blacklist", cl::init(false), cl::ZeroOrMore, + cl::desc("Use blacklist file specified by -func-entry-blacklist")); + +static cl::opt AddModuleHash( + "func-entry-add-module-hash", cl::init(true), cl::ZeroOrMore, + cl::desc("Add module hash to the counter array. 64 bit per module")); + +static cl::opt FunEntryArrayName( + "func-entry-instr-array-name-prefix", cl::init("_llvm_funcentry_array"), + cl::ZeroOrMore, + cl::desc("Global Array Name Prefix, start with single underscore"), + cl::Hidden); + +static cl::opt FunEntryInstrSizeMin( + "func-entry-instruction-threshold", cl::init(10), cl::ZeroOrMore, + cl::desc("Minimum number of instructions in a function"), cl::Hidden); + +static cl::opt WriteMappingDataMethod( + "func-entry-instrumentation-writemethod", cl::init(0), cl::ZeroOrMore, + cl::desc("Use mutex lock(0), or exclusive file lock (1)")); + +static cl::opt CLFuncEntryWriteMappingSeparateFiles( + "func-entry-write-mapping-nfiles", cl::init(false), cl::ZeroOrMore, + cl::desc("Dump functions and their MD5 hashes in separate files"), + cl::Hidden); + +namespace { + +std::mutex MappingMutex; +std::mutex WriteMappingDataMutex; +std::mutex WriteBuffNameMutex; +using BlackListType = std::unordered_set; + +struct InstrFuncEntry { +private: + GlobalVariable *FuncEntryCounterBuffer; + unsigned BufferIdxCounter = 0; + ArrayType *BufferTy; + std::vector> InstrFunctions; + int64_t ModuleHash = 0; + +public: + bool shouldSkipFunction(const Function &F, + const BlackListType &BlackList) const { + auto Name = F.getName(); + // Skip Objectiv-C function names + if (Name.startswith("\x01-[") || Name.startswith("\x01+[")) + return true; + // Skip C++ standard library names + if (Name.startswith("_ZNSt") || Name.startswith("_ZSt") || + Name.startswith("_ZNKSt")) + return true; + // TODO: Find if instrumenting blocks is useful. + if (Name.contains("_block_invoke")) + return true; + if (FunEntryInstrSizeMin > 0 && + F.getInstructionCount() < (unsigned)FunEntryInstrSizeMin) + return true; + if (BlackList.count(Name)) + return true; + return false; + } + + std::string initializeFuncEntryBuffer(Module &M, + const BlackListType &BlackList) { + uint32_t NumFunctions = 0; + for (Function &F : M) { + if (!F.isDeclaration() && !shouldSkipFunction(F, BlackList)) + NumFunctions++; + } + LLVMContext &Ctx = M.getContext(); + // Write Module hash (4 bytes) and #functions (4 bytes) + SmallVector Init; + + auto ModuleHash = MD5Hash(M.getModuleIdentifier()); + + static_assert(sizeof(ModuleHash == 8), "ModuleHash > 8 bytes"); + IntegerType *Int8Ty = Type::getInt8Ty(Ctx); + auto getNthByte = [](unsigned n, decltype(ModuleHash) Num) -> uint8_t { + return (Num >> (8 * n)) & 0xff; + }; + + if (AddModuleHash) { + // cross-compilation can mess up endianness. + for (unsigned i = 0; i < sizeof(ModuleHash); ++i) { + // FIXME: Take care of endianness as well? + Init.push_back(ConstantInt::get(Int8Ty, getNthByte(i, ModuleHash))); + } + + BufferIdxCounter += sizeof(ModuleHash); + } + + for (unsigned i = 0; i < sizeof(NumFunctions); ++i) { + // #Functions can't exceed 2 billion. + Init.push_back(ConstantInt::get(Int8Ty, getNthByte(i, NumFunctions))); + } + + BufferIdxCounter += sizeof(NumFunctions); + Init.resize(BufferIdxCounter + NumFunctions, + ConstantInt::get(Int8Ty, 0xff)); + BufferTy = ArrayType::get(Type::getInt8Ty(Ctx), Init.size()); + + // Write all the buffer names to a file + std::stringstream SymbolName; + SymbolName << FunEntryArrayName; + SymbolName << "_" << ModuleHash; + FuncEntryCounterBuffer = new GlobalVariable( + M, BufferTy, false, GlobalValue::LinkOnceAnyLinkage, + ConstantArray::getNullValue(BufferTy), SymbolName.str()); + FuncEntryCounterBuffer->setVisibility(GlobalVariable::HiddenVisibility); + + Triple TT = Triple(M.getTargetTriple()); + FuncEntryCounterBuffer->setSection( + getInstrProfSectionName(IPSK_funcentry, TT.getObjectFormat())); + + FuncEntryCounterBuffer->setInitializer(ConstantArray::get(BufferTy, Init)); + return SymbolName.str(); + } + + std::string createMapDir() const { + SmallString<128> LogDir; + // Create a directory for storing mapping data + if (!CLFuncEntryWriteMapping.empty()) { + std::lock_guard LogLock(MappingMutex); + if (CLFuncEntryWriteMapping[0] == '~') { + auto EC = sys::fs::real_path(CLFuncEntryWriteMapping, LogDir, true); + if (EC) { + LLVM_DEBUG(errs() << "\nCould not expand tilde(HOME) for: " + << CLFuncEntryWriteMapping << "\n"); + LogDir = CLFuncEntryWriteMapping; + } + } else + LogDir = CLFuncEntryWriteMapping; + + sys::fs::perms P = + sys::fs::all_read | sys::fs::all_write | sys::fs::owner_exe; + if (!sys::fs::exists(LogDir) && + sys::fs::create_directories(LogDir, true, P)) + sys::path::system_temp_directory(false, LogDir); + } + + if (!sys::fs::exists(LogDir)) { + LLVM_DEBUG(errs() << "\nCould not create directory: " << LogDir << "\n"); + report_fatal_error("Unable to create directory: " + LogDir + "\n"); + } else { + LLVM_DEBUG(errs() << "\nCreated directory: " << LogDir << "\n"); + } + + return LogDir.str(); + } + + + std::string getMappingLine(std::pair P) const { + auto Name = P.first->getName(); + std::stringstream stream; + stream << std::hex << MD5Hash(Name); + std::string singleLine = "MD5 " + stream.str() + ',' + + std::string(Name) + ',' + + std::to_string(P.second) + '\n'; + return singleLine; + } + + void writeMappingDataExLock(StringRef FileName) const { + int fd = open(FileName.str().c_str(), O_APPEND | O_CREAT); + if (flock(fd, LOCK_EX) == 0) { + for (auto &P : InstrFunctions) { + auto singleLine = getMappingLine(P); + int bytesWritten = write(fd, singleLine.c_str(), singleLine.size()); + assert(bytesWritten > 0 && singleLine.size() == (unsigned)bytesWritten); + } + flock(fd, LOCK_UN); + close(fd); + return; + } + report_fatal_error( + Twine("Failed to open ") + FileName + + " to save mapping file for function entry instrumentation\n"); + } + + void writeMappingData(StringRef FileName) const { + { + std::lock_guard LogLock(WriteMappingDataMutex); + std::error_code EC; + raw_fd_ostream OS(FileName, EC, sys::fs::OpenFlags::F_Append); + if (!EC) { + for (auto &P : InstrFunctions) { + auto singleLine = getMappingLine(P); + OS << singleLine; + } + return; + } + } + report_fatal_error( + Twine("Failed to open ") + FileName + + " to save mapping file for function entry instrumentation\n"); + } + + void writeBuffName(StringRef FileName, StringRef BuffName) const { + { + std::lock_guard LogLock(WriteBuffNameMutex); + std::error_code EC; + raw_fd_ostream OS(FileName, EC, sys::fs::OpenFlags::F_Append); + if (!EC) { + // Write all the global buffer names in a single file. + OS << BuffName << "\n"; + return; + } + } + report_fatal_error( + Twine("Failed to open ") + FileName + + " to save buffer name for function entry instrumentation\n"); + } + + // Generate the code sequence in the entry block of each function to + // update the buffer. + void generateCodeSequence(Module &M, Function &F) { + BasicBlock *OrigEntry = &F.getEntryBlock(); + LLVMContext &Ctx = M.getContext(); + IntegerType *Int32Ty = Type::getInt32Ty(Ctx); + IntegerType *Int8Ty = Type::getInt8Ty(Ctx); + + // Create a new entry block for instrumentation. + BasicBlock *UpdateFuncEntryBB = + BasicBlock::Create(M.getContext(), "funcentry_set", &F, OrigEntry); + IRBuilder<> updateB(UpdateFuncEntryBB); + + Value *BufferGEPIdx[] = {ConstantInt::get(Int32Ty, 0), + ConstantInt::get(Int32Ty, BufferIdxCounter)}; + Value *BufferAddr = + updateB.CreateGEP(BufferTy, FuncEntryCounterBuffer, BufferGEPIdx, ""); + // TODO: Call a clamping function to update the function counter. + // A saturating number will suggest higher frequency. + // Initializing with 1 and writing zero will use the zero register. + unsigned ValueToWrite = 0; + updateB.CreateStore(ConstantInt::get(Int8Ty, ValueToWrite), BufferAddr); + updateB.CreateBr(OrigEntry); + + // Collect all the names and write them at once. + InstrFunctions.push_back({&F, BufferIdxCounter}); + ++BufferIdxCounter; + } + + bool run(Module &M, const BlackListType &BlackList) { + auto BuffName = initializeFuncEntryBuffer(M, BlackList); + auto MapDirectory = createMapDir(); + + SmallString<128> MapFile(MapDirectory); + std::string writeMapping("func-entry-write-mapping.txt"); + std::string globalBuffer("func-entry-global-buffer.txt"); + if (CLFuncEntryWriteMappingSeparateFiles) { + auto n = std::to_string(ModuleHash); + writeMapping = n + writeMapping; + globalBuffer = n + writeMapping; + } + sys::path::append(MapFile, writeMapping); + SmallString<128> GlobalBufferList(MapDirectory); + sys::path::append(GlobalBufferList, globalBuffer); + LLVM_DEBUG(llvm::errs() + << "InstrFuncEntry: MapFile: " << MapFile + << ", GlobalBufferList: " << GlobalBufferList << "\n"); + + writeBuffName(GlobalBufferList, BuffName); + + for (Function &F : M) { + if (F.isDeclaration()) + continue; + if (shouldSkipFunction(F, BlackList)) + continue; + generateCodeSequence(M, F); + } + + if (WriteMappingDataMethod == 0) + writeMappingData(MapFile); + else // = 1 + writeMappingDataExLock(MapFile); + return true; + } + + static void readBlackList(BlackListType &BlackList) { + if (!UseBlacklist) + return; + // Read the list of function names from a file. + std::ifstream infile(CLFuncEntryBlacklist); + if (infile.is_open()) { + std::string line; + while (std::getline(infile, line)) + BlackList.insert(line); + } + } +}; // End of InstrFuncEntry struct + +class InstrFuncEntryLegacyPass : public ModulePass { +public: + static char ID; + BlackListType BlackList; + + InstrFuncEntryLegacyPass() : ModulePass(ID) { + initializeInstrFuncEntryLegacyPassPass(*PassRegistry::getPassRegistry()); + InstrFuncEntry::readBlackList(BlackList); + } + + bool runOnModule(Module &M) override; +}; + +} // End anonymous namespace + +bool InstrFuncEntryLegacyPass::runOnModule(Module &M) { + if (skipModule(M)) + return false; + + return InstrFuncEntry().run(M, BlackList); +} + +PreservedAnalyses InstrFuncEntryPass::run(Module &M, + ModuleAnalysisManager &AM) { + InstrFuncEntry::readBlackList(BlackList); + if (InstrFuncEntry().run(M, BlackList)) + return PreservedAnalyses::none(); + return PreservedAnalyses::all(); +} + +char InstrFuncEntryLegacyPass::ID = 0; +INITIALIZE_PASS(InstrFuncEntryLegacyPass, DEBUG_TYPE, + "Instrumentation for Function Entry", false, false) + +ModulePass *llvm::createInstrFuncEntryPass() { + return new InstrFuncEntryLegacyPass(); +} Index: llvm/lib/Transforms/Instrumentation/Instrumentation.cpp =================================================================== --- llvm/lib/Transforms/Instrumentation/Instrumentation.cpp +++ llvm/lib/Transforms/Instrumentation/Instrumentation.cpp @@ -108,6 +108,7 @@ initializeBoundsCheckingLegacyPassPass(Registry); initializeControlHeightReductionLegacyPassPass(Registry); initializeGCOVProfilerLegacyPassPass(Registry); + initializeInstrFuncEntryLegacyPassPass(Registry); initializePGOInstrumentationGenLegacyPassPass(Registry); initializePGOInstrumentationUseLegacyPassPass(Registry); initializePGOIndirectCallPromotionLegacyPassPass(Registry); Index: llvm/test/Instrumentation/InstrFuncEntry/Inputs/blacklist1.txt =================================================================== --- /dev/null +++ llvm/test/Instrumentation/InstrFuncEntry/Inputs/blacklist1.txt @@ -0,0 +1 @@ +foo Index: llvm/test/Instrumentation/InstrFuncEntry/Inputs/blacklist2.txt =================================================================== --- /dev/null +++ llvm/test/Instrumentation/InstrFuncEntry/Inputs/blacklist2.txt @@ -0,0 +1,2 @@ +bar +bare Index: llvm/test/Instrumentation/InstrFuncEntry/funcentry.ll =================================================================== --- /dev/null +++ llvm/test/Instrumentation/InstrFuncEntry/funcentry.ll @@ -0,0 +1,68 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -debug -instrfuncentry -func-entry-write-mapping=%T/mapping -S -func-entry-instruction-threshold=1 < %s 2>&1 | FileCheck %s +; RUN: opt -instrfuncentry -func-entry-write-mapping=%T/mapping -func-entry-instruction-threshold=1 < %s | llc -o - | FileCheck %s --check-prefix=ASMCHECK + +target triple = "arm64-apple-ios" +; CHECK: InstrFuncEntry: MapFile: {{.*}}func-entry-write-mapping.txt{{.*}}GlobalBufferList: {{.*}}func-entry-global-buffer.txt +; The contents of func-entry-write-mapping.txt looks like: +; MD5 5cf8c24cdb18bdac,foo,12 +; MD5 e413754a191db537,bar,13 + +; Size of the array is 14 (8 for the module hash, 4 for the #functions, and 2 for the functions) +; CHECK: @_llvm_funcentry_array_5977508082728489289 = linkonce hidden global [14 x i8] c"I\D1\0EY\EE_\F4R\02\00\00\00\FF\FF", section "__DATA,__llvm_funcentry" + +; Function Attrs: norecurse nounwind readnone +; CHECK-LABEL: @foo( +; CHECK: funcentry_set: +; CHECK: store i8 0, i8* getelementptr inbounds ([14 x i8], [14 x i8]* @_llvm_funcentry_array_5977508082728489289, i32 0, i32 12) +; CHECK-NEXT: br label %0 + +; CHECK-LABEL: @bar( +; CHECK: funcentry_set: +; CHECK: store i8 0, i8* getelementptr inbounds ([14 x i8], [14 x i8]* @_llvm_funcentry_array_5977508082728489289, i32 0, i32 13) +; CHECK-NEXT: br label %0 + +; CHECK: funcentry + +; ASMCHECK: _foo +; ASMCHECK: adrp x8, __llvm_funcentry_array_5977508082728489289@PAGE +; Due to non-determinism in the compiler it generates strb with hardcoded page offset (12 in this case) on mac +; but generates add followed by strb on linux machines. +; ASMCHECK: __llvm_funcentry_array_5977508082728489289@PAGEOFF + +; ASMCHECK: _bar +; ASMCHECK: adrp x8, __llvm_funcentry_array_5977508082728489289@PAGE +; Due to non-determinism in the compiler it generates strb with hardcoded page offset (12 in this case) on mac +; but generates add followed by strb on linux machines. +; ASMCHECK: __llvm_funcentry_array_5977508082728489289@PAGEOFF + +define i32 @foo(i32 %a) { + %a.sroa.0.0.trunc = trunc i32 %a to i8 + %a.sroa.5.0.shift = lshr i32 %a, 8 + %bf.clear = and i8 %a.sroa.0.0.trunc, 1 + %bf.cast = zext i8 %bf.clear to i32 + %bf.lshr = lshr i8 %a.sroa.0.0.trunc, 1 + %bf.clear2 = and i8 %bf.lshr, 1 + %bf.cast3 = zext i8 %bf.clear2 to i32 + %or = or i32 %bf.cast, %bf.cast3 + %bf.lshr5 = lshr i8 %a.sroa.0.0.trunc, 2 + %bf.clear6 = and i8 %bf.lshr5, 1 + %bf.cast7 = zext i8 %bf.clear6 to i32 + %or8 = or i32 %or, %bf.cast7 + %bf.lshr10 = lshr i8 %a.sroa.0.0.trunc, 3 + %bf.clear11 = and i8 %bf.lshr10, 1 + %bf.cast12 = zext i8 %bf.clear11 to i32 + %or13 = or i32 %or8, %bf.cast12 + %cmp = icmp eq i32 %or13, 0 + %conv = zext i1 %cmp to i32 + ret i32 %conv +} + +define i32 @bar(i32 %a) { + %a.sroa.0.0.trunc = trunc i32 %a to i8 + %a.sroa.5.0.shift = lshr i32 %a, 8 + %bf.clear = and i8 %a.sroa.0.0.trunc, 1 + %bf.cast = zext i8 %bf.clear to i32 + %bf.lshr = lshr i8 %a.sroa.0.0.trunc, 1 + ret i32 %a +} Index: llvm/test/Instrumentation/InstrFuncEntry/read-blacklist-from-file.ll =================================================================== --- /dev/null +++ llvm/test/Instrumentation/InstrFuncEntry/read-blacklist-from-file.ll @@ -0,0 +1,75 @@ +; RUN: opt -debug -instrfuncentry -func-entry-instruction-threshold=1 -func-entry-write-mapping=%T/mapping -func-entry-blacklist=%p/Inputs/blacklist2.txt -func-entry-use-blacklist -S < %s 2>&1 | FileCheck %s +; RUN: opt -debug -instrfuncentry -func-entry-instruction-threshold=1 -func-entry-write-mapping=%T/mapping -func-entry-blacklist=%p/Inputs/blacklist1.txt -func-entry-use-blacklist -S < %s 2>&1 | FileCheck %s --check-prefix=CHECK2 + +target triple = "arm64-apple-ios" +%0 = type opaque +; CHECK: InstrFuncEntry: MapFile: {{.*}}func-entry-write-mapping.txt{{.*}}GlobalBufferList: {{.*}}func-entry-global-buffer.txt +; Size of the array is 15 (8 for the module hash, 4 for the #functions, 3 functions are instrumented in this module) +; CHECK: @_llvm_funcentry_array_5977508082728489289 = linkonce hidden global [13 x i8] c"I\D1\0EY\EE_\F4R\01\00\00\00\FF", section "__DATA,__llvm_funcentry" +; CHECK2: @_llvm_funcentry_array_5977508082728489289 = linkonce hidden global [14 x i8] c"I\D1\0EY\EE_\F4R\02\00\00\00\FF\FF", section "__DATA,__llvm_funcentry" + +; Function Attrs: norecurse nounwind readnone +; CHECK-LABEL: @foo( +; CHECK: funcentry_set: +; CHECK: store{{.*}}llvm_funcentry_array + +; CHECK2-LABEL: @foo( +; CHECK2-NOT: funcentry_set: +; CHECK2-NOT: store{{.*}}llvm_funcentry_array + +define i32 @foo(i32 %a) { + %a.sroa.0.0.trunc = trunc i32 %a to i8 + %a.sroa.5.0.shift = lshr i32 %a, 8 + %bf.clear = and i8 %a.sroa.0.0.trunc, 1 + %bf.cast = zext i8 %bf.clear to i32 + %bf.lshr = lshr i8 %a.sroa.0.0.trunc, 1 + ret i32 %a +} + +; CHECK-LABEL: @bar( +; CHECK-NOT: funcentry_set: +; CHECK-NOT: store{{.*}}llvm_funcentry_array + +; CHECK2-LABEL: @bar( +; CHECK2: funcentry_set: +; CHECK2: store{{.*}}llvm_funcentry_array + +define i32 @bar(i32 %a) { + %a.sroa.0.0.trunc = trunc i32 %a to i8 + %a.sroa.5.0.shift = lshr i32 %a, 8 + %bf.clear = and i8 %a.sroa.0.0.trunc, 1 + %bf.cast = zext i8 %bf.clear to i32 + %bf.lshr = lshr i8 %a.sroa.0.0.trunc, 1 + ret i32 %a +} + +; CHECK-LABEL: @bare( +; CHECK-NOT: funcentry_set: +; CHECK-NOT: store{{.*}}llvm_funcentry_array + +; CHECK2-LABEL: @bare( +; CHECK2: funcentry_set: +; CHECK2: store{{.*}}llvm_funcentry_array + +define i32 @bare(i32 %a) { + %a.sroa.0.0.trunc = trunc i32 %a to i8 + %a.sroa.5.0.shift = lshr i32 %a, 8 + %bf.clear = and i8 %a.sroa.0.0.trunc, 1 + %bf.cast = zext i8 %bf.clear to i32 + %bf.lshr = lshr i8 %a.sroa.0.0.trunc, 1 + ret i32 %a +} + + +; CHECK-LABEL: @"\01+[Square numSides]" +; CHECK-NOT: funcentry_set: +; CHECK-NOT: store{{.*}}llvm_funcentry_array + +; CHECK2-LABEL: @"\01+[Square numSides]" +; CHECK2-NOT: funcentry_set: +; CHECK2-NOT: store{{.*}}llvm_funcentry_array + +define internal i32* @"\01+[Square numSides]"(%0*, i8* nocapture readnone) { + %foo = bitcast %0* %0 to i32* + ret i32* %foo +} Index: llvm/test/Instrumentation/InstrFuncEntry/skip-objc-and-stdcxx.ll =================================================================== --- /dev/null +++ llvm/test/Instrumentation/InstrFuncEntry/skip-objc-and-stdcxx.ll @@ -0,0 +1,58 @@ +; RUN: opt -debug -instrfuncentry -func-entry-write-mapping=%T/mapping -func-entry-instruction-threshold=1 -S < %s 2>&1 | FileCheck %s + +target triple = "arm64-apple-ios" +; CHECK: InstrFuncEntry: MapFile: {{.*}}func-entry-write-mapping.txt{{.*}}GlobalBufferList: {{.*}}func-entry-global-buffer.txt +; Size of the array is 12 (8 for the module hash, 4 for the #functions, no functions are instrumented in this module) +; CHECK: @_llvm_funcentry_array_5977508082728489289 = linkonce hidden global [12 x i8] c"I\D1\0EY\EE_\F4R\00\00\00\00", section "__DATA,__llvm_funcentry" + +; Function Attrs: norecurse nounwind readnone +; CHECK-LABEL: @_ZNSt1iEi( +; CHECK-NOT: funcentry_set: +; CHECK-NOT: store{{.*}}llvm_funcentry_array + +; CHECK-LABEL: @_ZSt9terminatev( +; CHECK-NOT: funcentry_set: +; CHECK-NOT: store{{.*}}llvm_funcentry_array + +define i32 @_ZNSt1iEi(i32 %a) { + %a.sroa.0.0.trunc = trunc i32 %a to i8 + %a.sroa.5.0.shift = lshr i32 %a, 8 + %bf.clear = and i8 %a.sroa.0.0.trunc, 1 + %bf.cast = zext i8 %bf.clear to i32 + %bf.lshr = lshr i8 %a.sroa.0.0.trunc, 1 + ret i32 %a +} + +define i32 @_ZSt9terminatev(i32 %a) { + %a.sroa.0.0.trunc = trunc i32 %a to i8 + %a.sroa.5.0.shift = lshr i32 %a, 8 + %bf.clear = and i8 %a.sroa.0.0.trunc, 1 + %bf.cast = zext i8 %bf.clear to i32 + %bf.lshr = lshr i8 %a.sroa.0.0.trunc, 1 + ret i32 %a +} + +; CHECK-LABEL: @"__27-[obj selectorForTypeMeth:]_block_invoke_46304" +; CHECK-NOT: funcentry_set: +; CHECK-NOT: store{{.*}}llvm_funcentry_array +define void @"__27-[obj selectorForTypeMeth:]_block_invoke_46304"() { +entry: + ret void +} + +; CHECK-LABEL: @"\01-[Square area]" +; CHECK-NOT: funcentry_set: +; CHECK-NOT: store{{.*}}llvm_funcentry_array +%0 = type opaque +define internal i32* @"\01-[Square area]"(%0*, i8* nocapture readnone) { + %foo = bitcast %0* %0 to i32* + ret i32* %foo +} + +; CHECK-LABEL: @"\01+[Square numSides]" +; CHECK-NOT: funcentry_set: +; CHECK-NOT: store{{.*}}llvm_funcentry_array +define internal i32* @"\01+[Square numSides]"(%0*, i8* nocapture readnone) { + %foo = bitcast %0* %0 to i32* + ret i32* %foo +} Index: llvm/test/Instrumentation/InstrFuncEntry/skip-small-functions.ll =================================================================== --- /dev/null +++ llvm/test/Instrumentation/InstrFuncEntry/skip-small-functions.ll @@ -0,0 +1,20 @@ +; RUN: opt -debug -instrfuncentry -func-entry-write-mapping=%T/mapping -func-entry-instruction-threshold=10 -S < %s 2>&1 | FileCheck %s + +target triple = "arm64-apple-ios" +; CHECK: InstrFuncEntry: MapFile: {{.*}}func-entry-write-mapping.txt{{.*}}GlobalBufferList: {{.*}}func-entry-global-buffer.txt +; Size of the array is 12 (8 for the module hash, 4 for the #functions, no functions are instrumented in this module) +; CHECK: @_llvm_funcentry_array_5977508082728489289 = linkonce hidden global [12 x i8] c"I\D1\0EY\EE_\F4R\00\00\00\00", section "__DATA,__llvm_funcentry" + +; Function Attrs: norecurse nounwind readnone +; CHECK-LABEL: @foo( +; CHECK-NOT: funcentry_set: +; CHECK-NOT: store{{.*}}llvm_funcentry_array + +define i32 @foo(i32 %a) { + %a.sroa.0.0.trunc = trunc i32 %a to i8 + %a.sroa.5.0.shift = lshr i32 %a, 8 + %bf.clear = and i8 %a.sroa.0.0.trunc, 1 + %bf.cast = zext i8 %bf.clear to i32 + %bf.lshr = lshr i8 %a.sroa.0.0.trunc, 1 + ret i32 %a +} Index: llvm/test/Instrumentation/InstrFuncEntry/verify-files-created.ll =================================================================== --- /dev/null +++ llvm/test/Instrumentation/InstrFuncEntry/verify-files-created.ll @@ -0,0 +1,43 @@ +; RUN: opt -debug -instrfuncentry -func-entry-instruction-threshold=1 -func-entry-write-mapping=%T/mapping -S < %s 2>&1 | FileCheck %s +; Check that files exist and has the relevant contents +; RUN: grep _llvm_funcentry_array_5977508082728489289 %T/mapping/func-entry-global-buffer.txt | FileCheck %s --check-prefix=CHECK-BUFFER +; RUN: grep MD5 %T/mapping/func-entry-write-mapping.txt | FileCheck %s --check-prefix=CHECK-MAPPING +; Cleanup +; RUN: rm -rf %T + +; CHECK-BUFFER: _llvm_funcentry_array_5977508082728489289 +; CHECK-MAPPING: MD5 {{.*}},foo,12 +; CHECK-MAPPING: MD5 {{.*}},bar,13 + +target triple = "arm64-apple-ios" +; CHECK: InstrFuncEntry: MapFile: {{.*}}func-entry-write-mapping.txt{{.*}}GlobalBufferList: {{.*}}func-entry-global-buffer.txt +; Size of the array is 12 (8 for the module hash, 4 for the #functions, no functions are instrumented in this module) +; CHECK: @_llvm_funcentry_array_5977508082728489289 = linkonce hidden global [14 x i8] c"I\D1\0EY\EE_\F4R\02\00\00\00\FF\FF", section "__DATA,__llvm_funcentry" + +; Function Attrs: norecurse nounwind readnone +; CHECK-LABEL: @foo( +; CHECK: funcentry_set: +; CHECK: store{{.*}}llvm_funcentry_array + +; CHECK-LABEL: @bar( +; CHECK: funcentry_set: +; CHECK: store{{.*}}llvm_funcentry_array + +define i32 @foo(i32 %a) { + %a.sroa.0.0.trunc = trunc i32 %a to i8 + %a.sroa.5.0.shift = lshr i32 %a, 8 + %bf.clear = and i8 %a.sroa.0.0.trunc, 1 + %bf.cast = zext i8 %bf.clear to i32 + %bf.lshr = lshr i8 %a.sroa.0.0.trunc, 1 + ret i32 %a +} + +define i32 @bar(i32 %a) { + %a.sroa.0.0.trunc = trunc i32 %a to i8 + %a.sroa.5.0.shift = lshr i32 %a, 8 + %bf.clear = and i8 %a.sroa.0.0.trunc, 1 + %bf.cast = zext i8 %bf.clear to i32 + %bf.lshr = lshr i8 %a.sroa.0.0.trunc, 1 + ret i32 %a +} +