| //===- GCOVProfiling.cpp - Insert edge counters for gcov profiling --------===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This pass implements GCOV-style profiling. When this pass is run it emits |
| // "gcno" files next to the existing source, and instruments the code that runs |
| // to records the edges between blocks that run and emit a complementary "gcda" |
| // file on exit. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "llvm/ADT/DenseMap.h" |
| #include "llvm/ADT/Hashing.h" |
| #include "llvm/ADT/STLExtras.h" |
| #include "llvm/ADT/Statistic.h" |
| #include "llvm/ADT/StringExtras.h" |
| #include "llvm/ADT/StringMap.h" |
| #include "llvm/ADT/UniqueVector.h" |
| #include "llvm/IR/DebugInfo.h" |
| #include "llvm/IR/DebugLoc.h" |
| #include "llvm/IR/IRBuilder.h" |
| #include "llvm/IR/InstIterator.h" |
| #include "llvm/IR/Instructions.h" |
| #include "llvm/IR/IntrinsicInst.h" |
| #include "llvm/IR/Module.h" |
| #include "llvm/Pass.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/GCOVProfiler.h" |
| #include "llvm/Transforms/Instrumentation.h" |
| #include "llvm/Transforms/Utils/ModuleUtils.h" |
| #include <algorithm> |
| #include <memory> |
| #include <string> |
| #include <utility> |
| using namespace llvm; |
| |
| #define DEBUG_TYPE "insert-gcov-profiling" |
| |
| static cl::opt<std::string> |
| DefaultGCOVVersion("default-gcov-version", cl::init("402*"), cl::Hidden, |
| cl::ValueRequired); |
| static cl::opt<bool> DefaultExitBlockBeforeBody("gcov-exit-block-before-body", |
| cl::init(false), cl::Hidden); |
| |
| GCOVOptions GCOVOptions::getDefault() { |
| GCOVOptions Options; |
| Options.EmitNotes = true; |
| Options.EmitData = true; |
| Options.UseCfgChecksum = false; |
| Options.NoRedZone = false; |
| Options.FunctionNamesInData = true; |
| Options.ExitBlockBeforeBody = DefaultExitBlockBeforeBody; |
| |
| if (DefaultGCOVVersion.size() != 4) { |
| llvm::report_fatal_error(std::string("Invalid -default-gcov-version: ") + |
| DefaultGCOVVersion); |
| } |
| memcpy(Options.Version, DefaultGCOVVersion.c_str(), 4); |
| return Options; |
| } |
| |
| namespace { |
| class GCOVFunction; |
| |
| class GCOVProfiler { |
| public: |
| GCOVProfiler() : GCOVProfiler(GCOVOptions::getDefault()) {} |
| GCOVProfiler(const GCOVOptions &Opts) : Options(Opts) { |
| assert((Options.EmitNotes || Options.EmitData) && |
| "GCOVProfiler asked to do nothing?"); |
| ReversedVersion[0] = Options.Version[3]; |
| ReversedVersion[1] = Options.Version[2]; |
| ReversedVersion[2] = Options.Version[1]; |
| ReversedVersion[3] = Options.Version[0]; |
| ReversedVersion[4] = '\0'; |
| } |
| bool runOnModule(Module &M); |
| |
| private: |
| // Create the .gcno files for the Module based on DebugInfo. |
| void emitProfileNotes(); |
| |
| // Modify the program to track transitions along edges and call into the |
| // profiling runtime to emit .gcda files when run. |
| bool emitProfileArcs(); |
| |
| // Get pointers to the functions in the runtime library. |
| Constant *getStartFileFunc(); |
| Constant *getIncrementIndirectCounterFunc(); |
| Constant *getEmitFunctionFunc(); |
| Constant *getEmitArcsFunc(); |
| Constant *getSummaryInfoFunc(); |
| Constant *getEndFileFunc(); |
| |
| // Create or retrieve an i32 state value that is used to represent the |
| // pred block number for certain non-trivial edges. |
| GlobalVariable *getEdgeStateValue(); |
| |
| // Produce a table of pointers to counters, by predecessor and successor |
| // block number. |
| GlobalVariable *buildEdgeLookupTable(Function *F, GlobalVariable *Counter, |
| const UniqueVector<BasicBlock *> &Preds, |
| const UniqueVector<BasicBlock *> &Succs); |
| |
| // Add the function to write out all our counters to the global destructor |
| // list. |
| Function * |
| insertCounterWriteout(ArrayRef<std::pair<GlobalVariable *, MDNode *>>); |
| Function *insertFlush(ArrayRef<std::pair<GlobalVariable *, MDNode *>>); |
| void insertIndirectCounterIncrement(); |
| |
| std::string mangleName(const DICompileUnit *CU, const char *NewStem); |
| |
| GCOVOptions Options; |
| |
| // Reversed, NUL-terminated copy of Options.Version. |
| char ReversedVersion[5]; |
| // Checksum, produced by hash of EdgeDestinations |
| SmallVector<uint32_t, 4> FileChecksums; |
| |
| Module *M; |
| LLVMContext *Ctx; |
| SmallVector<std::unique_ptr<GCOVFunction>, 16> Funcs; |
| }; |
| |
| class GCOVProfilerLegacyPass : public ModulePass { |
| public: |
| static char ID; |
| GCOVProfilerLegacyPass() |
| : GCOVProfilerLegacyPass(GCOVOptions::getDefault()) {} |
| GCOVProfilerLegacyPass(const GCOVOptions &Opts) |
| : ModulePass(ID), Profiler(Opts) { |
| initializeGCOVProfilerLegacyPassPass(*PassRegistry::getPassRegistry()); |
| } |
| const char *getPassName() const override { return "GCOV Profiler"; } |
| |
| bool runOnModule(Module &M) override { return Profiler.runOnModule(M); } |
| |
| private: |
| GCOVProfiler Profiler; |
| }; |
| } |
| |
| char GCOVProfilerLegacyPass::ID = 0; |
| INITIALIZE_PASS(GCOVProfilerLegacyPass, "insert-gcov-profiling", |
| "Insert instrumentation for GCOV profiling", false, false) |
| |
| ModulePass *llvm::createGCOVProfilerPass(const GCOVOptions &Options) { |
| return new GCOVProfilerLegacyPass(Options); |
| } |
| |
| static StringRef getFunctionName(const DISubprogram *SP) { |
| if (!SP->getLinkageName().empty()) |
| return SP->getLinkageName(); |
| return SP->getName(); |
| } |
| |
| namespace { |
| class GCOVRecord { |
| protected: |
| static const char *const LinesTag; |
| static const char *const FunctionTag; |
| static const char *const BlockTag; |
| static const char *const EdgeTag; |
| |
| GCOVRecord() = default; |
| |
| void writeBytes(const char *Bytes, int Size) { |
| os->write(Bytes, Size); |
| } |
| |
| void write(uint32_t i) { |
| writeBytes(reinterpret_cast<char*>(&i), 4); |
| } |
| |
| // Returns the length measured in 4-byte blocks that will be used to |
| // represent this string in a GCOV file |
| static unsigned lengthOfGCOVString(StringRef s) { |
| // A GCOV string is a length, followed by a NUL, then between 0 and 3 NULs |
| // padding out to the next 4-byte word. The length is measured in 4-byte |
| // words including padding, not bytes of actual string. |
| return (s.size() / 4) + 1; |
| } |
| |
| void writeGCOVString(StringRef s) { |
| uint32_t Len = lengthOfGCOVString(s); |
| write(Len); |
| writeBytes(s.data(), s.size()); |
| |
| // Write 1 to 4 bytes of NUL padding. |
| assert((unsigned)(4 - (s.size() % 4)) > 0); |
| assert((unsigned)(4 - (s.size() % 4)) <= 4); |
| writeBytes("\0\0\0\0", 4 - (s.size() % 4)); |
| } |
| |
| raw_ostream *os; |
| }; |
| const char *const GCOVRecord::LinesTag = "\0\0\x45\x01"; |
| const char *const GCOVRecord::FunctionTag = "\0\0\0\1"; |
| const char *const GCOVRecord::BlockTag = "\0\0\x41\x01"; |
| const char *const GCOVRecord::EdgeTag = "\0\0\x43\x01"; |
| |
| class GCOVFunction; |
| class GCOVBlock; |
| |
| // Constructed only by requesting it from a GCOVBlock, this object stores a |
| // list of line numbers and a single filename, representing lines that belong |
| // to the block. |
| class GCOVLines : public GCOVRecord { |
| public: |
| void addLine(uint32_t Line) { |
| assert(Line != 0 && "Line zero is not a valid real line number."); |
| Lines.push_back(Line); |
| } |
| |
| uint32_t length() const { |
| // Here 2 = 1 for string length + 1 for '0' id#. |
| return lengthOfGCOVString(Filename) + 2 + Lines.size(); |
| } |
| |
| void writeOut() { |
| write(0); |
| writeGCOVString(Filename); |
| for (int i = 0, e = Lines.size(); i != e; ++i) |
| write(Lines[i]); |
| } |
| |
| GCOVLines(StringRef F, raw_ostream *os) |
| : Filename(F) { |
| this->os = os; |
| } |
| |
| private: |
| StringRef Filename; |
| SmallVector<uint32_t, 32> Lines; |
| }; |
| |
| |
| // Represent a basic block in GCOV. Each block has a unique number in the |
| // function, number of lines belonging to each block, and a set of edges to |
| // other blocks. |
| class GCOVBlock : public GCOVRecord { |
| public: |
| GCOVLines &getFile(StringRef Filename) { |
| GCOVLines *&Lines = LinesByFile[Filename]; |
| if (!Lines) { |
| Lines = new GCOVLines(Filename, os); |
| } |
| return *Lines; |
| } |
| |
| void addEdge(GCOVBlock &Successor) { |
| OutEdges.push_back(&Successor); |
| } |
| |
| void writeOut() { |
| uint32_t Len = 3; |
| SmallVector<StringMapEntry<GCOVLines *> *, 32> SortedLinesByFile; |
| for (auto &I : LinesByFile) { |
| Len += I.second->length(); |
| SortedLinesByFile.push_back(&I); |
| } |
| |
| writeBytes(LinesTag, 4); |
| write(Len); |
| write(Number); |
| |
| std::sort(SortedLinesByFile.begin(), SortedLinesByFile.end(), |
| [](StringMapEntry<GCOVLines *> *LHS, |
| StringMapEntry<GCOVLines *> *RHS) { |
| return LHS->getKey() < RHS->getKey(); |
| }); |
| for (auto &I : SortedLinesByFile) |
| I->getValue()->writeOut(); |
| write(0); |
| write(0); |
| } |
| |
| ~GCOVBlock() { |
| DeleteContainerSeconds(LinesByFile); |
| } |
| |
| GCOVBlock(const GCOVBlock &RHS) : GCOVRecord(RHS), Number(RHS.Number) { |
| // Only allow copy before edges and lines have been added. After that, |
| // there are inter-block pointers (eg: edges) that won't take kindly to |
| // blocks being copied or moved around. |
| assert(LinesByFile.empty()); |
| assert(OutEdges.empty()); |
| } |
| |
| private: |
| friend class GCOVFunction; |
| |
| GCOVBlock(uint32_t Number, raw_ostream *os) |
| : Number(Number) { |
| this->os = os; |
| } |
| |
| uint32_t Number; |
| StringMap<GCOVLines *> LinesByFile; |
| SmallVector<GCOVBlock *, 4> OutEdges; |
| }; |
| |
| // A function has a unique identifier, a checksum (we leave as zero) and a |
| // set of blocks and a map of edges between blocks. This is the only GCOV |
| // object users can construct, the blocks and lines will be rooted here. |
| class GCOVFunction : public GCOVRecord { |
| public: |
| GCOVFunction(const DISubprogram *SP, Function *F, raw_ostream *os, |
| uint32_t Ident, bool UseCfgChecksum, bool ExitBlockBeforeBody) |
| : SP(SP), Ident(Ident), UseCfgChecksum(UseCfgChecksum), CfgChecksum(0), |
| ReturnBlock(1, os) { |
| this->os = os; |
| |
| DEBUG(dbgs() << "Function: " << getFunctionName(SP) << "\n"); |
| |
| uint32_t i = 0; |
| for (auto &BB : *F) { |
| // Skip index 1 if it's assigned to the ReturnBlock. |
| if (i == 1 && ExitBlockBeforeBody) |
| ++i; |
| Blocks.insert(std::make_pair(&BB, GCOVBlock(i++, os))); |
| } |
| if (!ExitBlockBeforeBody) |
| ReturnBlock.Number = i; |
| |
| std::string FunctionNameAndLine; |
| raw_string_ostream FNLOS(FunctionNameAndLine); |
| FNLOS << getFunctionName(SP) << SP->getLine(); |
| FNLOS.flush(); |
| FuncChecksum = hash_value(FunctionNameAndLine); |
| } |
| |
| GCOVBlock &getBlock(BasicBlock *BB) { |
| return Blocks.find(BB)->second; |
| } |
| |
| GCOVBlock &getReturnBlock() { |
| return ReturnBlock; |
| } |
| |
| std::string getEdgeDestinations() { |
| std::string EdgeDestinations; |
| raw_string_ostream EDOS(EdgeDestinations); |
| Function *F = Blocks.begin()->first->getParent(); |
| for (BasicBlock &I : *F) { |
| GCOVBlock &Block = getBlock(&I); |
| for (int i = 0, e = Block.OutEdges.size(); i != e; ++i) |
| EDOS << Block.OutEdges[i]->Number; |
| } |
| return EdgeDestinations; |
| } |
| |
| uint32_t getFuncChecksum() { |
| return FuncChecksum; |
| } |
| |
| void setCfgChecksum(uint32_t Checksum) { |
| CfgChecksum = Checksum; |
| } |
| |
| void writeOut() { |
| writeBytes(FunctionTag, 4); |
| uint32_t BlockLen = 1 + 1 + 1 + lengthOfGCOVString(getFunctionName(SP)) + |
| 1 + lengthOfGCOVString(SP->getFilename()) + 1; |
| if (UseCfgChecksum) |
| ++BlockLen; |
| write(BlockLen); |
| write(Ident); |
| write(FuncChecksum); |
| if (UseCfgChecksum) |
| write(CfgChecksum); |
| writeGCOVString(getFunctionName(SP)); |
| writeGCOVString(SP->getFilename()); |
| write(SP->getLine()); |
| |
| // Emit count of blocks. |
| writeBytes(BlockTag, 4); |
| write(Blocks.size() + 1); |
| for (int i = 0, e = Blocks.size() + 1; i != e; ++i) { |
| write(0); // No flags on our blocks. |
| } |
| DEBUG(dbgs() << Blocks.size() << " blocks.\n"); |
| |
| // Emit edges between blocks. |
| if (Blocks.empty()) return; |
| Function *F = Blocks.begin()->first->getParent(); |
| for (BasicBlock &I : *F) { |
| GCOVBlock &Block = getBlock(&I); |
| if (Block.OutEdges.empty()) continue; |
| |
| writeBytes(EdgeTag, 4); |
| write(Block.OutEdges.size() * 2 + 1); |
| write(Block.Number); |
| for (int i = 0, e = Block.OutEdges.size(); i != e; ++i) { |
| DEBUG(dbgs() << Block.Number << " -> " << Block.OutEdges[i]->Number |
| << "\n"); |
| write(Block.OutEdges[i]->Number); |
| write(0); // no flags |
| } |
| } |
| |
| // Emit lines for each block. |
| for (BasicBlock &I : *F) |
| getBlock(&I).writeOut(); |
| } |
| |
| private: |
| const DISubprogram *SP; |
| uint32_t Ident; |
| uint32_t FuncChecksum; |
| bool UseCfgChecksum; |
| uint32_t CfgChecksum; |
| DenseMap<BasicBlock *, GCOVBlock> Blocks; |
| GCOVBlock ReturnBlock; |
| }; |
| } |
| |
| std::string GCOVProfiler::mangleName(const DICompileUnit *CU, |
| const char *NewStem) { |
| if (NamedMDNode *GCov = M->getNamedMetadata("llvm.gcov")) { |
| for (int i = 0, e = GCov->getNumOperands(); i != e; ++i) { |
| MDNode *N = GCov->getOperand(i); |
| if (N->getNumOperands() != 2) continue; |
| MDString *GCovFile = dyn_cast<MDString>(N->getOperand(0)); |
| MDNode *CompileUnit = dyn_cast<MDNode>(N->getOperand(1)); |
| if (!GCovFile || !CompileUnit) continue; |
| if (CompileUnit == CU) { |
| SmallString<128> Filename = GCovFile->getString(); |
| sys::path::replace_extension(Filename, NewStem); |
| return Filename.str(); |
| } |
| } |
| } |
| |
| SmallString<128> Filename = CU->getFilename(); |
| sys::path::replace_extension(Filename, NewStem); |
| StringRef FName = sys::path::filename(Filename); |
| SmallString<128> CurPath; |
| if (sys::fs::current_path(CurPath)) return FName; |
| sys::path::append(CurPath, FName); |
| return CurPath.str(); |
| } |
| |
| bool GCOVProfiler::runOnModule(Module &M) { |
| this->M = &M; |
| Ctx = &M.getContext(); |
| |
| if (Options.EmitNotes) emitProfileNotes(); |
| if (Options.EmitData) return emitProfileArcs(); |
| return false; |
| } |
| |
| PreservedAnalyses GCOVProfilerPass::run(Module &M, |
| AnalysisManager<Module> &AM) { |
| |
| GCOVProfiler Profiler(GCOVOpts); |
| |
| if (!Profiler.runOnModule(M)) |
| return PreservedAnalyses::all(); |
| |
| return PreservedAnalyses::none(); |
| } |
| |
| static bool functionHasLines(Function &F) { |
| // Check whether this function actually has any source lines. Not only |
| // do these waste space, they also can crash gcov. |
| for (auto &BB : F) { |
| for (auto &I : BB) { |
| // Debug intrinsic locations correspond to the location of the |
| // declaration, not necessarily any statements or expressions. |
| if (isa<DbgInfoIntrinsic>(&I)) continue; |
| |
| const DebugLoc &Loc = I.getDebugLoc(); |
| if (!Loc) |
| continue; |
| |
| // Artificial lines such as calls to the global constructors. |
| if (Loc.getLine() == 0) continue; |
| |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| void GCOVProfiler::emitProfileNotes() { |
| NamedMDNode *CU_Nodes = M->getNamedMetadata("llvm.dbg.cu"); |
| if (!CU_Nodes) return; |
| |
| for (unsigned i = 0, e = CU_Nodes->getNumOperands(); i != e; ++i) { |
| // Each compile unit gets its own .gcno file. This means that whether we run |
| // this pass over the original .o's as they're produced, or run it after |
| // LTO, we'll generate the same .gcno files. |
| |
| auto *CU = cast<DICompileUnit>(CU_Nodes->getOperand(i)); |
| |
| // Skip module skeleton (and module) CUs. |
| if (CU->getDWOId()) |
| continue; |
| |
| std::error_code EC; |
| raw_fd_ostream out(mangleName(CU, "gcno"), EC, sys::fs::F_None); |
| std::string EdgeDestinations; |
| |
| unsigned FunctionIdent = 0; |
| for (auto &F : M->functions()) { |
| DISubprogram *SP = F.getSubprogram(); |
| if (!SP) continue; |
| if (!functionHasLines(F)) continue; |
| |
| // gcov expects every function to start with an entry block that has a |
| // single successor, so split the entry block to make sure of that. |
| BasicBlock &EntryBlock = F.getEntryBlock(); |
| BasicBlock::iterator It = EntryBlock.begin(); |
| while (isa<AllocaInst>(*It) || isa<DbgInfoIntrinsic>(*It)) |
| ++It; |
| EntryBlock.splitBasicBlock(It); |
| |
| Funcs.push_back(make_unique<GCOVFunction>(SP, &F, &out, FunctionIdent++, |
| Options.UseCfgChecksum, |
| Options.ExitBlockBeforeBody)); |
| GCOVFunction &Func = *Funcs.back(); |
| |
| for (auto &BB : F) { |
| GCOVBlock &Block = Func.getBlock(&BB); |
| TerminatorInst *TI = BB.getTerminator(); |
| if (int successors = TI->getNumSuccessors()) { |
| for (int i = 0; i != successors; ++i) { |
| Block.addEdge(Func.getBlock(TI->getSuccessor(i))); |
| } |
| } else if (isa<ReturnInst>(TI)) { |
| Block.addEdge(Func.getReturnBlock()); |
| } |
| |
| uint32_t Line = 0; |
| for (auto &I : BB) { |
| // Debug intrinsic locations correspond to the location of the |
| // declaration, not necessarily any statements or expressions. |
| if (isa<DbgInfoIntrinsic>(&I)) continue; |
| |
| const DebugLoc &Loc = I.getDebugLoc(); |
| if (!Loc) |
| continue; |
| |
| // Artificial lines such as calls to the global constructors. |
| if (Loc.getLine() == 0) continue; |
| |
| if (Line == Loc.getLine()) continue; |
| Line = Loc.getLine(); |
| if (SP != getDISubprogram(Loc.getScope())) |
| continue; |
| |
| GCOVLines &Lines = Block.getFile(SP->getFilename()); |
| Lines.addLine(Loc.getLine()); |
| } |
| } |
| EdgeDestinations += Func.getEdgeDestinations(); |
| } |
| |
| FileChecksums.push_back(hash_value(EdgeDestinations)); |
| out.write("oncg", 4); |
| out.write(ReversedVersion, 4); |
| out.write(reinterpret_cast<char*>(&FileChecksums.back()), 4); |
| |
| for (auto &Func : Funcs) { |
| Func->setCfgChecksum(FileChecksums.back()); |
| Func->writeOut(); |
| } |
| |
| out.write("\0\0\0\0\0\0\0\0", 8); // EOF |
| out.close(); |
| } |
| } |
| |
| bool GCOVProfiler::emitProfileArcs() { |
| NamedMDNode *CU_Nodes = M->getNamedMetadata("llvm.dbg.cu"); |
| if (!CU_Nodes) return false; |
| |
| bool Result = false; |
| bool InsertIndCounterIncrCode = false; |
| for (unsigned i = 0, e = CU_Nodes->getNumOperands(); i != e; ++i) { |
| SmallVector<std::pair<GlobalVariable *, MDNode *>, 8> CountersBySP; |
| for (auto &F : M->functions()) { |
| DISubprogram *SP = F.getSubprogram(); |
| if (!SP) continue; |
| if (!functionHasLines(F)) continue; |
| if (!Result) Result = true; |
| unsigned Edges = 0; |
| for (auto &BB : F) { |
| TerminatorInst *TI = BB.getTerminator(); |
| if (isa<ReturnInst>(TI)) |
| ++Edges; |
| else |
| Edges += TI->getNumSuccessors(); |
| } |
| |
| ArrayType *CounterTy = |
| ArrayType::get(Type::getInt64Ty(*Ctx), Edges); |
| GlobalVariable *Counters = |
| new GlobalVariable(*M, CounterTy, false, |
| GlobalValue::InternalLinkage, |
| Constant::getNullValue(CounterTy), |
| "__llvm_gcov_ctr"); |
| CountersBySP.push_back(std::make_pair(Counters, SP)); |
| |
| UniqueVector<BasicBlock *> ComplexEdgePreds; |
| UniqueVector<BasicBlock *> ComplexEdgeSuccs; |
| |
| unsigned Edge = 0; |
| for (auto &BB : F) { |
| TerminatorInst *TI = BB.getTerminator(); |
| int Successors = isa<ReturnInst>(TI) ? 1 : TI->getNumSuccessors(); |
| if (Successors) { |
| if (Successors == 1) { |
| IRBuilder<> Builder(&*BB.getFirstInsertionPt()); |
| Value *Counter = Builder.CreateConstInBoundsGEP2_64(Counters, 0, |
| Edge); |
| Value *Count = Builder.CreateLoad(Counter); |
| Count = Builder.CreateAdd(Count, Builder.getInt64(1)); |
| Builder.CreateStore(Count, Counter); |
| } else if (BranchInst *BI = dyn_cast<BranchInst>(TI)) { |
| IRBuilder<> Builder(BI); |
| Value *Sel = Builder.CreateSelect(BI->getCondition(), |
| Builder.getInt64(Edge), |
| Builder.getInt64(Edge + 1)); |
| Value *Counter = Builder.CreateInBoundsGEP( |
| Counters->getValueType(), Counters, {Builder.getInt64(0), Sel}); |
| Value *Count = Builder.CreateLoad(Counter); |
| Count = Builder.CreateAdd(Count, Builder.getInt64(1)); |
| Builder.CreateStore(Count, Counter); |
| } else { |
| ComplexEdgePreds.insert(&BB); |
| for (int i = 0; i != Successors; ++i) |
| ComplexEdgeSuccs.insert(TI->getSuccessor(i)); |
| } |
| |
| Edge += Successors; |
| } |
| } |
| |
| if (!ComplexEdgePreds.empty()) { |
| GlobalVariable *EdgeTable = |
| buildEdgeLookupTable(&F, Counters, |
| ComplexEdgePreds, ComplexEdgeSuccs); |
| GlobalVariable *EdgeState = getEdgeStateValue(); |
| |
| for (int i = 0, e = ComplexEdgePreds.size(); i != e; ++i) { |
| IRBuilder<> Builder(&*ComplexEdgePreds[i + 1]->getFirstInsertionPt()); |
| Builder.CreateStore(Builder.getInt32(i), EdgeState); |
| } |
| |
| for (int i = 0, e = ComplexEdgeSuccs.size(); i != e; ++i) { |
| // Call runtime to perform increment. |
| IRBuilder<> Builder(&*ComplexEdgeSuccs[i + 1]->getFirstInsertionPt()); |
| Value *CounterPtrArray = |
| Builder.CreateConstInBoundsGEP2_64(EdgeTable, 0, |
| i * ComplexEdgePreds.size()); |
| |
| // Build code to increment the counter. |
| InsertIndCounterIncrCode = true; |
| Builder.CreateCall(getIncrementIndirectCounterFunc(), |
| {EdgeState, CounterPtrArray}); |
| } |
| } |
| } |
| |
| Function *WriteoutF = insertCounterWriteout(CountersBySP); |
| Function *FlushF = insertFlush(CountersBySP); |
| |
| // Create a small bit of code that registers the "__llvm_gcov_writeout" to |
| // be executed at exit and the "__llvm_gcov_flush" function to be executed |
| // when "__gcov_flush" is called. |
| FunctionType *FTy = FunctionType::get(Type::getVoidTy(*Ctx), false); |
| Function *F = Function::Create(FTy, GlobalValue::InternalLinkage, |
| "__llvm_gcov_init", M); |
| F->setUnnamedAddr(GlobalValue::UnnamedAddr::Global); |
| F->setLinkage(GlobalValue::InternalLinkage); |
| F->addFnAttr(Attribute::NoInline); |
| if (Options.NoRedZone) |
| F->addFnAttr(Attribute::NoRedZone); |
| |
| BasicBlock *BB = BasicBlock::Create(*Ctx, "entry", F); |
| IRBuilder<> Builder(BB); |
| |
| FTy = FunctionType::get(Type::getVoidTy(*Ctx), false); |
| Type *Params[] = { |
| PointerType::get(FTy, 0), |
| PointerType::get(FTy, 0) |
| }; |
| FTy = FunctionType::get(Builder.getVoidTy(), Params, false); |
| |
| // Initialize the environment and register the local writeout and flush |
| // functions. |
| Constant *GCOVInit = M->getOrInsertFunction("llvm_gcov_init", FTy); |
| Builder.CreateCall(GCOVInit, {WriteoutF, FlushF}); |
| Builder.CreateRetVoid(); |
| |
| appendToGlobalCtors(*M, F, 0); |
| } |
| |
| if (InsertIndCounterIncrCode) |
| insertIndirectCounterIncrement(); |
| |
| return Result; |
| } |
| |
| // All edges with successors that aren't branches are "complex", because it |
| // requires complex logic to pick which counter to update. |
| GlobalVariable *GCOVProfiler::buildEdgeLookupTable( |
| Function *F, |
| GlobalVariable *Counters, |
| const UniqueVector<BasicBlock *> &Preds, |
| const UniqueVector<BasicBlock *> &Succs) { |
| // TODO: support invoke, threads. We rely on the fact that nothing can modify |
| // the whole-Module pred edge# between the time we set it and the time we next |
| // read it. Threads and invoke make this untrue. |
| |
| // emit [(succs * preds) x i64*], logically [succ x [pred x i64*]]. |
| size_t TableSize = Succs.size() * Preds.size(); |
| Type *Int64PtrTy = Type::getInt64PtrTy(*Ctx); |
| ArrayType *EdgeTableTy = ArrayType::get(Int64PtrTy, TableSize); |
| |
| std::unique_ptr<Constant * []> EdgeTable(new Constant *[TableSize]); |
| Constant *NullValue = Constant::getNullValue(Int64PtrTy); |
| for (size_t i = 0; i != TableSize; ++i) |
| EdgeTable[i] = NullValue; |
| |
| unsigned Edge = 0; |
| for (BasicBlock &BB : *F) { |
| TerminatorInst *TI = BB.getTerminator(); |
| int Successors = isa<ReturnInst>(TI) ? 1 : TI->getNumSuccessors(); |
| if (Successors > 1 && !isa<BranchInst>(TI) && !isa<ReturnInst>(TI)) { |
| for (int i = 0; i != Successors; ++i) { |
| BasicBlock *Succ = TI->getSuccessor(i); |
| IRBuilder<> Builder(Succ); |
| Value *Counter = Builder.CreateConstInBoundsGEP2_64(Counters, 0, |
| Edge + i); |
| EdgeTable[((Succs.idFor(Succ) - 1) * Preds.size()) + |
| (Preds.idFor(&BB) - 1)] = cast<Constant>(Counter); |
| } |
| } |
| Edge += Successors; |
| } |
| |
| GlobalVariable *EdgeTableGV = |
| new GlobalVariable( |
| *M, EdgeTableTy, true, GlobalValue::InternalLinkage, |
| ConstantArray::get(EdgeTableTy, |
| makeArrayRef(&EdgeTable[0],TableSize)), |
| "__llvm_gcda_edge_table"); |
| EdgeTableGV->setUnnamedAddr(GlobalValue::UnnamedAddr::Global); |
| return EdgeTableGV; |
| } |
| |
| Constant *GCOVProfiler::getStartFileFunc() { |
| Type *Args[] = { |
| Type::getInt8PtrTy(*Ctx), // const char *orig_filename |
| Type::getInt8PtrTy(*Ctx), // const char version[4] |
| Type::getInt32Ty(*Ctx), // uint32_t checksum |
| }; |
| FunctionType *FTy = FunctionType::get(Type::getVoidTy(*Ctx), Args, false); |
| return M->getOrInsertFunction("llvm_gcda_start_file", FTy); |
| } |
| |
| Constant *GCOVProfiler::getIncrementIndirectCounterFunc() { |
| Type *Int32Ty = Type::getInt32Ty(*Ctx); |
| Type *Int64Ty = Type::getInt64Ty(*Ctx); |
| Type *Args[] = { |
| Int32Ty->getPointerTo(), // uint32_t *predecessor |
| Int64Ty->getPointerTo()->getPointerTo() // uint64_t **counters |
| }; |
| FunctionType *FTy = FunctionType::get(Type::getVoidTy(*Ctx), Args, false); |
| return M->getOrInsertFunction("__llvm_gcov_indirect_counter_increment", FTy); |
| } |
| |
| Constant *GCOVProfiler::getEmitFunctionFunc() { |
| Type *Args[] = { |
| Type::getInt32Ty(*Ctx), // uint32_t ident |
| Type::getInt8PtrTy(*Ctx), // const char *function_name |
| Type::getInt32Ty(*Ctx), // uint32_t func_checksum |
| Type::getInt8Ty(*Ctx), // uint8_t use_extra_checksum |
| Type::getInt32Ty(*Ctx), // uint32_t cfg_checksum |
| }; |
| FunctionType *FTy = FunctionType::get(Type::getVoidTy(*Ctx), Args, false); |
| return M->getOrInsertFunction("llvm_gcda_emit_function", FTy); |
| } |
| |
| Constant *GCOVProfiler::getEmitArcsFunc() { |
| Type *Args[] = { |
| Type::getInt32Ty(*Ctx), // uint32_t num_counters |
| Type::getInt64PtrTy(*Ctx), // uint64_t *counters |
| }; |
| FunctionType *FTy = FunctionType::get(Type::getVoidTy(*Ctx), Args, false); |
| return M->getOrInsertFunction("llvm_gcda_emit_arcs", FTy); |
| } |
| |
| Constant *GCOVProfiler::getSummaryInfoFunc() { |
| FunctionType *FTy = FunctionType::get(Type::getVoidTy(*Ctx), false); |
| return M->getOrInsertFunction("llvm_gcda_summary_info", FTy); |
| } |
| |
| Constant *GCOVProfiler::getEndFileFunc() { |
| FunctionType *FTy = FunctionType::get(Type::getVoidTy(*Ctx), false); |
| return M->getOrInsertFunction("llvm_gcda_end_file", FTy); |
| } |
| |
| GlobalVariable *GCOVProfiler::getEdgeStateValue() { |
| GlobalVariable *GV = M->getGlobalVariable("__llvm_gcov_global_state_pred"); |
| if (!GV) { |
| GV = new GlobalVariable(*M, Type::getInt32Ty(*Ctx), false, |
| GlobalValue::InternalLinkage, |
| ConstantInt::get(Type::getInt32Ty(*Ctx), |
| 0xffffffff), |
| "__llvm_gcov_global_state_pred"); |
| GV->setUnnamedAddr(GlobalValue::UnnamedAddr::Global); |
| } |
| return GV; |
| } |
| |
| Function *GCOVProfiler::insertCounterWriteout( |
| ArrayRef<std::pair<GlobalVariable *, MDNode *> > CountersBySP) { |
| FunctionType *WriteoutFTy = FunctionType::get(Type::getVoidTy(*Ctx), false); |
| Function *WriteoutF = M->getFunction("__llvm_gcov_writeout"); |
| if (!WriteoutF) |
| WriteoutF = Function::Create(WriteoutFTy, GlobalValue::InternalLinkage, |
| "__llvm_gcov_writeout", M); |
| WriteoutF->setUnnamedAddr(GlobalValue::UnnamedAddr::Global); |
| WriteoutF->addFnAttr(Attribute::NoInline); |
| if (Options.NoRedZone) |
| WriteoutF->addFnAttr(Attribute::NoRedZone); |
| |
| BasicBlock *BB = BasicBlock::Create(*Ctx, "entry", WriteoutF); |
| IRBuilder<> Builder(BB); |
| |
| Constant *StartFile = getStartFileFunc(); |
| Constant *EmitFunction = getEmitFunctionFunc(); |
| Constant *EmitArcs = getEmitArcsFunc(); |
| Constant *SummaryInfo = getSummaryInfoFunc(); |
| Constant *EndFile = getEndFileFunc(); |
| |
| NamedMDNode *CU_Nodes = M->getNamedMetadata("llvm.dbg.cu"); |
| if (CU_Nodes) { |
| for (unsigned i = 0, e = CU_Nodes->getNumOperands(); i != e; ++i) { |
| auto *CU = cast<DICompileUnit>(CU_Nodes->getOperand(i)); |
| |
| // Skip module skeleton (and module) CUs. |
| if (CU->getDWOId()) |
| continue; |
| |
| std::string FilenameGcda = mangleName(CU, "gcda"); |
| uint32_t CfgChecksum = FileChecksums.empty() ? 0 : FileChecksums[i]; |
| Builder.CreateCall(StartFile, |
| {Builder.CreateGlobalStringPtr(FilenameGcda), |
| Builder.CreateGlobalStringPtr(ReversedVersion), |
| Builder.getInt32(CfgChecksum)}); |
| for (unsigned j = 0, e = CountersBySP.size(); j != e; ++j) { |
| auto *SP = cast_or_null<DISubprogram>(CountersBySP[j].second); |
| uint32_t FuncChecksum = Funcs.empty() ? 0 : Funcs[j]->getFuncChecksum(); |
| Builder.CreateCall( |
| EmitFunction, |
| {Builder.getInt32(j), |
| Options.FunctionNamesInData |
| ? Builder.CreateGlobalStringPtr(getFunctionName(SP)) |
| : Constant::getNullValue(Builder.getInt8PtrTy()), |
| Builder.getInt32(FuncChecksum), |
| Builder.getInt8(Options.UseCfgChecksum), |
| Builder.getInt32(CfgChecksum)}); |
| |
| GlobalVariable *GV = CountersBySP[j].first; |
| unsigned Arcs = |
| cast<ArrayType>(GV->getValueType())->getNumElements(); |
| Builder.CreateCall(EmitArcs, {Builder.getInt32(Arcs), |
| Builder.CreateConstGEP2_64(GV, 0, 0)}); |
| } |
| Builder.CreateCall(SummaryInfo, {}); |
| Builder.CreateCall(EndFile, {}); |
| } |
| } |
| |
| Builder.CreateRetVoid(); |
| return WriteoutF; |
| } |
| |
| void GCOVProfiler::insertIndirectCounterIncrement() { |
| Function *Fn = |
| cast<Function>(GCOVProfiler::getIncrementIndirectCounterFunc()); |
| Fn->setUnnamedAddr(GlobalValue::UnnamedAddr::Global); |
| Fn->setLinkage(GlobalValue::InternalLinkage); |
| Fn->addFnAttr(Attribute::NoInline); |
| if (Options.NoRedZone) |
| Fn->addFnAttr(Attribute::NoRedZone); |
| |
| // Create basic blocks for function. |
| BasicBlock *BB = BasicBlock::Create(*Ctx, "entry", Fn); |
| IRBuilder<> Builder(BB); |
| |
| BasicBlock *PredNotNegOne = BasicBlock::Create(*Ctx, "", Fn); |
| BasicBlock *CounterEnd = BasicBlock::Create(*Ctx, "", Fn); |
| BasicBlock *Exit = BasicBlock::Create(*Ctx, "exit", Fn); |
| |
| // uint32_t pred = *predecessor; |
| // if (pred == 0xffffffff) return; |
| Argument *Arg = &*Fn->arg_begin(); |
| Arg->setName("predecessor"); |
| Value *Pred = Builder.CreateLoad(Arg, "pred"); |
| Value *Cond = Builder.CreateICmpEQ(Pred, Builder.getInt32(0xffffffff)); |
| BranchInst::Create(Exit, PredNotNegOne, Cond, BB); |
| |
| Builder.SetInsertPoint(PredNotNegOne); |
| |
| // uint64_t *counter = counters[pred]; |
| // if (!counter) return; |
| Value *ZExtPred = Builder.CreateZExt(Pred, Builder.getInt64Ty()); |
| Arg = &*std::next(Fn->arg_begin()); |
| Arg->setName("counters"); |
| Value *GEP = Builder.CreateGEP(Type::getInt64PtrTy(*Ctx), Arg, ZExtPred); |
| Value *Counter = Builder.CreateLoad(GEP, "counter"); |
| Cond = Builder.CreateICmpEQ(Counter, |
| Constant::getNullValue( |
| Builder.getInt64Ty()->getPointerTo())); |
| Builder.CreateCondBr(Cond, Exit, CounterEnd); |
| |
| // ++*counter; |
| Builder.SetInsertPoint(CounterEnd); |
| Value *Add = Builder.CreateAdd(Builder.CreateLoad(Counter), |
| Builder.getInt64(1)); |
| Builder.CreateStore(Add, Counter); |
| Builder.CreateBr(Exit); |
| |
| // Fill in the exit block. |
| Builder.SetInsertPoint(Exit); |
| Builder.CreateRetVoid(); |
| } |
| |
| Function *GCOVProfiler:: |
| insertFlush(ArrayRef<std::pair<GlobalVariable*, MDNode*> > CountersBySP) { |
| FunctionType *FTy = FunctionType::get(Type::getVoidTy(*Ctx), false); |
| Function *FlushF = M->getFunction("__llvm_gcov_flush"); |
| if (!FlushF) |
| FlushF = Function::Create(FTy, GlobalValue::InternalLinkage, |
| "__llvm_gcov_flush", M); |
| else |
| FlushF->setLinkage(GlobalValue::InternalLinkage); |
| FlushF->setUnnamedAddr(GlobalValue::UnnamedAddr::Global); |
| FlushF->addFnAttr(Attribute::NoInline); |
| if (Options.NoRedZone) |
| FlushF->addFnAttr(Attribute::NoRedZone); |
| |
| BasicBlock *Entry = BasicBlock::Create(*Ctx, "entry", FlushF); |
| |
| // Write out the current counters. |
| Constant *WriteoutF = M->getFunction("__llvm_gcov_writeout"); |
| assert(WriteoutF && "Need to create the writeout function first!"); |
| |
| IRBuilder<> Builder(Entry); |
| Builder.CreateCall(WriteoutF, {}); |
| |
| // Zero out the counters. |
| for (const auto &I : CountersBySP) { |
| GlobalVariable *GV = I.first; |
| Constant *Null = Constant::getNullValue(GV->getValueType()); |
| Builder.CreateStore(Null, GV); |
| } |
| |
| Type *RetTy = FlushF->getReturnType(); |
| if (RetTy == Type::getVoidTy(*Ctx)) |
| Builder.CreateRetVoid(); |
| else if (RetTy->isIntegerTy()) |
| // Used if __llvm_gcov_flush was implicitly declared. |
| Builder.CreateRet(ConstantInt::get(RetTy, 0)); |
| else |
| report_fatal_error("invalid return type for __llvm_gcov_flush"); |
| |
| return FlushF; |
| } |