diff options
Diffstat (limited to 'llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp')
| -rw-r--r-- | llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp | 1229 |
1 files changed, 1229 insertions, 0 deletions
diff --git a/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp b/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp new file mode 100644 index 0000000000000..ac6082441eaeb --- /dev/null +++ b/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp @@ -0,0 +1,1229 @@ +//===- GCOVProfiling.cpp - Insert edge counters for gcov profiling --------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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/Sequence.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/Analysis/EHPersonalities.h" +#include "llvm/Analysis/TargetLibraryInfo.h" +#include "llvm/IR/CFG.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/Regex.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Transforms/Instrumentation.h" +#include "llvm/Transforms/Instrumentation/GCOVProfiler.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, + std::function<const TargetLibraryInfo &(Function &F)> GetTLI); + +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(); + + bool isFunctionInstrumented(const Function &F); + std::vector<Regex> createRegexesFromString(StringRef RegexesStr); + static bool doesFilenameMatchARegex(StringRef Filename, + std::vector<Regex> &Regexes); + + // Get pointers to the functions in the runtime library. + FunctionCallee getStartFileFunc(const TargetLibraryInfo *TLI); + FunctionCallee getEmitFunctionFunc(const TargetLibraryInfo *TLI); + FunctionCallee getEmitArcsFunc(const TargetLibraryInfo *TLI); + FunctionCallee getSummaryInfoFunc(); + FunctionCallee getEndFileFunc(); + + // 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 AddFlushBeforeForkAndExec(); + + enum class GCovFileType { GCNO, GCDA }; + std::string mangleName(const DICompileUnit *CU, GCovFileType FileType); + + 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; + std::function<const TargetLibraryInfo &(Function &F)> GetTLI; + LLVMContext *Ctx; + SmallVector<std::unique_ptr<GCOVFunction>, 16> Funcs; + std::vector<Regex> FilterRe; + std::vector<Regex> ExcludeRe; + StringMap<bool> InstrumentedFiles; +}; + +class GCOVProfilerLegacyPass : public ModulePass { +public: + static char ID; + GCOVProfilerLegacyPass() + : GCOVProfilerLegacyPass(GCOVOptions::getDefault()) {} + GCOVProfilerLegacyPass(const GCOVOptions &Opts) + : ModulePass(ID), Profiler(Opts) { + initializeGCOVProfilerLegacyPassPass(*PassRegistry::getPassRegistry()); + } + StringRef getPassName() const override { return "GCOV Profiler"; } + + bool runOnModule(Module &M) override { + return Profiler.runOnModule(M, [this](Function &F) -> TargetLibraryInfo & { + return getAnalysis<TargetLibraryInfoWrapperPass>().getTLI(F); + }); + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.addRequired<TargetLibraryInfoWrapperPass>(); + } + +private: + GCOVProfiler Profiler; +}; +} + +char GCOVProfilerLegacyPass::ID = 0; +INITIALIZE_PASS_BEGIN( + GCOVProfilerLegacyPass, "insert-gcov-profiling", + "Insert instrumentation for GCOV profiling", false, false) +INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass) +INITIALIZE_PASS_END( + 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(); +} + +/// Extract a filename for a DISubprogram. +/// +/// Prefer relative paths in the coverage notes. Clang also may split +/// up absolute paths into a directory and filename component. When +/// the relative path doesn't exist, reconstruct the absolute path. +static SmallString<128> getFilename(const DISubprogram *SP) { + SmallString<128> Path; + StringRef RelPath = SP->getFilename(); + if (sys::fs::exists(RelPath)) + Path = RelPath; + else + sys::path::append(Path, SP->getDirectory(), SP->getFilename()); + return Path; +} + +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: + std::string 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) { + return LinesByFile.try_emplace(Filename, Filename, os).first->second; + } + + 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); + + llvm::sort(SortedLinesByFile, [](StringMapEntry<GCOVLines> *LHS, + StringMapEntry<GCOVLines> *RHS) { + return LHS->getKey() < RHS->getKey(); + }); + for (auto &I : SortedLinesByFile) + I->getValue().writeOut(); + write(0); + write(0); + } + + 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; + + LLVM_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); + SmallString<128> Filename = getFilename(SP); + uint32_t BlockLen = 1 + 1 + 1 + lengthOfGCOVString(getFunctionName(SP)) + + 1 + lengthOfGCOVString(Filename) + 1; + if (UseCfgChecksum) + ++BlockLen; + write(BlockLen); + write(Ident); + write(FuncChecksum); + if (UseCfgChecksum) + write(CfgChecksum); + writeGCOVString(getFunctionName(SP)); + writeGCOVString(Filename); + 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. + } + LLVM_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) { + LLVM_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; + }; +} + +// RegexesStr is a string containing differents regex separated by a semi-colon. +// For example "foo\..*$;bar\..*$". +std::vector<Regex> GCOVProfiler::createRegexesFromString(StringRef RegexesStr) { + std::vector<Regex> Regexes; + while (!RegexesStr.empty()) { + std::pair<StringRef, StringRef> HeadTail = RegexesStr.split(';'); + if (!HeadTail.first.empty()) { + Regex Re(HeadTail.first); + std::string Err; + if (!Re.isValid(Err)) { + Ctx->emitError(Twine("Regex ") + HeadTail.first + + " is not valid: " + Err); + } + Regexes.emplace_back(std::move(Re)); + } + RegexesStr = HeadTail.second; + } + return Regexes; +} + +bool GCOVProfiler::doesFilenameMatchARegex(StringRef Filename, + std::vector<Regex> &Regexes) { + for (Regex &Re : Regexes) { + if (Re.match(Filename)) { + return true; + } + } + return false; +} + +bool GCOVProfiler::isFunctionInstrumented(const Function &F) { + if (FilterRe.empty() && ExcludeRe.empty()) { + return true; + } + SmallString<128> Filename = getFilename(F.getSubprogram()); + auto It = InstrumentedFiles.find(Filename); + if (It != InstrumentedFiles.end()) { + return It->second; + } + + SmallString<256> RealPath; + StringRef RealFilename; + + // Path can be + // /usr/lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8/bits/*.h so for + // such a case we must get the real_path. + if (sys::fs::real_path(Filename, RealPath)) { + // real_path can fail with path like "foo.c". + RealFilename = Filename; + } else { + RealFilename = RealPath; + } + + bool ShouldInstrument; + if (FilterRe.empty()) { + ShouldInstrument = !doesFilenameMatchARegex(RealFilename, ExcludeRe); + } else if (ExcludeRe.empty()) { + ShouldInstrument = doesFilenameMatchARegex(RealFilename, FilterRe); + } else { + ShouldInstrument = doesFilenameMatchARegex(RealFilename, FilterRe) && + !doesFilenameMatchARegex(RealFilename, ExcludeRe); + } + InstrumentedFiles[Filename] = ShouldInstrument; + return ShouldInstrument; +} + +std::string GCOVProfiler::mangleName(const DICompileUnit *CU, + GCovFileType OutputType) { + bool Notes = OutputType == GCovFileType::GCNO; + + if (NamedMDNode *GCov = M->getNamedMetadata("llvm.gcov")) { + for (int i = 0, e = GCov->getNumOperands(); i != e; ++i) { + MDNode *N = GCov->getOperand(i); + bool ThreeElement = N->getNumOperands() == 3; + if (!ThreeElement && N->getNumOperands() != 2) + continue; + if (dyn_cast<MDNode>(N->getOperand(ThreeElement ? 2 : 1)) != CU) + continue; + + if (ThreeElement) { + // These nodes have no mangling to apply, it's stored mangled in the + // bitcode. + MDString *NotesFile = dyn_cast<MDString>(N->getOperand(0)); + MDString *DataFile = dyn_cast<MDString>(N->getOperand(1)); + if (!NotesFile || !DataFile) + continue; + return Notes ? NotesFile->getString() : DataFile->getString(); + } + + MDString *GCovFile = dyn_cast<MDString>(N->getOperand(0)); + if (!GCovFile) + continue; + + SmallString<128> Filename = GCovFile->getString(); + sys::path::replace_extension(Filename, Notes ? "gcno" : "gcda"); + return Filename.str(); + } + } + + SmallString<128> Filename = CU->getFilename(); + sys::path::replace_extension(Filename, Notes ? "gcno" : "gcda"); + 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, std::function<const TargetLibraryInfo &(Function &F)> GetTLI) { + this->M = &M; + this->GetTLI = std::move(GetTLI); + Ctx = &M.getContext(); + + AddFlushBeforeForkAndExec(); + + FilterRe = createRegexesFromString(Options.Filter); + ExcludeRe = createRegexesFromString(Options.Exclude); + + if (Options.EmitNotes) emitProfileNotes(); + if (Options.EmitData) return emitProfileArcs(); + return false; +} + +PreservedAnalyses GCOVProfilerPass::run(Module &M, + ModuleAnalysisManager &AM) { + + GCOVProfiler Profiler(GCOVOpts); + FunctionAnalysisManager &FAM = + AM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager(); + + if (!Profiler.runOnModule(M, [&](Function &F) -> TargetLibraryInfo & { + return FAM.getResult<TargetLibraryAnalysis>(F); + })) + 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; +} + +static bool isUsingScopeBasedEH(Function &F) { + if (!F.hasPersonalityFn()) return false; + + EHPersonality Personality = classifyEHPersonality(F.getPersonalityFn()); + return isScopedEHPersonality(Personality); +} + +static bool shouldKeepInEntry(BasicBlock::iterator It) { + if (isa<AllocaInst>(*It)) return true; + if (isa<DbgInfoIntrinsic>(*It)) return true; + if (auto *II = dyn_cast<IntrinsicInst>(It)) { + if (II->getIntrinsicID() == llvm::Intrinsic::localescape) return true; + } + + return false; +} + +void GCOVProfiler::AddFlushBeforeForkAndExec() { + SmallVector<Instruction *, 2> ForkAndExecs; + for (auto &F : M->functions()) { + auto *TLI = &GetTLI(F); + for (auto &I : instructions(F)) { + if (CallInst *CI = dyn_cast<CallInst>(&I)) { + if (Function *Callee = CI->getCalledFunction()) { + LibFunc LF; + if (TLI->getLibFunc(*Callee, LF) && + (LF == LibFunc_fork || LF == LibFunc_execl || + LF == LibFunc_execle || LF == LibFunc_execlp || + LF == LibFunc_execv || LF == LibFunc_execvp || + LF == LibFunc_execve || LF == LibFunc_execvpe || + LF == LibFunc_execvP)) { + ForkAndExecs.push_back(&I); + } + } + } + } + } + + // We need to split the block after the fork/exec call + // because else the counters for the lines after will be + // the same as before the call. + for (auto I : ForkAndExecs) { + IRBuilder<> Builder(I); + FunctionType *FTy = FunctionType::get(Builder.getVoidTy(), {}, false); + FunctionCallee GCOVFlush = M->getOrInsertFunction("__gcov_flush", FTy); + Builder.CreateCall(GCOVFlush); + I->getParent()->splitBasicBlock(I); + } +} + +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, GCovFileType::GCNO), EC, + sys::fs::OF_None); + if (EC) { + Ctx->emitError(Twine("failed to open coverage notes file for writing: ") + + EC.message()); + continue; + } + + std::string EdgeDestinations; + + unsigned FunctionIdent = 0; + for (auto &F : M->functions()) { + DISubprogram *SP = F.getSubprogram(); + if (!SP) continue; + if (!functionHasLines(F) || !isFunctionInstrumented(F)) + continue; + // TODO: Functions using scope-based EH are currently not supported. + if (isUsingScopeBasedEH(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 (shouldKeepInEntry(It)) + ++It; + EntryBlock.splitBasicBlock(It); + + Funcs.push_back(std::make_unique<GCOVFunction>(SP, &F, &out, FunctionIdent++, + Options.UseCfgChecksum, + Options.ExitBlockBeforeBody)); + GCOVFunction &Func = *Funcs.back(); + + // Add the function line number to the lines of the entry block + // to have a counter for the function definition. + uint32_t Line = SP->getLine(); + auto Filename = getFilename(SP); + Func.getBlock(&EntryBlock).getFile(Filename).addLine(Line); + + for (auto &BB : F) { + GCOVBlock &Block = Func.getBlock(&BB); + Instruction *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()); + } + + 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 || Loc.isImplicitCode()) + continue; + + if (Line == Loc.getLine()) continue; + Line = Loc.getLine(); + if (SP != getDISubprogram(Loc.getScope())) + continue; + + GCOVLines &Lines = Block.getFile(Filename); + Lines.addLine(Loc.getLine()); + } + Line = 0; + } + 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; + 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) || !isFunctionInstrumented(F)) + continue; + // TODO: Functions using scope-based EH are currently not supported. + if (isUsingScopeBasedEH(F)) continue; + if (!Result) Result = true; + + DenseMap<std::pair<BasicBlock *, BasicBlock *>, unsigned> EdgeToCounter; + unsigned Edges = 0; + for (auto &BB : F) { + Instruction *TI = BB.getTerminator(); + if (isa<ReturnInst>(TI)) { + EdgeToCounter[{&BB, nullptr}] = Edges++; + } else { + for (BasicBlock *Succ : successors(TI)) { + EdgeToCounter[{&BB, Succ}] = Edges++; + } + } + } + + 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)); + + // If a BB has several predecessors, use a PHINode to select + // the correct counter. + for (auto &BB : F) { + const unsigned EdgeCount = + std::distance(pred_begin(&BB), pred_end(&BB)); + if (EdgeCount) { + // The phi node must be at the begin of the BB. + IRBuilder<> BuilderForPhi(&*BB.begin()); + Type *Int64PtrTy = Type::getInt64PtrTy(*Ctx); + PHINode *Phi = BuilderForPhi.CreatePHI(Int64PtrTy, EdgeCount); + for (BasicBlock *Pred : predecessors(&BB)) { + auto It = EdgeToCounter.find({Pred, &BB}); + assert(It != EdgeToCounter.end()); + const unsigned Edge = It->second; + Value *EdgeCounter = BuilderForPhi.CreateConstInBoundsGEP2_64( + Counters->getValueType(), Counters, 0, Edge); + Phi->addIncoming(EdgeCounter, Pred); + } + + // Skip phis, landingpads. + IRBuilder<> Builder(&*BB.getFirstInsertionPt()); + Value *Count = Builder.CreateLoad(Builder.getInt64Ty(), Phi); + Count = Builder.CreateAdd(Count, Builder.getInt64(1)); + Builder.CreateStore(Count, Phi); + + Instruction *TI = BB.getTerminator(); + if (isa<ReturnInst>(TI)) { + auto It = EdgeToCounter.find({&BB, nullptr}); + assert(It != EdgeToCounter.end()); + const unsigned Edge = It->second; + Value *Counter = Builder.CreateConstInBoundsGEP2_64( + Counters->getValueType(), Counters, 0, Edge); + Value *Count = Builder.CreateLoad(Builder.getInt64Ty(), Counter); + Count = Builder.CreateAdd(Count, Builder.getInt64(1)); + Builder.CreateStore(Count, Counter); + } + } + } + } + + 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. + FunctionCallee GCOVInit = M->getOrInsertFunction("llvm_gcov_init", FTy); + Builder.CreateCall(GCOVInit, {WriteoutF, FlushF}); + Builder.CreateRetVoid(); + + appendToGlobalCtors(*M, F, 0); + } + + return Result; +} + +FunctionCallee GCOVProfiler::getStartFileFunc(const TargetLibraryInfo *TLI) { + 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); + AttributeList AL; + if (auto AK = TLI->getExtAttrForI32Param(false)) + AL = AL.addParamAttribute(*Ctx, 2, AK); + FunctionCallee Res = M->getOrInsertFunction("llvm_gcda_start_file", FTy, AL); + return Res; +} + +FunctionCallee GCOVProfiler::getEmitFunctionFunc(const TargetLibraryInfo *TLI) { + 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); + AttributeList AL; + if (auto AK = TLI->getExtAttrForI32Param(false)) { + AL = AL.addParamAttribute(*Ctx, 0, AK); + AL = AL.addParamAttribute(*Ctx, 2, AK); + AL = AL.addParamAttribute(*Ctx, 3, AK); + AL = AL.addParamAttribute(*Ctx, 4, AK); + } + return M->getOrInsertFunction("llvm_gcda_emit_function", FTy); +} + +FunctionCallee GCOVProfiler::getEmitArcsFunc(const TargetLibraryInfo *TLI) { + Type *Args[] = { + Type::getInt32Ty(*Ctx), // uint32_t num_counters + Type::getInt64PtrTy(*Ctx), // uint64_t *counters + }; + FunctionType *FTy = FunctionType::get(Type::getVoidTy(*Ctx), Args, false); + AttributeList AL; + if (auto AK = TLI->getExtAttrForI32Param(false)) + AL = AL.addParamAttribute(*Ctx, 0, AK); + return M->getOrInsertFunction("llvm_gcda_emit_arcs", FTy, AL); +} + +FunctionCallee GCOVProfiler::getSummaryInfoFunc() { + FunctionType *FTy = FunctionType::get(Type::getVoidTy(*Ctx), false); + return M->getOrInsertFunction("llvm_gcda_summary_info", FTy); +} + +FunctionCallee GCOVProfiler::getEndFileFunc() { + FunctionType *FTy = FunctionType::get(Type::getVoidTy(*Ctx), false); + return M->getOrInsertFunction("llvm_gcda_end_file", FTy); +} + +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); + + auto *TLI = &GetTLI(*WriteoutF); + + FunctionCallee StartFile = getStartFileFunc(TLI); + FunctionCallee EmitFunction = getEmitFunctionFunc(TLI); + FunctionCallee EmitArcs = getEmitArcsFunc(TLI); + FunctionCallee SummaryInfo = getSummaryInfoFunc(); + FunctionCallee EndFile = getEndFileFunc(); + + NamedMDNode *CUNodes = M->getNamedMetadata("llvm.dbg.cu"); + if (!CUNodes) { + Builder.CreateRetVoid(); + return WriteoutF; + } + + // Collect the relevant data into a large constant data structure that we can + // walk to write out everything. + StructType *StartFileCallArgsTy = StructType::create( + {Builder.getInt8PtrTy(), Builder.getInt8PtrTy(), Builder.getInt32Ty()}); + StructType *EmitFunctionCallArgsTy = StructType::create( + {Builder.getInt32Ty(), Builder.getInt8PtrTy(), Builder.getInt32Ty(), + Builder.getInt8Ty(), Builder.getInt32Ty()}); + StructType *EmitArcsCallArgsTy = StructType::create( + {Builder.getInt32Ty(), Builder.getInt64Ty()->getPointerTo()}); + StructType *FileInfoTy = + StructType::create({StartFileCallArgsTy, Builder.getInt32Ty(), + EmitFunctionCallArgsTy->getPointerTo(), + EmitArcsCallArgsTy->getPointerTo()}); + + Constant *Zero32 = Builder.getInt32(0); + // Build an explicit array of two zeros for use in ConstantExpr GEP building. + Constant *TwoZero32s[] = {Zero32, Zero32}; + + SmallVector<Constant *, 8> FileInfos; + for (int i : llvm::seq<int>(0, CUNodes->getNumOperands())) { + auto *CU = cast<DICompileUnit>(CUNodes->getOperand(i)); + + // Skip module skeleton (and module) CUs. + if (CU->getDWOId()) + continue; + + std::string FilenameGcda = mangleName(CU, GCovFileType::GCDA); + uint32_t CfgChecksum = FileChecksums.empty() ? 0 : FileChecksums[i]; + auto *StartFileCallArgs = ConstantStruct::get( + StartFileCallArgsTy, {Builder.CreateGlobalStringPtr(FilenameGcda), + Builder.CreateGlobalStringPtr(ReversedVersion), + Builder.getInt32(CfgChecksum)}); + + SmallVector<Constant *, 8> EmitFunctionCallArgsArray; + SmallVector<Constant *, 8> EmitArcsCallArgsArray; + for (int j : llvm::seq<int>(0, CountersBySP.size())) { + auto *SP = cast_or_null<DISubprogram>(CountersBySP[j].second); + uint32_t FuncChecksum = Funcs.empty() ? 0 : Funcs[j]->getFuncChecksum(); + EmitFunctionCallArgsArray.push_back(ConstantStruct::get( + EmitFunctionCallArgsTy, + {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(); + EmitArcsCallArgsArray.push_back(ConstantStruct::get( + EmitArcsCallArgsTy, + {Builder.getInt32(Arcs), ConstantExpr::getInBoundsGetElementPtr( + GV->getValueType(), GV, TwoZero32s)})); + } + // Create global arrays for the two emit calls. + int CountersSize = CountersBySP.size(); + assert(CountersSize == (int)EmitFunctionCallArgsArray.size() && + "Mismatched array size!"); + assert(CountersSize == (int)EmitArcsCallArgsArray.size() && + "Mismatched array size!"); + auto *EmitFunctionCallArgsArrayTy = + ArrayType::get(EmitFunctionCallArgsTy, CountersSize); + auto *EmitFunctionCallArgsArrayGV = new GlobalVariable( + *M, EmitFunctionCallArgsArrayTy, /*isConstant*/ true, + GlobalValue::InternalLinkage, + ConstantArray::get(EmitFunctionCallArgsArrayTy, + EmitFunctionCallArgsArray), + Twine("__llvm_internal_gcov_emit_function_args.") + Twine(i)); + auto *EmitArcsCallArgsArrayTy = + ArrayType::get(EmitArcsCallArgsTy, CountersSize); + EmitFunctionCallArgsArrayGV->setUnnamedAddr( + GlobalValue::UnnamedAddr::Global); + auto *EmitArcsCallArgsArrayGV = new GlobalVariable( + *M, EmitArcsCallArgsArrayTy, /*isConstant*/ true, + GlobalValue::InternalLinkage, + ConstantArray::get(EmitArcsCallArgsArrayTy, EmitArcsCallArgsArray), + Twine("__llvm_internal_gcov_emit_arcs_args.") + Twine(i)); + EmitArcsCallArgsArrayGV->setUnnamedAddr(GlobalValue::UnnamedAddr::Global); + + FileInfos.push_back(ConstantStruct::get( + FileInfoTy, + {StartFileCallArgs, Builder.getInt32(CountersSize), + ConstantExpr::getInBoundsGetElementPtr(EmitFunctionCallArgsArrayTy, + EmitFunctionCallArgsArrayGV, + TwoZero32s), + ConstantExpr::getInBoundsGetElementPtr( + EmitArcsCallArgsArrayTy, EmitArcsCallArgsArrayGV, TwoZero32s)})); + } + + // If we didn't find anything to actually emit, bail on out. + if (FileInfos.empty()) { + Builder.CreateRetVoid(); + return WriteoutF; + } + + // To simplify code, we cap the number of file infos we write out to fit + // easily in a 32-bit signed integer. This gives consistent behavior between + // 32-bit and 64-bit systems without requiring (potentially very slow) 64-bit + // operations on 32-bit systems. It also seems unreasonable to try to handle + // more than 2 billion files. + if ((int64_t)FileInfos.size() > (int64_t)INT_MAX) + FileInfos.resize(INT_MAX); + + // Create a global for the entire data structure so we can walk it more + // easily. + auto *FileInfoArrayTy = ArrayType::get(FileInfoTy, FileInfos.size()); + auto *FileInfoArrayGV = new GlobalVariable( + *M, FileInfoArrayTy, /*isConstant*/ true, GlobalValue::InternalLinkage, + ConstantArray::get(FileInfoArrayTy, FileInfos), + "__llvm_internal_gcov_emit_file_info"); + FileInfoArrayGV->setUnnamedAddr(GlobalValue::UnnamedAddr::Global); + + // Create the CFG for walking this data structure. + auto *FileLoopHeader = + BasicBlock::Create(*Ctx, "file.loop.header", WriteoutF); + auto *CounterLoopHeader = + BasicBlock::Create(*Ctx, "counter.loop.header", WriteoutF); + auto *FileLoopLatch = BasicBlock::Create(*Ctx, "file.loop.latch", WriteoutF); + auto *ExitBB = BasicBlock::Create(*Ctx, "exit", WriteoutF); + + // We always have at least one file, so just branch to the header. + Builder.CreateBr(FileLoopHeader); + + // The index into the files structure is our loop induction variable. + Builder.SetInsertPoint(FileLoopHeader); + PHINode *IV = + Builder.CreatePHI(Builder.getInt32Ty(), /*NumReservedValues*/ 2); + IV->addIncoming(Builder.getInt32(0), BB); + auto *FileInfoPtr = Builder.CreateInBoundsGEP( + FileInfoArrayTy, FileInfoArrayGV, {Builder.getInt32(0), IV}); + auto *StartFileCallArgsPtr = + Builder.CreateStructGEP(FileInfoTy, FileInfoPtr, 0); + auto *StartFileCall = Builder.CreateCall( + StartFile, + {Builder.CreateLoad(StartFileCallArgsTy->getElementType(0), + Builder.CreateStructGEP(StartFileCallArgsTy, + StartFileCallArgsPtr, 0)), + Builder.CreateLoad(StartFileCallArgsTy->getElementType(1), + Builder.CreateStructGEP(StartFileCallArgsTy, + StartFileCallArgsPtr, 1)), + Builder.CreateLoad(StartFileCallArgsTy->getElementType(2), + Builder.CreateStructGEP(StartFileCallArgsTy, + StartFileCallArgsPtr, 2))}); + if (auto AK = TLI->getExtAttrForI32Param(false)) + StartFileCall->addParamAttr(2, AK); + auto *NumCounters = + Builder.CreateLoad(FileInfoTy->getElementType(1), + Builder.CreateStructGEP(FileInfoTy, FileInfoPtr, 1)); + auto *EmitFunctionCallArgsArray = + Builder.CreateLoad(FileInfoTy->getElementType(2), + Builder.CreateStructGEP(FileInfoTy, FileInfoPtr, 2)); + auto *EmitArcsCallArgsArray = + Builder.CreateLoad(FileInfoTy->getElementType(3), + Builder.CreateStructGEP(FileInfoTy, FileInfoPtr, 3)); + auto *EnterCounterLoopCond = + Builder.CreateICmpSLT(Builder.getInt32(0), NumCounters); + Builder.CreateCondBr(EnterCounterLoopCond, CounterLoopHeader, FileLoopLatch); + + Builder.SetInsertPoint(CounterLoopHeader); + auto *JV = Builder.CreatePHI(Builder.getInt32Ty(), /*NumReservedValues*/ 2); + JV->addIncoming(Builder.getInt32(0), FileLoopHeader); + auto *EmitFunctionCallArgsPtr = Builder.CreateInBoundsGEP( + EmitFunctionCallArgsTy, EmitFunctionCallArgsArray, JV); + auto *EmitFunctionCall = Builder.CreateCall( + EmitFunction, + {Builder.CreateLoad(EmitFunctionCallArgsTy->getElementType(0), + Builder.CreateStructGEP(EmitFunctionCallArgsTy, + EmitFunctionCallArgsPtr, 0)), + Builder.CreateLoad(EmitFunctionCallArgsTy->getElementType(1), + Builder.CreateStructGEP(EmitFunctionCallArgsTy, + EmitFunctionCallArgsPtr, 1)), + Builder.CreateLoad(EmitFunctionCallArgsTy->getElementType(2), + Builder.CreateStructGEP(EmitFunctionCallArgsTy, + EmitFunctionCallArgsPtr, 2)), + Builder.CreateLoad(EmitFunctionCallArgsTy->getElementType(3), + Builder.CreateStructGEP(EmitFunctionCallArgsTy, + EmitFunctionCallArgsPtr, 3)), + Builder.CreateLoad(EmitFunctionCallArgsTy->getElementType(4), + Builder.CreateStructGEP(EmitFunctionCallArgsTy, + EmitFunctionCallArgsPtr, + 4))}); + if (auto AK = TLI->getExtAttrForI32Param(false)) { + EmitFunctionCall->addParamAttr(0, AK); + EmitFunctionCall->addParamAttr(2, AK); + EmitFunctionCall->addParamAttr(3, AK); + EmitFunctionCall->addParamAttr(4, AK); + } + auto *EmitArcsCallArgsPtr = + Builder.CreateInBoundsGEP(EmitArcsCallArgsTy, EmitArcsCallArgsArray, JV); + auto *EmitArcsCall = Builder.CreateCall( + EmitArcs, + {Builder.CreateLoad( + EmitArcsCallArgsTy->getElementType(0), + Builder.CreateStructGEP(EmitArcsCallArgsTy, EmitArcsCallArgsPtr, 0)), + Builder.CreateLoad(EmitArcsCallArgsTy->getElementType(1), + Builder.CreateStructGEP(EmitArcsCallArgsTy, + EmitArcsCallArgsPtr, 1))}); + if (auto AK = TLI->getExtAttrForI32Param(false)) + EmitArcsCall->addParamAttr(0, AK); + auto *NextJV = Builder.CreateAdd(JV, Builder.getInt32(1)); + auto *CounterLoopCond = Builder.CreateICmpSLT(NextJV, NumCounters); + Builder.CreateCondBr(CounterLoopCond, CounterLoopHeader, FileLoopLatch); + JV->addIncoming(NextJV, CounterLoopHeader); + + Builder.SetInsertPoint(FileLoopLatch); + Builder.CreateCall(SummaryInfo, {}); + Builder.CreateCall(EndFile, {}); + auto *NextIV = Builder.CreateAdd(IV, Builder.getInt32(1)); + auto *FileLoopCond = + Builder.CreateICmpSLT(NextIV, Builder.getInt32(FileInfos.size())); + Builder.CreateCondBr(FileLoopCond, FileLoopHeader, ExitBB); + IV->addIncoming(NextIV, FileLoopLatch); + + Builder.SetInsertPoint(ExitBB); + Builder.CreateRetVoid(); + + return WriteoutF; +} + +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. + Function *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; +} |
