Index: lib/Transforms/Instrumentation/GCOVProfiling.cpp =================================================================== --- lib/Transforms/Instrumentation/GCOVProfiling.cpp +++ lib/Transforms/Instrumentation/GCOVProfiling.cpp @@ -16,6 +16,7 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/Hashing.h" +#include "llvm/ADT/MapVector.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/Sequence.h" #include "llvm/ADT/Statistic.h" @@ -89,6 +90,12 @@ bool runOnModule(Module &M, const TargetLibraryInfo &TLI); private: + using GlobalCountersTy = + SmallVector, 8>; + using CUCountersTy = + SmallVector, 4>; + using CUFunctionsTy = MapVector>; + // Create the .gcno files for the Module based on DebugInfo. void emitProfileNotes(); @@ -105,9 +112,10 @@ // Add the function to write out all our counters to the global destructor // list. - Function * - insertCounterWriteout(ArrayRef>); - Function *insertFlush(ArrayRef>); + Function *insertCounterWriteout(CUCountersTy &CUToCounters); + Function *insertFlush(CUCountersTy &CUToCounters); + + static CUFunctionsTy getCUToFunctions(Module &M); enum class GCovFileType { GCNO, GCDA }; std::string mangleName(const DICompileUnit *CU, GCovFileType FileType); @@ -117,12 +125,14 @@ // Reversed, NUL-terminated copy of Options.Version. char ReversedVersion[5]; // Checksum, produced by hash of EdgeDestinations - SmallVector FileChecksums; + DenseMap FileChecksums; Module *M; const TargetLibraryInfo *TLI; LLVMContext *Ctx; - SmallVector, 16> Funcs; + DenseMap, 16>> + CUToFuncs; + CUFunctionsTy CUToFunctions; }; class GCOVProfilerLegacyPass : public ModulePass { @@ -466,6 +476,7 @@ bool GCOVProfiler::runOnModule(Module &M, const TargetLibraryInfo &TLI) { this->M = &M; this->TLI = &TLI; + CUToFunctions = getCUToFunctions(M); Ctx = &M.getContext(); if (Options.EmitNotes) emitProfileNotes(); @@ -524,21 +535,44 @@ 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. +GCOVProfiler::CUFunctionsTy GCOVProfiler::getCUToFunctions(Module &M) { + CUFunctionsTy Map; + NamedMDNode *CU_Nodes = M.getNamedMetadata("llvm.dbg.cu"); + if (!CU_Nodes) + return Map; + + for (auto &F : M.functions()) { + DISubprogram *SP = F.getSubprogram(); + if (!SP) + continue; + if (!functionHasLines(F)) + continue; + // TODO: Functions using scope-based EH are currently not supported. + if (isUsingScopeBasedEH(F)) + continue; - auto *CU = cast(CU_Nodes->getOperand(i)); + DICompileUnit *CU = SP->getUnit(); + if (!CU) + continue; // Skip module skeleton (and module) CUs. if (CU->getDWOId()) continue; + Map[CU].push_back(&F); + } + return Map; +} + +void GCOVProfiler::emitProfileNotes() { + for (auto &CUFs : CUToFunctions) { + // 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 = CUFs.first; + auto &Functions = CUFs.second; + std::error_code EC; raw_fd_ostream out(mangleName(CU, GCovFileType::GCNO), EC, sys::fs::F_None); if (EC) { @@ -548,24 +582,20 @@ } std::string EdgeDestinations; - unsigned FunctionIdent = 0; - for (auto &F : M->functions()) { - DISubprogram *SP = F.getSubprogram(); - if (!SP) continue; - if (!functionHasLines(F)) continue; - // TODO: Functions using scope-based EH are currently not supported. - if (isUsingScopeBasedEH(F)) continue; + auto &Funcs = CUToFuncs[CU]; + for (auto *F : Functions) { + DISubprogram *SP = F->getSubprogram(); // 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 &EntryBlock = F->getEntryBlock(); BasicBlock::iterator It = EntryBlock.begin(); while (shouldKeepInEntry(It)) ++It; EntryBlock.splitBasicBlock(It); - Funcs.push_back(make_unique(SP, &F, &out, FunctionIdent++, + Funcs.push_back(make_unique(SP, F, &out, FunctionIdent++, Options.UseCfgChecksum, Options.ExitBlockBeforeBody)); GCOVFunction &Func = *Funcs.back(); @@ -576,7 +606,7 @@ .getFile(SP->getFilename()) .addLine(SP->getLine()); - for (auto &BB : F) { + for (auto &BB : *F) { GCOVBlock &Block = Func.getBlock(&BB); Instruction *TI = BB.getTerminator(); if (int successors = TI->getNumSuccessors()) { @@ -591,7 +621,8 @@ for (auto &I : BB) { // Debug intrinsic locations correspond to the location of the // declaration, not necessarily any statements or expressions. - if (isa(&I)) continue; + if (isa(&I)) + continue; const DebugLoc &Loc = I.getDebugLoc(); if (!Loc) @@ -601,7 +632,8 @@ if (Loc.getLine() == 0 || Loc.isImplicitCode()) continue; - if (Line == Loc.getLine()) continue; + if (Line == Loc.getLine()) + continue; Line = Loc.getLine(); if (SP != getDISubprogram(Loc.getScope())) continue; @@ -613,39 +645,37 @@ EdgeDestinations += Func.getEdgeDestinations(); } - FileChecksums.push_back(hash_value(EdgeDestinations)); + uint32_t hash = hash_value(EdgeDestinations); + FileChecksums[CU] = hash; out.write("oncg", 4); out.write(ReversedVersion, 4); - out.write(reinterpret_cast(&FileChecksums.back()), 4); + out.write(reinterpret_cast(&hash), 4); for (auto &Func : Funcs) { - Func->setCfgChecksum(FileChecksums.back()); + Func->setCfgChecksum(hash); Func->writeOut(); } - out.write("\0\0\0\0\0\0\0\0", 8); // EOF + 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, 8> CountersBySP; - for (auto &F : M->functions()) { - DISubprogram *SP = F.getSubprogram(); - if (!SP) continue; - if (!functionHasLines(F)) continue; - // TODO: Functions using scope-based EH are currently not supported. - if (isUsingScopeBasedEH(F)) continue; - if (!Result) Result = true; + CUCountersTy CUToCounters; + for (auto &CUFs : CUToFunctions) { + auto *CU = CUFs.first; + CUToCounters.push_back({CU, {}}); + auto &CountersBySP = CUToCounters.back().second; + + for (auto *F : CUFs.second) { + if (!Result) + Result = true; DenseMap, unsigned> EdgeToCounter; unsigned Edges = 0; - for (auto &BB : F) { + for (auto &BB : *F) { Instruction *TI = BB.getTerminator(); if (isa(TI)) { EdgeToCounter[{&BB, nullptr}] = Edges++; @@ -656,18 +686,15 @@ } } - 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)); + 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({Counters, F->getSubprogram()}); // If a BB has several predecessors, use a PHINode to select // the correct counter. - for (auto &BB : F) { + for (auto &BB : *F) { const unsigned EdgeCount = std::distance(pred_begin(&BB), pred_end(&BB)); if (EdgeCount) { @@ -704,40 +731,37 @@ } } } + } - Function *WriteoutF = insertCounterWriteout(CountersBySP); - Function *FlushF = insertFlush(CountersBySP); + Function *WriteoutF = insertCounterWriteout(CUToCounters); + Function *FlushF = insertFlush(CUToCounters); - // 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(); + // 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); - appendToGlobalCtors(*M, F, 0); - } + 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); return Result; } @@ -800,8 +824,7 @@ return M->getOrInsertFunction("llvm_gcda_end_file", FTy); } -Function *GCOVProfiler::insertCounterWriteout( - ArrayRef > CountersBySP) { +Function *GCOVProfiler::insertCounterWriteout(CUCountersTy &CUToCounters) { FunctionType *WriteoutFTy = FunctionType::get(Type::getVoidTy(*Ctx), false); Function *WriteoutF = M->getFunction("__llvm_gcov_writeout"); if (!WriteoutF) @@ -815,216 +838,66 @@ BasicBlock *BB = BasicBlock::Create(*Ctx, "entry", WriteoutF); IRBuilder<> Builder(BB); + if (CUToCounters.empty()) { + Builder.CreateRetVoid(); + return WriteoutF; + } + Constant *StartFile = getStartFileFunc(); Constant *EmitFunction = getEmitFunctionFunc(); Constant *EmitArcs = getEmitArcsFunc(); Constant *SummaryInfo = getSummaryInfoFunc(); Constant *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 FileInfos; - for (int i : llvm::seq(0, CUNodes->getNumOperands())) { - auto *CU = cast(CUNodes->getOperand(i)); - - // Skip module skeleton (and module) CUs. - if (CU->getDWOId()) - continue; + Constant *Version = Builder.CreateGlobalStringPtr(ReversedVersion); + Constant *UseCfgChecksum = Builder.getInt8(Options.UseCfgChecksum); + Constant *NullCharPtr = Options.FunctionNamesInData + ? nullptr + : Constant::getNullValue(Builder.getInt8PtrTy()); + + for (auto &CUCs : CUToCounters) { + auto *CU = CUCs.first; + auto &CountersBySP = CUCs.second; + auto &Funcs = CUToFuncs[CU]; 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)}); + Constant *CfgChecksum = + Builder.getInt32(FileChecksums.empty() ? 0 : FileChecksums[CU]); + + Builder.CreateCall(StartFile, {Builder.CreateGlobalStringPtr(FilenameGcda), + Version, CfgChecksum}); - SmallVector EmitFunctionCallArgsArray; - SmallVector EmitArcsCallArgsArray; for (int j : llvm::seq(0, CountersBySP.size())) { auto *SP = cast_or_null(CountersBySP[j].second); - uint32_t FuncChecksum = Funcs.empty() ? 0 : Funcs[j]->getFuncChecksum(); - EmitFunctionCallArgsArray.push_back(ConstantStruct::get( - EmitFunctionCallArgsTy, + const 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)})); + NullCharPtr ? NullCharPtr + : Builder.CreateGlobalStringPtr(getFunctionName(SP)), + Builder.getInt32(FuncChecksum), UseCfgChecksum, CfgChecksum}); GlobalVariable *GV = CountersBySP[j].first; unsigned Arcs = cast(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; - } + Builder.CreateCall(EmitArcs, {Builder.getInt32(Arcs), + ConstantExpr::getInBoundsGetElementPtr( + GV->getValueType(), GV, TwoZero32s)}); + } - // 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(FileInfoArrayGV, {Builder.getInt32(0), IV}); - auto *StartFileCallArgsPtr = Builder.CreateStructGEP(FileInfoPtr, 0); - auto *StartFileCall = Builder.CreateCall( - StartFile, - {Builder.CreateLoad(Builder.CreateStructGEP(StartFileCallArgsPtr, 0)), - Builder.CreateLoad(Builder.CreateStructGEP(StartFileCallArgsPtr, 1)), - Builder.CreateLoad(Builder.CreateStructGEP(StartFileCallArgsPtr, 2))}); - if (auto AK = TLI->getExtAttrForI32Param(false)) - StartFileCall->addParamAttr(2, AK); - auto *NumCounters = - Builder.CreateLoad(Builder.CreateStructGEP(FileInfoPtr, 1)); - auto *EmitFunctionCallArgsArray = - Builder.CreateLoad(Builder.CreateStructGEP(FileInfoPtr, 2)); - auto *EmitArcsCallArgsArray = - Builder.CreateLoad(Builder.CreateStructGEP(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(EmitFunctionCallArgsArray, {JV}); - auto *EmitFunctionCall = Builder.CreateCall( - EmitFunction, - {Builder.CreateLoad(Builder.CreateStructGEP(EmitFunctionCallArgsPtr, 0)), - Builder.CreateLoad(Builder.CreateStructGEP(EmitFunctionCallArgsPtr, 1)), - Builder.CreateLoad(Builder.CreateStructGEP(EmitFunctionCallArgsPtr, 2)), - Builder.CreateLoad(Builder.CreateStructGEP(EmitFunctionCallArgsPtr, 3)), - Builder.CreateLoad( - Builder.CreateStructGEP(EmitFunctionCallArgsPtr, 4))}); - if (auto AK = TLI->getExtAttrForI32Param(false)) { - EmitFunctionCall->addParamAttr(0, AK); - EmitFunctionCall->addParamAttr(2, AK); - EmitFunctionCall->addParamAttr(3, AK); - EmitFunctionCall->addParamAttr(4, AK); + Builder.CreateCall(SummaryInfo, {}); + Builder.CreateCall(EndFile, {}); } - auto *EmitArcsCallArgsPtr = - Builder.CreateInBoundsGEP(EmitArcsCallArgsArray, {JV}); - auto *EmitArcsCall = Builder.CreateCall( - EmitArcs, - {Builder.CreateLoad(Builder.CreateStructGEP(EmitArcsCallArgsPtr, 0)), - Builder.CreateLoad(Builder.CreateStructGEP(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 > CountersBySP) { +Function *GCOVProfiler::insertFlush(CUCountersTy &CUToCounters) { FunctionType *FTy = FunctionType::get(Type::getVoidTy(*Ctx), false); Function *FlushF = M->getFunction("__llvm_gcov_flush"); if (!FlushF) @@ -1047,10 +920,12 @@ 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); + for (auto &CUCs : CUToCounters) { + for (const auto &I : CUCs.second) { + GlobalVariable *GV = I.first; + Constant *Null = Constant::getNullValue(GV->getValueType()); + Builder.CreateStore(Null, GV); + } } Type *RetTy = FlushF->getReturnType(); Index: test/Transforms/GCOVProfiling/Inputs/empty-function-1.ll =================================================================== --- /dev/null +++ test/Transforms/GCOVProfiling/Inputs/empty-function-1.ll @@ -0,0 +1,3 @@ +define void @empty1() { + ret void +} \ No newline at end of file Index: test/Transforms/GCOVProfiling/Inputs/empty-function-2.ll =================================================================== --- /dev/null +++ test/Transforms/GCOVProfiling/Inputs/empty-function-2.ll @@ -0,0 +1,3 @@ +define void @empty2() { + ret void +} \ No newline at end of file Index: test/Transforms/GCOVProfiling/Inputs/main-calls-empty-functions.ll =================================================================== --- /dev/null +++ test/Transforms/GCOVProfiling/Inputs/main-calls-empty-functions.ll @@ -0,0 +1,8 @@ +define i32 @main() { + call void @empty1() + call void @empty2() + ret i32 0 +} + +declare void @empty1() +declare void @empty2() \ No newline at end of file Index: test/Transforms/GCOVProfiling/function-numbering.ll =================================================================== --- test/Transforms/GCOVProfiling/function-numbering.ll +++ test/Transforms/GCOVProfiling/function-numbering.ll @@ -19,72 +19,12 @@ ; GCDA: @[[FOO:[0-9]+]] = private unnamed_addr constant [4 x i8] c"foo\00" ; GCDA-NOT: @{{[0-9]+}} = private unnamed_addr constant .* c"bar\00" ; GCDA: @[[BAZ:[0-9]+]] = private unnamed_addr constant [4 x i8] c"baz\00" -; GCDA: @__llvm_internal_gcov_emit_function_args.0 = internal unnamed_addr constant -; GCDA-SAME: { i32 0, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @[[FOO]] -; GCDA-SAME: { i32 1, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @[[BAZ]] ; ; GCDA-LABEL: define internal void @__llvm_gcov_writeout() {{.*}} { ; GCDA-NEXT: entry: -; GCDA-NEXT: br label %[[FILE_LOOP_HEADER:.*]] -; -; GCDA: [[FILE_LOOP_HEADER]]: -; GCDA-NEXT: %[[IV:.*]] = phi i32 [ 0, %entry ], [ %[[NEXT_IV:.*]], %[[FILE_LOOP_LATCH:.*]] ] -; GCDA-NEXT: %[[FILE_INFO:.*]] = getelementptr inbounds {{.*}}, {{.*}}* @__llvm_internal_gcov_emit_file_info, i32 0, i32 %[[IV]] -; GCDA-NEXT: %[[START_FILE_ARGS:.*]] = getelementptr inbounds {{.*}}, {{.*}}* %[[FILE_INFO]], i32 0, i32 0 -; GCDA-NEXT: %[[START_FILE_ARG_0_PTR:.*]] = getelementptr inbounds {{.*}}, {{.*}}* %[[START_FILE_ARGS]], i32 0, i32 0 -; GCDA-NEXT: %[[START_FILE_ARG_0:.*]] = load i8*, i8** %[[START_FILE_ARG_0_PTR]] -; GCDA-NEXT: %[[START_FILE_ARG_1_PTR:.*]] = getelementptr inbounds {{.*}}, {{.*}}* %[[START_FILE_ARGS]], i32 0, i32 1 -; GCDA-NEXT: %[[START_FILE_ARG_1:.*]] = load i8*, i8** %[[START_FILE_ARG_1_PTR]] -; GCDA-NEXT: %[[START_FILE_ARG_2_PTR:.*]] = getelementptr inbounds {{.*}}, {{.*}}* %[[START_FILE_ARGS]], i32 0, i32 2 -; GCDA-NEXT: %[[START_FILE_ARG_2:.*]] = load i32, i32* %[[START_FILE_ARG_2_PTR]] -; GCDA-NEXT: call void @llvm_gcda_start_file(i8* %[[START_FILE_ARG_0]], i8* %[[START_FILE_ARG_1]], i32 %[[START_FILE_ARG_2]]) -; GCDA-NEXT: %[[NUM_COUNTERS_PTR:.*]] = getelementptr inbounds {{.*}}, {{.*}}* %[[FILE_INFO]], i32 0, i32 1 -; GCDA-NEXT: %[[NUM_COUNTERS:.*]] = load i32, i32* %[[NUM_COUNTERS_PTR]] -; GCDA-NEXT: %[[EMIT_FUN_ARGS_ARRAY_PTR:.*]] = getelementptr inbounds {{.*}}, {{.*}}* %[[FILE_INFO]], i32 0, i32 2 -; GCDA-NEXT: %[[EMIT_FUN_ARGS_ARRAY:.*]] = load {{.*}}*, {{.*}}** %[[EMIT_FUN_ARGS_ARRAY_PTR]] -; GCDA-NEXT: %[[EMIT_ARCS_ARGS_ARRAY_PTR:.*]] = getelementptr inbounds {{.*}}, {{.*}}* %[[FILE_INFO]], i32 0, i32 3 -; GCDA-NEXT: %[[EMIT_ARCS_ARGS_ARRAY:.*]] = load {{.*}}*, {{.*}}** %[[EMIT_ARCS_ARGS_ARRAY_PTR]] -; GCDA-NEXT: %[[ENTER_COUNTER_LOOP_COND:.*]] = icmp slt i32 0, %[[NUM_COUNTERS]] -; GCDA-NEXT: br i1 %[[ENTER_COUNTER_LOOP_COND]], label %[[COUNTER_LOOP:.*]], label %[[FILE_LOOP_LATCH]] -; -; GCDA: [[COUNTER_LOOP]]: -; GCDA-NEXT: %[[JV:.*]] = phi i32 [ 0, %[[FILE_LOOP_HEADER]] ], [ %[[NEXT_JV:.*]], %[[COUNTER_LOOP]] ] -; GCDA-NEXT: %[[EMIT_FUN_ARGS:.*]] = getelementptr inbounds {{.*}}, {{.*}}* %[[EMIT_FUN_ARGS_ARRAY]], i32 %[[JV]] -; GCDA-NEXT: %[[EMIT_FUN_ARG_0_PTR:.*]] = getelementptr inbounds {{.*}}, {{.*}}* %[[EMIT_FUN_ARGS]], i32 0, i32 0 -; GCDA-NEXT: %[[EMIT_FUN_ARG_0:.*]] = load i32, i32* %[[EMIT_FUN_ARG_0_PTR]] -; GCDA-NEXT: %[[EMIT_FUN_ARG_1_PTR:.*]] = getelementptr inbounds {{.*}}, {{.*}}* %[[EMIT_FUN_ARGS]], i32 0, i32 1 -; GCDA-NEXT: %[[EMIT_FUN_ARG_1:.*]] = load i8*, i8** %[[EMIT_FUN_ARG_1_PTR]] -; GCDA-NEXT: %[[EMIT_FUN_ARG_2_PTR:.*]] = getelementptr inbounds {{.*}}, {{.*}}* %[[EMIT_FUN_ARGS]], i32 0, i32 2 -; GCDA-NEXT: %[[EMIT_FUN_ARG_2:.*]] = load i32, i32* %[[EMIT_FUN_ARG_2_PTR]] -; GCDA-NEXT: %[[EMIT_FUN_ARG_3_PTR:.*]] = getelementptr inbounds {{.*}}, {{.*}}* %[[EMIT_FUN_ARGS]], i32 0, i32 3 -; GCDA-NEXT: %[[EMIT_FUN_ARG_3:.*]] = load i8, i8* %[[EMIT_FUN_ARG_3_PTR]] -; GCDA-NEXT: %[[EMIT_FUN_ARG_4_PTR:.*]] = getelementptr inbounds {{.*}}, {{.*}}* %[[EMIT_FUN_ARGS]], i32 0, i32 4 -; GCDA-NEXT: %[[EMIT_FUN_ARG_4:.*]] = load i32, i32* %[[EMIT_FUN_ARG_4_PTR]] -; GCDA-NEXT: call void @llvm_gcda_emit_function(i32 %[[EMIT_FUN_ARG_0]], -; GCDA-SAME: i8* %[[EMIT_FUN_ARG_1]], -; GCDA-SAME: i32 %[[EMIT_FUN_ARG_2]], -; GCDA-SAME: i8 %[[EMIT_FUN_ARG_3]], -; GCDA-SAME: i32 %[[EMIT_FUN_ARG_4]]) -; GCDA-NEXT: %[[EMIT_ARCS_ARGS:.*]] = getelementptr inbounds {{.*}}, {{.*}}* %[[EMIT_ARCS_ARGS_ARRAY]], i32 %[[JV]] -; GCDA-NEXT: %[[EMIT_ARCS_ARG_0_PTR:.*]] = getelementptr inbounds {{.*}}, {{.*}}* %[[EMIT_ARCS_ARGS]], i32 0, i32 0 -; GCDA-NEXT: %[[EMIT_ARCS_ARG_0:.*]] = load i32, i32* %[[EMIT_ARCS_ARG_0_PTR]] -; GCDA-NEXT: %[[EMIT_ARCS_ARG_1_PTR:.*]] = getelementptr inbounds {{.*}}, {{.*}}* %[[EMIT_ARCS_ARGS]], i32 0, i32 1 -; GCDA-NEXT: %[[EMIT_ARCS_ARG_1:.*]] = load i64*, i64** %[[EMIT_ARCS_ARG_1_PTR]] -; GCDA-NEXT: call void @llvm_gcda_emit_arcs(i32 %[[EMIT_ARCS_ARG_0]], -; GCDA-SAME: i64* %[[EMIT_ARCS_ARG_1]]) -; GCDA-NEXT: %[[NEXT_JV]] = add i32 %[[JV]], 1 -; GCDA-NEXT: %[[COUNTER_LOOP_COND:.*]] = icmp slt i32 %[[NEXT_JV]], %[[NUM_COUNTERS]] -; GCDA-NEXT: br i1 %[[COUNTER_LOOP_COND]], label %[[COUNTER_LOOP]], label %[[FILE_LOOP_LATCH]] -; -; GCDA: [[FILE_LOOP_LATCH]]: -; GCDA-NEXT: call void @llvm_gcda_summary_info() -; GCDA-NEXT: call void @llvm_gcda_end_file() -; GCDA-NEXT: %[[NEXT_IV]] = add i32 %[[IV]], 1 -; GCDA-NEXT: %[[FILE_LOOP_COND:.*]] = icmp slt i32 %[[NEXT_IV]], 1 -; GCDA-NEXT: br i1 %[[FILE_LOOP_COND]], label %[[FILE_LOOP_HEADER]], label %[[EXIT:.*]] -; -; GCDA: [[EXIT]]: -; GCDA-NEXT: ret void +; GCDA: call void @llvm_gcda_emit_function({{.*}} @[[FOO]]{{.*}}) +; GCDA: call void @llvm_gcda_emit_function({{.*}} @[[BAZ]]{{.*}}) +; GCDA: ret void ; GCNO: == foo (0) @ ; GCNO-NOT: == bar ({{[0-9]+}}) @ Index: test/Transforms/GCOVProfiling/multiple-cus.ll =================================================================== --- /dev/null +++ test/Transforms/GCOVProfiling/multiple-cus.ll @@ -0,0 +1,14 @@ +; RUN: opt -debugify %S/Inputs/empty-function-1.ll -o %t.1.bc +; RUN: opt -debugify %S/Inputs/empty-function-2.ll -o %t.2.bc +; RUN: opt -debugify %S/Inputs/main-calls-empty-functions.ll -o %t.main.bc +; RUN: llvm-link %t.1.bc %t.2.bc %t.main.bc -o %t.lto.bc +; RUN: opt -S -insert-gcov-profiling %t.lto.bc -o - | FileCheck %s + +; CHECK: @[[EMPTYONE:[0-9]+]] = private unnamed_addr constant {{.*}}empty-function-1.gcda{{.*}} +; CHECK: @[[EMPTYTWO:[0-9]+]] = private unnamed_addr constant {{.*}}empty-function-2.gcda{{.*}} +; CHECK: @[[MAIN:[0-9]+]] = private unnamed_addr constant {{.*}}main-calls-empty-functions.gcda{{.*}} + +; CHECK: define internal void @__llvm_gcov_writeout(){{.*}} +; CHECK: {{.*}}call void @llvm_gcda_start_file({{.*}} @[[EMPTYONE]]{{.*}}) +; CHECK: {{.*}}call void @llvm_gcda_start_file({{.*}} @[[EMPTYTWO]]{{.*}}) +; CHECK: {{.*}}call void @llvm_gcda_start_file({{.*}} @[[MAIN]]{{.*}})