From ee02499a8fb64f9710b4543f2f46c5305dcb11fc Mon Sep 17 00:00:00 2001 From: Alex Lorenz Date: Mon, 4 Aug 2014 18:41:51 +0000 Subject: [PATCH] Add coverage mapping generation. This patch adds the '-fcoverage-mapping' option which allows clang to generate the coverage mapping information that can be used to provide code coverage analysis using the execution counts obtained from the instrumentation based profiling (-fprofile-instr-generate). llvm-svn: 214752 --- clang/include/clang/CodeGen/CodeGenABITypes.h | 4 +- clang/include/clang/CodeGen/ModuleBuilder.h | 4 +- clang/include/clang/Driver/Options.td | 3 + .../include/clang/Frontend/CodeGenOptions.def | 2 + clang/lib/CodeGen/CodeGenABITypes.cpp | 6 +- clang/lib/CodeGen/CodeGenAction.cpp | 17 +- clang/lib/CodeGen/CodeGenFunction.cpp | 1 + clang/lib/CodeGen/CodeGenModule.cpp | 89 +- clang/lib/CodeGen/CodeGenModule.h | 25 +- clang/lib/CodeGen/CodeGenPGO.cpp | 154 ++- clang/lib/CodeGen/CodeGenPGO.h | 17 +- clang/lib/CodeGen/CoverageMappingGen.cpp | 1166 +++++++++++++++++ clang/lib/CodeGen/CoverageMappingGen.h | 117 ++ clang/lib/CodeGen/ModuleBuilder.cpp | 17 +- clang/lib/Driver/Tools.cpp | 8 + clang/lib/Frontend/CompilerInvocation.cpp | 1 + 16 files changed, 1577 insertions(+), 54 deletions(-) create mode 100644 clang/lib/CodeGen/CoverageMappingGen.cpp create mode 100644 clang/lib/CodeGen/CoverageMappingGen.h diff --git a/clang/include/clang/CodeGen/CodeGenABITypes.h b/clang/include/clang/CodeGen/CodeGenABITypes.h index 2502982b23fb..a3fed630966b 100644 --- a/clang/include/clang/CodeGen/CodeGenABITypes.h +++ b/clang/include/clang/CodeGen/CodeGenABITypes.h @@ -39,6 +39,7 @@ class CXXRecordDecl; class CodeGenOptions; class DiagnosticsEngine; class ObjCMethodDecl; +class CoverageSourceInfo; namespace CodeGen { class CGFunctionInfo; @@ -47,7 +48,8 @@ class CodeGenModule; class CodeGenABITypes { public: - CodeGenABITypes(ASTContext &C, llvm::Module &M, const llvm::DataLayout &TD); + CodeGenABITypes(ASTContext &C, llvm::Module &M, const llvm::DataLayout &TD, + CoverageSourceInfo *CoverageInfo = nullptr); ~CodeGenABITypes(); /// These methods all forward to methods in the private implementation class diff --git a/clang/include/clang/CodeGen/ModuleBuilder.h b/clang/include/clang/CodeGen/ModuleBuilder.h index 4b7236bfd03b..f4c31074e9cd 100644 --- a/clang/include/clang/CodeGen/ModuleBuilder.h +++ b/clang/include/clang/CodeGen/ModuleBuilder.h @@ -24,6 +24,7 @@ namespace llvm { namespace clang { class DiagnosticsEngine; + class CoverageSourceInfo; class LangOptions; class CodeGenOptions; class TargetOptions; @@ -44,7 +45,8 @@ namespace clang { const std::string &ModuleName, const CodeGenOptions &CGO, const TargetOptions &TO, - llvm::LLVMContext& C); + llvm::LLVMContext& C, + CoverageSourceInfo *CoverageInfo = nullptr); } #endif diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index a32d932cb7c4..ad4dda5cb851 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -408,6 +408,9 @@ def fprofile_instr_use : Flag<["-"], "fprofile-instr-use">, Group; def fprofile_instr_use_EQ : Joined<["-"], "fprofile-instr-use=">, Group, Flags<[CC1Option]>, HelpText<"Use instrumentation data for profile-guided optimization">; +def fcoverage_mapping : Flag<["-"], "fcoverage-mapping">, + Group, Flags<[CC1Option]>, + HelpText<"Generate coverage mapping to enable code coverage analysis">; def fblocks : Flag<["-"], "fblocks">, Group, Flags<[CC1Option]>, HelpText<"Enable the 'blocks' language feature">; diff --git a/clang/include/clang/Frontend/CodeGenOptions.def b/clang/include/clang/Frontend/CodeGenOptions.def index 1d92efeda258..3767b4244f5a 100644 --- a/clang/include/clang/Frontend/CodeGenOptions.def +++ b/clang/include/clang/Frontend/CodeGenOptions.def @@ -88,6 +88,8 @@ VALUE_CODEGENOPT(OptimizeSize, 2, 0) ///< If -Os (==1) or -Oz (==2) is specified CODEGENOPT(ProfileInstrGenerate , 1, 0) ///< Instrument code to generate ///< execution counts to use with PGO. +CODEGENOPT(CoverageMapping , 1, 0) ///< Generate coverage mapping regions to + ///< enable code coverage analysis. /// If -fpcc-struct-return or -freg-struct-return is specified. ENUM_CODEGENOPT(StructReturnConvention, StructReturnConventionKind, 2, SRCK_Default) diff --git a/clang/lib/CodeGen/CodeGenABITypes.cpp b/clang/lib/CodeGen/CodeGenABITypes.cpp index 180cd51940ac..f455e7005cf3 100644 --- a/clang/lib/CodeGen/CodeGenABITypes.cpp +++ b/clang/lib/CodeGen/CodeGenABITypes.cpp @@ -26,9 +26,11 @@ using namespace CodeGen; CodeGenABITypes::CodeGenABITypes(ASTContext &C, llvm::Module &M, - const llvm::DataLayout &TD) + const llvm::DataLayout &TD, + CoverageSourceInfo *CoverageInfo) : CGO(new CodeGenOptions), - CGM(new CodeGen::CodeGenModule(C, *CGO, M, TD, C.getDiagnostics())) { + CGM(new CodeGen::CodeGenModule(C, *CGO, M, TD, C.getDiagnostics(), + CoverageInfo)) { } CodeGenABITypes::~CodeGenABITypes() diff --git a/clang/lib/CodeGen/CodeGenAction.cpp b/clang/lib/CodeGen/CodeGenAction.cpp index 04d2cd9d53e4..d319d220dc5c 100644 --- a/clang/lib/CodeGen/CodeGenAction.cpp +++ b/clang/lib/CodeGen/CodeGenAction.cpp @@ -7,6 +7,7 @@ // //===----------------------------------------------------------------------===// +#include "CoverageMappingGen.h" #include "clang/CodeGen/CodeGenAction.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" @@ -15,6 +16,7 @@ #include "clang/Basic/FileManager.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" +#include "clang/Lex/Preprocessor.h" #include "clang/CodeGen/BackendUtil.h" #include "clang/CodeGen/ModuleBuilder.h" #include "clang/Frontend/CompilerInstance.h" @@ -59,11 +61,13 @@ namespace clang { const TargetOptions &targetopts, const LangOptions &langopts, bool TimePasses, const std::string &infile, llvm::Module *LinkModule, - raw_ostream *OS, LLVMContext &C) + raw_ostream *OS, LLVMContext &C, + CoverageSourceInfo *CoverageInfo = nullptr) : Diags(_Diags), Action(action), CodeGenOpts(compopts), TargetOpts(targetopts), LangOpts(langopts), AsmOutStream(OS), Context(), LLVMIRGeneration("LLVM IR Generation Time"), - Gen(CreateLLVMCodeGen(Diags, infile, compopts, targetopts, C)), + Gen(CreateLLVMCodeGen(Diags, infile, compopts, + targetopts, C, CoverageInfo)), LinkModule(LinkModule) { llvm::TimePassesIsEnabled = TimePasses; } @@ -636,10 +640,17 @@ ASTConsumer *CodeGenAction::CreateASTConsumer(CompilerInstance &CI, LinkModuleToUse = ModuleOrErr.get(); } + CoverageSourceInfo *CoverageInfo = nullptr; + // Add the preprocessor callback only when the coverage mapping is generated. + if (CI.getCodeGenOpts().CoverageMapping) { + CoverageInfo = new CoverageSourceInfo; + CI.getPreprocessor().addPPCallbacks(CoverageInfo); + } BEConsumer = new BackendConsumer(BA, CI.getDiagnostics(), CI.getCodeGenOpts(), CI.getTargetOpts(), CI.getLangOpts(), CI.getFrontendOpts().ShowTimers, InFile, - LinkModuleToUse, OS.release(), *VMContext); + LinkModuleToUse, OS.release(), *VMContext, + CoverageInfo); return BEConsumer; } diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index 8ff02b390e4e..d609ac745bfa 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -829,6 +829,7 @@ void CodeGenFunction::GenerateCode(GlobalDecl GD, llvm::Function *Fn, StartFunction(GD, ResTy, Fn, FnInfo, Args, Loc, BodyRange.getBegin()); // Generate the body of the function. + PGO.checkGlobalDecl(GD); PGO.assignRegionCounters(GD.getDecl(), CurFn); if (isa(FD)) EmitDestructorBody(Args); diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 8b6aae21611a..54c32200f52b 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -21,6 +21,7 @@ #include "CGOpenMPRuntime.h" #include "CodeGenFunction.h" #include "CodeGenPGO.h" +#include "CoverageMappingGen.h" #include "CodeGenTBAA.h" #include "TargetInfo.h" #include "clang/AST/ASTContext.h" @@ -74,7 +75,8 @@ static CGCXXABI *createCXXABI(CodeGenModule &CGM) { CodeGenModule::CodeGenModule(ASTContext &C, const CodeGenOptions &CGO, llvm::Module &M, const llvm::DataLayout &TD, - DiagnosticsEngine &diags) + DiagnosticsEngine &diags, + CoverageSourceInfo *CoverageInfo) : Context(C), LangOpts(C.getLangOpts()), CodeGenOpts(CGO), TheModule(M), Diags(diags), TheDataLayout(TD), Target(C.getTargetInfo()), ABI(createCXXABI(*this)), VMContext(M.getContext()), TBAA(nullptr), @@ -146,6 +148,11 @@ CodeGenModule::CodeGenModule(ASTContext &C, const CodeGenOptions &CGO, getDiags().Report(DiagID) << EC.message(); } } + + // If coverage mapping generation is enabled, create the + // CoverageMappingModuleGen object. + if (CodeGenOpts.CoverageMapping) + CoverageMapping.reset(new CoverageMappingModuleGen(*this, *CoverageInfo)); } CodeGenModule::~CodeGenModule() { @@ -344,6 +351,9 @@ void CodeGenModule::Release() { EmitCtorList(GlobalDtors, "llvm.global_dtors"); EmitGlobalAnnotations(); EmitStaticExternCAliases(); + EmitDeferredUnusedCoverageMappings(); + if (CoverageMapping) + CoverageMapping->emit(); emitLLVMUsed(); if (CodeGenOpts.Autolink && @@ -2989,6 +2999,9 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) { return; EmitGlobal(cast(D)); + // Always provide some coverage mapping + // even for the functions that aren't emitted. + AddDeferredUnusedCoverageMapping(D); break; case Decl::Var: @@ -3138,6 +3151,80 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) { } } +void CodeGenModule::AddDeferredUnusedCoverageMapping(Decl *D) { + // Do we need to generate coverage mapping? + if (!CodeGenOpts.CoverageMapping) + return; + switch (D->getKind()) { + case Decl::CXXConversion: + case Decl::CXXMethod: + case Decl::Function: + case Decl::ObjCMethod: + case Decl::CXXConstructor: + case Decl::CXXDestructor: { + if (!cast(D)->hasBody()) + return; + auto I = DeferredEmptyCoverageMappingDecls.find(D); + if (I == DeferredEmptyCoverageMappingDecls.end()) + DeferredEmptyCoverageMappingDecls[D] = true; + break; + } + default: + break; + }; +} + +void CodeGenModule::ClearUnusedCoverageMapping(const Decl *D) { + // Do we need to generate coverage mapping? + if (!CodeGenOpts.CoverageMapping) + return; + if (const auto *Fn = dyn_cast(D)) { + if (Fn->isTemplateInstantiation()) + ClearUnusedCoverageMapping(Fn->getTemplateInstantiationPattern()); + } + auto I = DeferredEmptyCoverageMappingDecls.find(D); + if (I == DeferredEmptyCoverageMappingDecls.end()) + DeferredEmptyCoverageMappingDecls[D] = false; + else + I->second = false; +} + +void CodeGenModule::EmitDeferredUnusedCoverageMappings() { + for (const auto I : DeferredEmptyCoverageMappingDecls) { + if (!I.second) + continue; + const auto *D = I.first; + switch (D->getKind()) { + case Decl::CXXConversion: + case Decl::CXXMethod: + case Decl::Function: + case Decl::ObjCMethod: { + CodeGenPGO PGO(*this); + GlobalDecl GD(cast(D)); + PGO.emitEmptyCounterMapping(D, getMangledName(GD), + getFunctionLinkage(GD)); + break; + } + case Decl::CXXConstructor: { + CodeGenPGO PGO(*this); + GlobalDecl GD(cast(D), Ctor_Base); + PGO.emitEmptyCounterMapping(D, getMangledName(GD), + getFunctionLinkage(GD)); + break; + } + case Decl::CXXDestructor: { + CodeGenPGO PGO(*this); + GlobalDecl GD(cast(D), Dtor_Base); + PGO.emitEmptyCounterMapping(D, getMangledName(GD), + getFunctionLinkage(GD)); + break; + } + default: + break; + }; + } +} + /// Turns the given pointer into a constant. static llvm::Constant *GetPointerConstant(llvm::LLVMContext &Context, const void *Ptr) { diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index 326e4c7a290b..7b7ee22bdc58 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -73,6 +73,7 @@ class DiagnosticsEngine; class AnnotateAttr; class CXXDestructorDecl; class Module; +class CoverageSourceInfo; namespace CodeGen { @@ -87,6 +88,7 @@ class CGOpenMPRuntime; class CGCUDARuntime; class BlockFieldFlags; class FunctionArgList; +class CoverageMappingModuleGen; struct OrderGlobalInits { unsigned int priority; @@ -477,10 +479,15 @@ class CodeGenModule : public CodeGenTypeCache { std::unique_ptr SanitizerMD; /// @} + + llvm::DenseMap DeferredEmptyCoverageMappingDecls; + + std::unique_ptr CoverageMapping; public: CodeGenModule(ASTContext &C, const CodeGenOptions &CodeGenOpts, llvm::Module &M, const llvm::DataLayout &TD, - DiagnosticsEngine &Diags); + DiagnosticsEngine &Diags, + CoverageSourceInfo *CoverageInfo = nullptr); ~CodeGenModule(); @@ -529,6 +536,10 @@ public: InstrProfStats &getPGOStats() { return PGOStats; } llvm::IndexedInstrProfReader *getPGOReader() const { return PGOReader.get(); } + CoverageMappingModuleGen *getCoverageMapping() const { + return CoverageMapping.get(); + } + llvm::Constant *getStaticLocalDeclAddress(const VarDecl *D) { return StaticLocalDeclMap[D]; } @@ -815,6 +826,18 @@ public: /// Emit code for a single top level declaration. void EmitTopLevelDecl(Decl *D); + /// \brief Stored a deferred empty coverage mapping for an unused + /// and thus uninstrumented top level declaration. + void AddDeferredUnusedCoverageMapping(Decl *D); + + /// \brief Remove the deferred empty coverage mapping as this + /// declaration is actually instrumented. + void ClearUnusedCoverageMapping(const Decl *D); + + /// \brief Emit all the deferred coverage mappings + /// for the uninstrumented functions. + void EmitDeferredUnusedCoverageMappings(); + /// Tell the consumer that this variable has been instantiated. void HandleCXXStaticMemberVarInstantiation(VarDecl *VD); diff --git a/clang/lib/CodeGen/CodeGenPGO.cpp b/clang/lib/CodeGen/CodeGenPGO.cpp index 4def789132a5..e6dbad856c39 100644 --- a/clang/lib/CodeGen/CodeGenPGO.cpp +++ b/clang/lib/CodeGen/CodeGenPGO.cpp @@ -13,6 +13,7 @@ #include "CodeGenPGO.h" #include "CodeGenFunction.h" +#include "CoverageMappingGen.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/StmtVisitor.h" #include "llvm/IR/MDBuilder.h" @@ -24,8 +25,9 @@ using namespace clang; using namespace CodeGen; -void CodeGenPGO::setFuncName(llvm::Function *Fn) { - RawFuncName = Fn->getName(); +void CodeGenPGO::setFuncName(StringRef Name, + llvm::GlobalValue::LinkageTypes Linkage) { + RawFuncName = Name; // Function names may be prefixed with a binary '1' to indicate // that the backend should not modify the symbols due to any platform @@ -33,7 +35,7 @@ void CodeGenPGO::setFuncName(llvm::Function *Fn) { if (RawFuncName[0] == '\1') RawFuncName = RawFuncName.substr(1); - if (!Fn->hasLocalLinkage()) { + if (!llvm::GlobalValue::isLocalLinkage(Linkage)) { PrefixedFuncName.reset(new std::string(RawFuncName)); return; } @@ -49,6 +51,27 @@ void CodeGenPGO::setFuncName(llvm::Function *Fn) { PrefixedFuncName->append(RawFuncName); } +void CodeGenPGO::setFuncName(llvm::Function *Fn) { + setFuncName(Fn->getName(), Fn->getLinkage()); +} + +void CodeGenPGO::setVarLinkage(llvm::GlobalValue::LinkageTypes Linkage) { + // Set the linkage for variables based on the function linkage. Usually, we + // want to match it, but available_externally and extern_weak both have the + // wrong semantics. + VarLinkage = Linkage; + switch (VarLinkage) { + case llvm::GlobalValue::ExternalWeakLinkage: + VarLinkage = llvm::GlobalValue::LinkOnceAnyLinkage; + break; + case llvm::GlobalValue::AvailableExternallyLinkage: + VarLinkage = llvm::GlobalValue::LinkOnceODRLinkage; + break; + default: + break; + } +} + static llvm::Function *getRegisterFunc(CodeGenModule &CGM) { return CGM.getModule().getFunction("__llvm_profile_register_functions"); } @@ -120,37 +143,48 @@ llvm::GlobalVariable *CodeGenPGO::buildDataVar() { auto *Int64Ty = llvm::Type::getInt64Ty(Ctx); auto *Int8PtrTy = llvm::Type::getInt8PtrTy(Ctx); auto *Int64PtrTy = llvm::Type::getInt64PtrTy(Ctx); - llvm::Type *DataTypes[] = { - Int32Ty, Int32Ty, Int64Ty, Int8PtrTy, Int64PtrTy - }; - auto *DataTy = llvm::StructType::get(Ctx, makeArrayRef(DataTypes)); - llvm::Constant *DataVals[] = { - llvm::ConstantInt::get(Int32Ty, getFuncName().size()), - llvm::ConstantInt::get(Int32Ty, NumRegionCounters), - llvm::ConstantInt::get(Int64Ty, FunctionHash), - llvm::ConstantExpr::getBitCast(Name, Int8PtrTy), - llvm::ConstantExpr::getBitCast(RegionCounters, Int64PtrTy) - }; - auto *Data = - new llvm::GlobalVariable(CGM.getModule(), DataTy, true, VarLinkage, - llvm::ConstantStruct::get(DataTy, DataVals), - getFuncVarName("data")); + llvm::GlobalVariable *Data = nullptr; + if (RegionCounters) { + llvm::Type *DataTypes[] = { + Int32Ty, Int32Ty, Int64Ty, Int8PtrTy, Int64PtrTy + }; + auto *DataTy = llvm::StructType::get(Ctx, makeArrayRef(DataTypes)); + llvm::Constant *DataVals[] = { + llvm::ConstantInt::get(Int32Ty, getFuncName().size()), + llvm::ConstantInt::get(Int32Ty, NumRegionCounters), + llvm::ConstantInt::get(Int64Ty, FunctionHash), + llvm::ConstantExpr::getBitCast(Name, Int8PtrTy), + llvm::ConstantExpr::getBitCast(RegionCounters, Int64PtrTy) + }; + Data = + new llvm::GlobalVariable(CGM.getModule(), DataTy, true, VarLinkage, + llvm::ConstantStruct::get(DataTy, DataVals), + getFuncVarName("data")); - // All the data should be packed into an array in its own section. - Data->setSection(getDataSection(CGM)); - Data->setAlignment(8); + // All the data should be packed into an array in its own section. + Data->setSection(getDataSection(CGM)); + Data->setAlignment(8); + } + + // Create coverage mapping data variable. + if (!CoverageMapping.empty()) + CGM.getCoverageMapping()->addFunctionMappingRecord(Name, + getFuncName().size(), + CoverageMapping); // Hide all these symbols so that we correctly get a copy for each // executable. The profile format expects names and counters to be // contiguous, so references into shared objects would be invalid. if (!llvm::GlobalValue::isLocalLinkage(VarLinkage)) { Name->setVisibility(llvm::GlobalValue::HiddenVisibility); - Data->setVisibility(llvm::GlobalValue::HiddenVisibility); - RegionCounters->setVisibility(llvm::GlobalValue::HiddenVisibility); + if (Data) { + Data->setVisibility(llvm::GlobalValue::HiddenVisibility); + RegionCounters->setVisibility(llvm::GlobalValue::HiddenVisibility); + } } // Make sure the data doesn't get deleted. - CGM.addUsedGlobal(Data); + if (Data) CGM.addUsedGlobal(Data); return Data; } @@ -807,6 +841,20 @@ static void emitRuntimeHook(CodeGenModule &CGM) { CGM.addUsedGlobal(User); } +void CodeGenPGO::checkGlobalDecl(GlobalDecl GD) { + // Make sure we only emit coverage mapping for one constructor/destructor. + // Clang emits several functions for the constructor and the destructor of + // a class. Every function is instrumented, but we only want to provide + // coverage for one of them. Because of that we only emit the coverage mapping + // for the base constructor/destructor. + if ((isa(GD.getDecl()) && + GD.getCtorType() != Ctor_Base) || + (isa(GD.getDecl()) && + GD.getDtorType() != Dtor_Base)) { + SkipCoverageMapping = true; + } +} + void CodeGenPGO::assignRegionCounters(const Decl *D, llvm::Function *Fn) { bool InstrumentRegions = CGM.getCodeGenOpts().ProfileInstrGenerate; llvm::IndexedInstrProfReader *PGOReader = CGM.getPGOReader(); @@ -814,27 +862,16 @@ void CodeGenPGO::assignRegionCounters(const Decl *D, llvm::Function *Fn) { return; if (D->isImplicit()) return; + CGM.ClearUnusedCoverageMapping(D); setFuncName(Fn); - - // Set the linkage for variables based on the function linkage. Usually, we - // want to match it, but available_externally and extern_weak both have the - // wrong semantics. - VarLinkage = Fn->getLinkage(); - switch (VarLinkage) { - case llvm::GlobalValue::ExternalWeakLinkage: - VarLinkage = llvm::GlobalValue::LinkOnceAnyLinkage; - break; - case llvm::GlobalValue::AvailableExternallyLinkage: - VarLinkage = llvm::GlobalValue::LinkOnceODRLinkage; - break; - default: - break; - } + setVarLinkage(Fn->getLinkage()); mapRegionCounters(D); if (InstrumentRegions) { emitRuntimeHook(CGM); emitCounterVariables(); + if (CGM.getCodeGenOpts().CoverageMapping) + emitCounterRegionMapping(D); } if (PGOReader) { SourceManager &SM = CGM.getContext().getSourceManager(); @@ -860,6 +897,45 @@ void CodeGenPGO::mapRegionCounters(const Decl *D) { FunctionHash = Walker.Hash.finalize(); } +void CodeGenPGO::emitCounterRegionMapping(const Decl *D) { + if (SkipCoverageMapping) + return; + // Don't map the functions inside the system headers + auto Loc = D->getBody()->getLocStart(); + if (CGM.getContext().getSourceManager().isInSystemHeader(Loc)) + return; + + llvm::raw_string_ostream OS(CoverageMapping); + CoverageMappingGen MappingGen(*CGM.getCoverageMapping(), + CGM.getContext().getSourceManager(), + CGM.getLangOpts(), RegionCounterMap.get(), + NumRegionCounters); + MappingGen.emitCounterMapping(D, OS); + OS.flush(); +} + +void +CodeGenPGO::emitEmptyCounterMapping(const Decl *D, StringRef FuncName, + llvm::GlobalValue::LinkageTypes Linkage) { + if (SkipCoverageMapping) + return; + setFuncName(FuncName, Linkage); + setVarLinkage(Linkage); + + // Don't map the functions inside the system headers + auto Loc = D->getBody()->getLocStart(); + if (CGM.getContext().getSourceManager().isInSystemHeader(Loc)) + return; + + llvm::raw_string_ostream OS(CoverageMapping); + CoverageMappingGen MappingGen(*CGM.getCoverageMapping(), + CGM.getContext().getSourceManager(), + CGM.getLangOpts()); + MappingGen.emitEmptyMapping(D, OS); + OS.flush(); + buildDataVar(); +} + void CodeGenPGO::computeRegionCounts(const Decl *D) { StmtCountMap.reset(new llvm::DenseMap); ComputeRegionCounts Walker(*StmtCountMap, *this); diff --git a/clang/lib/CodeGen/CodeGenPGO.h b/clang/lib/CodeGen/CodeGenPGO.h index 2f4aa660bea3..698b0c069652 100644 --- a/clang/lib/CodeGen/CodeGenPGO.h +++ b/clang/lib/CodeGen/CodeGenPGO.h @@ -42,11 +42,16 @@ private: std::unique_ptr> StmtCountMap; std::unique_ptr> RegionCounts; uint64_t CurrentRegionCount; + std::string CoverageMapping; + /// \brief A flag that is set to true when this function doesn't need + /// to have coverage mapping data. + bool SkipCoverageMapping; public: CodeGenPGO(CodeGenModule &CGM) : CGM(CGM), NumRegionCounters(0), FunctionHash(0), - RegionCounters(nullptr), CurrentRegionCount(0) {} + RegionCounters(nullptr), CurrentRegionCount(0), + SkipCoverageMapping(false) {} /// Whether or not we have PGO region data for the current function. This is /// false both when we have no data at all and when our data has been @@ -99,6 +104,8 @@ public: llvm::MDNode *createBranchWeights(ArrayRef Weights); llvm::MDNode *createLoopWeights(const Stmt *Cond, RegionCounter &Cnt); + /// Check if we need to emit coverage mapping for a given declaration + void checkGlobalDecl(GlobalDecl GD); /// Assign counters to regions and configure them for PGO of a given /// function. Does nothing if instrumentation is not enabled and either /// generates global variables or associates PGO data with each of the @@ -111,9 +118,14 @@ public: void destroyRegionCounters(); /// Emit static initialization code, if any. static llvm::Function *emitInitialization(CodeGenModule &CGM); - + /// Emit a coverage mapping range with a counter zero + /// for an unused declaration. + void emitEmptyCounterMapping(const Decl *D, StringRef FuncName, + llvm::GlobalValue::LinkageTypes Linkage); private: void setFuncName(llvm::Function *Fn); + void setFuncName(StringRef Name, llvm::GlobalValue::LinkageTypes Linkage); + void setVarLinkage(llvm::GlobalValue::LinkageTypes Linkage); void mapRegionCounters(const Decl *D); void computeRegionCounts(const Decl *D); void applyFunctionAttributes(llvm::IndexedInstrProfReader *PGOReader, @@ -122,6 +134,7 @@ private: bool IsInMainFile); void emitCounterVariables(); llvm::GlobalVariable *buildDataVar(); + void emitCounterRegionMapping(const Decl *D); /// Emit code to increment the counter at the given index void emitCounterIncrement(CGBuilderTy &Builder, unsigned Counter); diff --git a/clang/lib/CodeGen/CoverageMappingGen.cpp b/clang/lib/CodeGen/CoverageMappingGen.cpp new file mode 100644 index 000000000000..526d30f20b77 --- /dev/null +++ b/clang/lib/CodeGen/CoverageMappingGen.cpp @@ -0,0 +1,1166 @@ +//===--- CoverageMappingGen.cpp - Coverage mapping generation ---*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Instrumentation-based code coverage mapping generator +// +//===----------------------------------------------------------------------===// + +#include "CoverageMappingGen.h" +#include "CodeGenFunction.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/Lex/Lexer.h" +#include "llvm/ProfileData/InstrProfReader.h" +#include "llvm/ProfileData/CoverageMapping.h" +#include "llvm/ProfileData/CoverageMappingWriter.h" +#include "llvm/Support/FileSystem.h" + +using namespace clang; +using namespace CodeGen; +using namespace llvm::coverage; + +void CoverageSourceInfo::SourceRangeSkipped(SourceRange Range) { + SkippedRanges.push_back(Range); +} + +namespace { + +/// \brief A region of source code that can be mapped to a counter. +struct SourceMappingRegion { + enum RegionFlags { + /// \brief This region won't be emitted if it wasn't extended. + /// This is useful so that we won't emit source ranges for single tokens + /// that we don't really care that much about, like: + /// the '(' token in #define MACRO ( + IgnoreIfNotExtended = 0x0001, + }; + + FileID File, MacroArgumentFile; + + Counter Count; + + /// \brief A statement that initiated the count of Zero. + /// + /// This initiator statement is useful to prevent merging of unreachable + /// regions with different statements that caused the counter to become + /// unreachable. + const Stmt *UnreachableInitiator; + + /// \brief A statement that separates certain mapping regions into groups. + /// + /// The group statement is sometimes useful when we are emitting the source + /// regions not in their correct lexical order, e.g. the regions for the + /// incrementation expression in the 'for' construct. By marking the regions + /// in the incrementation expression with the group statement, we avoid the + /// merging of the regions from the incrementation expression and the loop's + /// body. + const Stmt *Group; + + /// \brief The region's starting location. + SourceLocation LocStart; + + /// \brief The region's ending location. + SourceLocation LocEnd, AlternativeLocEnd; + unsigned Flags; + CounterMappingRegion::RegionKind Kind; + + SourceMappingRegion(FileID File, FileID MacroArgumentFile, Counter Count, + const Stmt *UnreachableInitiator, const Stmt *Group, + SourceLocation LocStart, SourceLocation LocEnd, + unsigned Flags = 0, + CounterMappingRegion::RegionKind Kind = + CounterMappingRegion::CodeRegion) + : File(File), MacroArgumentFile(MacroArgumentFile), Count(Count), + UnreachableInitiator(UnreachableInitiator), Group(Group), + LocStart(LocStart), LocEnd(LocEnd), AlternativeLocEnd(LocStart), + Flags(Flags), Kind(Kind) {} + + bool hasFlag(RegionFlags Flag) const { return (Flags & Flag) != 0; } + + void setFlag(RegionFlags Flag) { Flags |= Flag; } + + void clearFlag(RegionFlags Flag) { Flags &= ~Flag; } + + /// \brief Return true if two regions can be merged together. + bool isMergeable(SourceMappingRegion &R) { + return File == R.File && MacroArgumentFile == R.MacroArgumentFile && + Count == R.Count && UnreachableInitiator == R.UnreachableInitiator && + Group == R.Group && Kind == R.Kind; + } + + /// \brief Merge two regions by extending the 'this' region to cover the + /// given region. + void mergeByExtendingTo(SourceMappingRegion &R) { + LocEnd = R.LocEnd; + AlternativeLocEnd = R.LocStart; + if (hasFlag(IgnoreIfNotExtended)) + clearFlag(IgnoreIfNotExtended); + } +}; + +/// \brief The state of the coverage mapping builder. +struct SourceMappingState { + Counter CurrentRegionCount; + const Stmt *CurrentSourceGroup; + const Stmt *CurrentUnreachableRegionInitiator; + + SourceMappingState(Counter CurrentRegionCount, const Stmt *CurrentSourceGroup, + const Stmt *CurrentUnreachableRegionInitiator) + : CurrentRegionCount(CurrentRegionCount), + CurrentSourceGroup(CurrentSourceGroup), + CurrentUnreachableRegionInitiator(CurrentUnreachableRegionInitiator) {} +}; + +/// \brief Provides the common functionality for the different +/// coverage mapping region builders. +class CoverageMappingBuilder { +public: + CoverageMappingModuleGen &CVM; + SourceManager &SM; + const LangOptions &LangOpts; + +private: + struct FileInfo { + /// \brief The file id that will be used by the coverage mapping system. + unsigned CovMappingFileID; + const FileEntry *Entry; + + FileInfo(unsigned CovMappingFileID, const FileEntry *Entry) + : CovMappingFileID(CovMappingFileID), Entry(Entry) {} + }; + + /// \brief This mapping maps clang's FileIDs to file ids used + /// by the coverage mapping system and clang's file entries. + llvm::SmallDenseMap FileIDMapping; + +public: + /// \brief The statement that corresponds to the current source group. + const Stmt *CurrentSourceGroup; + + /// \brief The statement the initiated the current unreachable region. + const Stmt *CurrentUnreachableRegionInitiator; + + /// \brief The coverage mapping regions for this function + llvm::SmallVector MappingRegions; + /// \brief The source mapping regions for this function. + llvm::SmallVector SourceRegions; + + CoverageMappingBuilder(CoverageMappingModuleGen &CVM, SourceManager &SM, + const LangOptions &LangOpts) + : CVM(CVM), SM(SM), LangOpts(LangOpts), + CurrentSourceGroup(nullptr), + CurrentUnreachableRegionInitiator(nullptr) {} + + /// \brief Return the precise end location for the given token. + SourceLocation getPreciseTokenLocEnd(SourceLocation Loc) { + return Lexer::getLocForEndOfToken(SM.getSpellingLoc(Loc), 0, SM, LangOpts); + } + + /// \brief Create the mapping that maps from the function's file ids to + /// the indices for the translation unit's filenames. + void createFileIDMapping(SmallVectorImpl &Mapping) { + Mapping.resize(FileIDMapping.size(), 0); + for (const auto &I : FileIDMapping) + Mapping[I.second.CovMappingFileID] = CVM.getFileID(I.second.Entry); + } + + /// \brief Get the coverage mapping file id that corresponds to the given + /// clang file id. If such file id doesn't exist, it gets added to the + /// mapping that maps from clang's file ids to coverage mapping file ids. + /// Return true if there was an error getting the coverage mapping file id. + /// An example of an when this function fails is when the region tries + /// to get a coverage file id for a location in a built-in macro. + bool getCoverageFileID(SourceLocation LocStart, FileID File, + FileID SpellingFile, unsigned &Result) { + auto Mapping = FileIDMapping.find(File); + if (Mapping != FileIDMapping.end()) { + Result = Mapping->second.CovMappingFileID; + return false; + } + + auto Entry = SM.getFileEntryForID(SpellingFile); + if (!Entry) + return true; + + Result = FileIDMapping.size(); + FileIDMapping.insert(std::make_pair(File, FileInfo(Result, Entry))); + createFileExpansionRegion(LocStart, File); + return false; + } + + /// \brief Get the coverage mapping file id that corresponds to the given + /// clang file id. + /// Return true if there was an error getting the coverage mapping file id. + bool getExistingCoverageFileID(FileID File, unsigned &Result) { + // Make sure that the file is valid. + if (File.isInvalid()) + return true; + auto Mapping = FileIDMapping.find(File); + if (Mapping != FileIDMapping.end()) { + Result = Mapping->second.CovMappingFileID; + return false; + } + return true; + } + + /// \brief Return true if the given clang's file id has a corresponding + /// coverage file id. + bool hasExistingCoverageFileID(FileID File) const { + return FileIDMapping.count(File); + } + + /// \brief Gather all the regions that were skipped by the preprocessor + /// using the constructs like #if. + void gatherSkippedRegions() { + /// An array of the minimum lineStarts and the maximum lineEnds + /// for mapping regions from the appropriate source files. + llvm::SmallVector, 8> FileLineRanges; + FileLineRanges.resize( + FileIDMapping.size(), + std::make_pair(std::numeric_limits::max(), 0)); + for (const auto &R : MappingRegions) { + FileLineRanges[R.FileID].first = + std::min(FileLineRanges[R.FileID].first, R.LineStart); + FileLineRanges[R.FileID].second = + std::max(FileLineRanges[R.FileID].second, R.LineEnd); + } + + auto SkippedRanges = CVM.getSourceInfo().getSkippedRanges(); + for (const auto &I : SkippedRanges) { + auto LocStart = I.getBegin(); + auto LocEnd = I.getEnd(); + auto FileStart = SM.getFileID(LocStart); + if (!hasExistingCoverageFileID(FileStart)) + continue; + auto ActualFileStart = SM.getDecomposedSpellingLoc(LocStart).first; + if (ActualFileStart != SM.getDecomposedSpellingLoc(LocEnd).first) + // Ignore regions that span across multiple files. + continue; + + unsigned CovFileID; + if (getCoverageFileID(LocStart, FileStart, ActualFileStart, CovFileID)) + continue; + unsigned LineStart = SM.getSpellingLineNumber(LocStart); + unsigned ColumnStart = SM.getSpellingColumnNumber(LocStart); + unsigned LineEnd = SM.getSpellingLineNumber(LocEnd); + unsigned ColumnEnd = SM.getSpellingColumnNumber(LocEnd); + CounterMappingRegion Region(Counter(), CovFileID, LineStart, ColumnStart, + LineEnd, ColumnEnd, false, + CounterMappingRegion::SkippedRegion); + // Make sure that we only collect the regions that are inside + // the souce code of this function. + if (Region.LineStart >= FileLineRanges[CovFileID].first && + Region.LineEnd <= FileLineRanges[CovFileID].second) + MappingRegions.push_back(Region); + } + } + + /// \brief Create a mapping region that correponds to an expansion of + /// a macro or an embedded include. + void createFileExpansionRegion(SourceLocation Loc, FileID ExpandedFile) { + SourceLocation LocStart; + if (Loc.isMacroID()) + LocStart = SM.getImmediateExpansionRange(Loc).first; + else { + LocStart = SM.getIncludeLoc(ExpandedFile); + if (LocStart.isInvalid()) + return; // This file has no expansion region. + } + + auto File = SM.getFileID(LocStart); + auto SpellingFile = SM.getDecomposedSpellingLoc(LocStart).first; + unsigned CovFileID, ExpandedFileID; + if (getExistingCoverageFileID(ExpandedFile, ExpandedFileID)) + return; + if (getCoverageFileID(LocStart, File, SpellingFile, CovFileID)) + return; + unsigned LineStart = SM.getSpellingLineNumber(LocStart); + unsigned ColumnStart = SM.getSpellingColumnNumber(LocStart); + unsigned LineEnd = LineStart; + // Compute the end column manually as Lexer::getLocForEndOfToken doesn't + // give the correct result in all cases. + unsigned ColumnEnd = + ColumnStart + + Lexer::MeasureTokenLength(SM.getSpellingLoc(LocStart), SM, LangOpts); + + MappingRegions.push_back(CounterMappingRegion( + Counter(), CovFileID, LineStart, ColumnStart, LineEnd, ColumnEnd, + false, CounterMappingRegion::ExpansionRegion)); + MappingRegions.back().ExpandedFileID = ExpandedFileID; + } + + /// \brief Enter a source region group that is identified by the given + /// statement. + /// It's not possible to enter a group when there is already + /// another group present. + void beginSourceRegionGroup(const Stmt *Group) { + assert(!CurrentSourceGroup); + CurrentSourceGroup = Group; + } + + /// \brief Exit the current source region group. + void endSourceRegionGroup() { CurrentSourceGroup = nullptr; } + + /// \brief Brings a region that has the same counter and file to the back + /// of the source regions array. + void bringSimilarRegionBack(Counter Count, FileID File, + FileID MacroArgumentFile, + const Stmt *UnreachableInitiator, + const Stmt *SourceGroup) { + for (size_t I = SourceRegions.size(); I != 0;) { + --I; + if (SourceRegions[I].Count == Count && SourceRegions[I].File == File && + SourceRegions[I].MacroArgumentFile == MacroArgumentFile && + SourceRegions[I].UnreachableInitiator == UnreachableInitiator && + SourceRegions[I].Group == SourceGroup) { + if (I != SourceRegions.size() - 1) + std::swap(SourceRegions[I], SourceRegions.back()); + return; + } + } + } + + /// \brief Associate a counter with a given source code range. + void mapSourceCodeRange(SourceLocation LocStart, SourceLocation LocEnd, + Counter Count, const Stmt *UnreachableInitiator, + const Stmt *SourceGroup, unsigned Flags = 0, + FileID MacroArgumentFile = FileID()) { + if (SM.isMacroArgExpansion(LocStart)) { + // Map the code range with the macro argument's value. + mapSourceCodeRange(SM.getImmediateSpellingLoc(LocStart), + SM.getImmediateSpellingLoc(LocEnd), Count, + UnreachableInitiator, SourceGroup, Flags, + SM.getFileID(LocStart)); + // Map the code range where the macro argument is referenced. + SourceLocation RefLocStart(SM.getImmediateExpansionRange(LocStart).first); + SourceLocation RefLocEnd(RefLocStart); + if (SM.isMacroArgExpansion(RefLocStart)) + mapSourceCodeRange(RefLocStart, RefLocEnd, Count, UnreachableInitiator, + SourceGroup, 0, SM.getFileID(RefLocStart)); + else + mapSourceCodeRange(RefLocStart, RefLocEnd, Count, UnreachableInitiator, + SourceGroup); + return; + } + auto File = SM.getFileID(LocStart); + // Make sure that the file id is valid. + if (File.isInvalid()) + return; + bringSimilarRegionBack(Count, File, MacroArgumentFile, UnreachableInitiator, + SourceGroup); + SourceMappingRegion R(File, MacroArgumentFile, Count, UnreachableInitiator, + SourceGroup, LocStart, LocEnd, Flags); + if (SourceRegions.empty() || !SourceRegions.back().isMergeable(R)) { + SourceRegions.push_back(R); + return; + } + SourceRegions.back().mergeByExtendingTo(R); + } + + void mapSourceCodeRange(SourceLocation LocStart, SourceLocation LocEnd, + Counter Count, unsigned Flags = 0) { + mapSourceCodeRange(LocStart, LocEnd, Count, + CurrentUnreachableRegionInitiator, CurrentSourceGroup, + Flags); + } + + void mapSourceCodeRange(const SourceMappingState &State, + SourceLocation LocStart, SourceLocation LocEnd, + unsigned Flags = 0) { + mapSourceCodeRange(LocStart, LocEnd, State.CurrentRegionCount, + State.CurrentUnreachableRegionInitiator, + State.CurrentSourceGroup, Flags); + } + + /// \brief Generate the coverage counter mapping regions from collected + /// source regions. + void emitSourceRegions() { + for (const auto &R : SourceRegions) { + SourceLocation LocStart = R.LocStart; + SourceLocation LocEnd = R.LocEnd; + if (SM.getFileID(LocEnd) != R.File) + LocEnd = R.AlternativeLocEnd; + + if (R.hasFlag(SourceMappingRegion::IgnoreIfNotExtended) && + LocStart == LocEnd) + continue; + + LocEnd = getPreciseTokenLocEnd(LocEnd); + unsigned LineStart = SM.getSpellingLineNumber(LocStart); + unsigned ColumnStart = SM.getSpellingColumnNumber(LocStart); + unsigned LineEnd = SM.getSpellingLineNumber(LocEnd); + unsigned ColumnEnd = SM.getSpellingColumnNumber(LocEnd); + + auto SpellingFile = SM.getDecomposedSpellingLoc(R.LocStart).first; + unsigned CovFileID; + if (getCoverageFileID(R.LocStart, R.File, SpellingFile, CovFileID)) + continue; + + assert(LineStart <= LineEnd); + MappingRegions.push_back(CounterMappingRegion( + R.Count, CovFileID, LineStart, ColumnStart, LineEnd, ColumnEnd, + false, CounterMappingRegion::CodeRegion)); + } + } +}; + +/// \brief Creates unreachable coverage regions for the functions that +/// are not emitted. +struct EmptyCoverageMappingBuilder : public CoverageMappingBuilder { + EmptyCoverageMappingBuilder(CoverageMappingModuleGen &CVM, SourceManager &SM, + const LangOptions &LangOpts) + : CoverageMappingBuilder(CVM, SM, LangOpts) {} + + void VisitDecl(const Decl *D) { + if (!D->hasBody()) + return; + auto Body = D->getBody(); + mapSourceCodeRange(Body->getLocStart(), Body->getLocEnd(), Counter()); + } + + /// \brief Write the mapping data to the output stream + void write(llvm::raw_ostream &OS) { + emitSourceRegions(); + SmallVector FileIDMapping; + createFileIDMapping(FileIDMapping); + + CoverageMappingWriter Writer( + FileIDMapping, ArrayRef(), MappingRegions); + Writer.write(OS); + } +}; + +/// \brief A StmtVisitor that creates coverage mapping regions which map +/// from the source code locations to the PGO counters. +struct CounterCoverageMappingBuilder + : public CoverageMappingBuilder, + public ConstStmtVisitor { + /// \brief The map of statements to count values. + llvm::DenseMap &CounterMap; + + Counter CurrentRegionCount; + + CounterExpressionBuilder Builder; + + /// \brief Return a counter that represents the + /// expression that subracts rhs from lhs. + Counter subtractCounters(Counter LHS, Counter RHS) { + return Builder.subtract(LHS, RHS); + } + + /// \brief Return a counter that represents the + /// the exression that adds lhs and rhs. + Counter addCounters(Counter LHS, Counter RHS) { + return Builder.add(LHS, RHS); + } + + /// \brief Return the region counter for the given statement. + /// This should only be called on statements that have a dedicated counter. + unsigned getRegionCounter(const Stmt *S) { return CounterMap[S]; } + + /// \brief Return the region count for the counter at the given index. + Counter getRegionCount(unsigned CounterId) { + return Counter::getCounter(CounterId); + } + + /// \brief Return the counter value of the current region. + Counter getCurrentRegionCount() { return CurrentRegionCount; } + + /// \brief Set the counter value for the current region. + /// This is used to keep track of changes to the most recent counter + /// from control flow and non-local exits. + void setCurrentRegionCount(Counter Count) { + CurrentRegionCount = Count; + CurrentUnreachableRegionInitiator = nullptr; + } + + /// \brief Indicate that the current region is never reached, + /// and thus should have a counter value of zero. + /// This is important so that subsequent regions can correctly track + /// their parent counts. + void setCurrentRegionUnreachable(const Stmt *Initiator) { + CurrentRegionCount = Counter::getZero(); + CurrentUnreachableRegionInitiator = Initiator; + } + + /// \brief A counter for a particular region. + /// This is the primary interface through + /// which the coverage mapping builder manages counters and their values. + class RegionMapper { + CounterCoverageMappingBuilder &Mapping; + Counter Count; + Counter ParentCount; + Counter RegionCount; + Counter Adjust; + + public: + RegionMapper(CounterCoverageMappingBuilder *Mapper, const Stmt *S) + : Mapping(*Mapper), + Count(Mapper->getRegionCount(Mapper->getRegionCounter(S))), + ParentCount(Mapper->getCurrentRegionCount()) {} + + /// Get the value of the counter. In most cases this is the number of times + /// the region of the counter was entered, but for switch labels it's the + /// number of direct jumps to that label. + Counter getCount() const { return Count; } + + /// Get the value of the counter with adjustments applied. Adjustments occur + /// when control enters or leaves the region abnormally; i.e., if there is a + /// jump to a label within the region, or if the function can return from + /// within the region. The adjusted count, then, is the value of the counter + /// at the end of the region. + Counter getAdjustedCount() const { + return Mapping.addCounters(Count, Adjust); + } + + /// Get the value of the counter in this region's parent, i.e., the region + /// that was active when this region began. This is useful for deriving + /// counts in implicitly counted regions, like the false case of a condition + /// or the normal exits of a loop. + Counter getParentCount() const { return ParentCount; } + + /// Activate the counter by emitting an increment and starting to track + /// adjustments. If AddIncomingFallThrough is true, the current region count + /// will be added to the counter for the purposes of tracking the region. + void beginRegion(bool AddIncomingFallThrough = false) { + RegionCount = Count; + if (AddIncomingFallThrough) + RegionCount = + Mapping.addCounters(RegionCount, Mapping.getCurrentRegionCount()); + Mapping.setCurrentRegionCount(RegionCount); + } + + /// For counters on boolean branches, begins tracking adjustments for the + /// uncounted path. + void beginElseRegion() { + RegionCount = Mapping.subtractCounters(ParentCount, Count); + Mapping.setCurrentRegionCount(RegionCount); + } + + /// Reset the current region count. + void setCurrentRegionCount(Counter CurrentCount) { + RegionCount = CurrentCount; + Mapping.setCurrentRegionCount(RegionCount); + } + + /// Adjust for non-local control flow after emitting a subexpression or + /// substatement. This must be called to account for constructs such as + /// gotos, + /// labels, and returns, so that we can ensure that our region's count is + /// correct in the code that follows. + void adjustForControlFlow() { + Adjust = Mapping.addCounters( + Adjust, Mapping.subtractCounters(Mapping.getCurrentRegionCount(), + RegionCount)); + // Reset the region count in case this is called again later. + RegionCount = Mapping.getCurrentRegionCount(); + } + + /// Commit all adjustments to the current region. If the region is a loop, + /// the LoopAdjust value should be the count of all the breaks and continues + /// from the loop, to compensate for those counts being deducted from the + /// adjustments for the body of the loop. + void applyAdjustmentsToRegion() { + Mapping.setCurrentRegionCount(Mapping.addCounters(ParentCount, Adjust)); + } + void applyAdjustmentsToRegion(Counter LoopAdjust) { + Mapping.setCurrentRegionCount(Mapping.addCounters( + Mapping.addCounters(ParentCount, Adjust), LoopAdjust)); + } + }; + + /// \brief Keep counts of breaks and continues inside loops. + struct BreakContinue { + Counter BreakCount; + Counter ContinueCount; + }; + SmallVector BreakContinueStack; + + CounterCoverageMappingBuilder( + CoverageMappingModuleGen &CVM, + llvm::DenseMap &CounterMap, + unsigned NumRegionCounters, SourceManager &SM, + const LangOptions &LangOpts) + : CoverageMappingBuilder(CVM, SM, LangOpts), CounterMap(CounterMap), + Builder(NumRegionCounters) {} + + /// \brief Write the mapping data to the output stream + void write(llvm::raw_ostream &OS) { + emitSourceRegions(); + llvm::SmallVector VirtualFileMapping; + createFileIDMapping(VirtualFileMapping); + gatherSkippedRegions(); + + CoverageMappingWriter Writer( + VirtualFileMapping, Builder.getExpressions(), MappingRegions); + Writer.write(OS); + } + + /// \brief Return the current source mapping state. + SourceMappingState getCurrentState() const { + return SourceMappingState(CurrentRegionCount, CurrentSourceGroup, + CurrentUnreachableRegionInitiator); + } + + /// \brief Associate the source code range with the current region count. + void mapSourceCodeRange(SourceLocation LocStart, SourceLocation LocEnd, + unsigned Flags = 0) { + CoverageMappingBuilder::mapSourceCodeRange(LocStart, LocEnd, + CurrentRegionCount, Flags); + } + + void mapSourceCodeRange(SourceLocation LocStart) { + CoverageMappingBuilder::mapSourceCodeRange(LocStart, LocStart, + CurrentRegionCount); + } + + /// \brief Associate the source range of a token with the current region + /// count. + /// Ignore the source range for this token if it produces a distinct + /// mapping region with no other source ranges. + void mapToken(SourceLocation LocStart) { + CoverageMappingBuilder::mapSourceCodeRange( + LocStart, LocStart, CurrentRegionCount, + SourceMappingRegion::IgnoreIfNotExtended); + } + + void mapToken(const SourceMappingState &State, SourceLocation LocStart) { + CoverageMappingBuilder::mapSourceCodeRange( + State, LocStart, LocStart, SourceMappingRegion::IgnoreIfNotExtended); + } + + void VisitStmt(const Stmt *S) { + mapSourceCodeRange(S->getLocStart()); + for (Stmt::const_child_range I = S->children(); I; ++I) { + if (*I) + this->Visit(*I); + } + } + + /// \brief If the given statement is a compound statement, + /// map '}' with the same count as '{'. + void VisitSubStmtRBraceState(const Stmt *S) { + if (!isa(S)) + return Visit(S); + const auto *CS = cast(S); + auto State = getCurrentState(); + mapSourceCodeRange(CS->getLBracLoc()); + for (Stmt::const_child_range I = S->children(); I; ++I) { + if (*I) + this->Visit(*I); + } + CoverageMappingBuilder::mapSourceCodeRange(State, CS->getRBracLoc(), + CS->getRBracLoc()); + } + + void VisitDecl(const Decl *D) { + if (!D->hasBody()) + return; + // Counter tracks entry to the function body. + auto Body = D->getBody(); + RegionMapper Cnt(this, Body); + Cnt.beginRegion(); + VisitSubStmtRBraceState(Body); + } + + void VisitDeclStmt(const DeclStmt *S) { + mapSourceCodeRange(S->getLocStart()); + for (Stmt::const_child_range I = static_cast(S)->children(); + I; ++I) { + if (*I) + this->Visit(*I); + } + } + + void VisitCompoundStmt(const CompoundStmt *S) { + mapSourceCodeRange(S->getLBracLoc()); + for (Stmt::const_child_range I = S->children(); I; ++I) { + if (*I) + this->Visit(*I); + } + mapSourceCodeRange(S->getRBracLoc(), S->getRBracLoc()); + } + + void VisitReturnStmt(const ReturnStmt *S) { + mapSourceCodeRange(S->getLocStart()); + if (S->getRetValue()) + Visit(S->getRetValue()); + setCurrentRegionUnreachable(S); + } + + void VisitGotoStmt(const GotoStmt *S) { + mapSourceCodeRange(S->getLocStart()); + mapToken(S->getLabelLoc()); + setCurrentRegionUnreachable(S); + } + + void VisitLabelStmt(const LabelStmt *S) { + // Counter tracks the block following the label. + RegionMapper Cnt(this, S); + Cnt.beginRegion(); + mapSourceCodeRange(S->getLocStart()); + // Can't map the ':' token as its location isn't known. + Visit(S->getSubStmt()); + } + + void VisitBreakStmt(const BreakStmt *S) { + mapSourceCodeRange(S->getLocStart()); + assert(!BreakContinueStack.empty() && "break not in a loop or switch!"); + BreakContinueStack.back().BreakCount = addCounters( + BreakContinueStack.back().BreakCount, getCurrentRegionCount()); + setCurrentRegionUnreachable(S); + } + + void VisitContinueStmt(const ContinueStmt *S) { + mapSourceCodeRange(S->getLocStart()); + assert(!BreakContinueStack.empty() && "continue stmt not in a loop!"); + BreakContinueStack.back().ContinueCount = addCounters( + BreakContinueStack.back().ContinueCount, getCurrentRegionCount()); + setCurrentRegionUnreachable(S); + } + + void VisitWhileStmt(const WhileStmt *S) { + mapSourceCodeRange(S->getLocStart()); + // Counter tracks the body of the loop. + RegionMapper Cnt(this, S); + BreakContinueStack.push_back(BreakContinue()); + // Visit the body region first so the break/continue adjustments can be + // included when visiting the condition. + Cnt.beginRegion(); + VisitSubStmtRBraceState(S->getBody()); + Cnt.adjustForControlFlow(); + + // ...then go back and propagate counts through the condition. The count + // at the start of the condition is the sum of the incoming edges, + // the backedge from the end of the loop body, and the edges from + // continue statements. + BreakContinue BC = BreakContinueStack.pop_back_val(); + Cnt.setCurrentRegionCount( + addCounters(Cnt.getParentCount(), + addCounters(Cnt.getAdjustedCount(), BC.ContinueCount))); + beginSourceRegionGroup(S->getCond()); + Visit(S->getCond()); + endSourceRegionGroup(); + Cnt.adjustForControlFlow(); + Cnt.applyAdjustmentsToRegion(addCounters(BC.BreakCount, BC.ContinueCount)); + } + + void VisitDoStmt(const DoStmt *S) { + mapSourceCodeRange(S->getLocStart()); + // Counter tracks the body of the loop. + RegionMapper Cnt(this, S); + BreakContinueStack.push_back(BreakContinue()); + Cnt.beginRegion(/*AddIncomingFallThrough=*/true); + VisitSubStmtRBraceState(S->getBody()); + Cnt.adjustForControlFlow(); + + BreakContinue BC = BreakContinueStack.pop_back_val(); + // The count at the start of the condition is equal to the count at the + // end of the body. The adjusted count does not include either the + // fall-through count coming into the loop or the continue count, so add + // both of those separately. This is coincidentally the same equation as + // with while loops but for different reasons. + Cnt.setCurrentRegionCount( + addCounters(Cnt.getParentCount(), + addCounters(Cnt.getAdjustedCount(), BC.ContinueCount))); + Visit(S->getCond()); + Cnt.adjustForControlFlow(); + Cnt.applyAdjustmentsToRegion(addCounters(BC.BreakCount, BC.ContinueCount)); + } + + void VisitForStmt(const ForStmt *S) { + mapSourceCodeRange(S->getLocStart()); + if (S->getInit()) + Visit(S->getInit()); + + // Counter tracks the body of the loop. + RegionMapper Cnt(this, S); + BreakContinueStack.push_back(BreakContinue()); + // Visit the body region first. (This is basically the same as a while + // loop; see further comments in VisitWhileStmt.) + Cnt.beginRegion(); + VisitSubStmtRBraceState(S->getBody()); + Cnt.adjustForControlFlow(); + + // The increment is essentially part of the body but it needs to include + // the count for all the continue statements. + if (S->getInc()) { + Cnt.setCurrentRegionCount(addCounters( + getCurrentRegionCount(), BreakContinueStack.back().ContinueCount)); + beginSourceRegionGroup(S->getInc()); + Visit(S->getInc()); + endSourceRegionGroup(); + Cnt.adjustForControlFlow(); + } + + BreakContinue BC = BreakContinueStack.pop_back_val(); + + // ...then go back and propagate counts through the condition. + if (S->getCond()) { + Cnt.setCurrentRegionCount( + addCounters(addCounters(Cnt.getParentCount(), Cnt.getAdjustedCount()), + BC.ContinueCount)); + beginSourceRegionGroup(S->getCond()); + Visit(S->getCond()); + endSourceRegionGroup(); + Cnt.adjustForControlFlow(); + } + Cnt.applyAdjustmentsToRegion(addCounters(BC.BreakCount, BC.ContinueCount)); + } + + void VisitCXXForRangeStmt(const CXXForRangeStmt *S) { + mapSourceCodeRange(S->getLocStart()); + Visit(S->getRangeStmt()); + Visit(S->getBeginEndStmt()); + // Counter tracks the body of the loop. + RegionMapper Cnt(this, S); + BreakContinueStack.push_back(BreakContinue()); + // Visit the body region first. (This is basically the same as a while + // loop; see further comments in VisitWhileStmt.) + Cnt.beginRegion(); + VisitSubStmtRBraceState(S->getBody()); + Cnt.adjustForControlFlow(); + BreakContinue BC = BreakContinueStack.pop_back_val(); + Cnt.applyAdjustmentsToRegion(addCounters(BC.BreakCount, BC.ContinueCount)); + } + + void VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S) { + mapSourceCodeRange(S->getLocStart()); + Visit(S->getElement()); + // Counter tracks the body of the loop. + RegionMapper Cnt(this, S); + BreakContinueStack.push_back(BreakContinue()); + VisitSubStmtRBraceState(S->getBody()); + BreakContinue BC = BreakContinueStack.pop_back_val(); + Cnt.adjustForControlFlow(); + Cnt.applyAdjustmentsToRegion(addCounters(BC.BreakCount, BC.ContinueCount)); + } + + void VisitSwitchStmt(const SwitchStmt *S) { + mapSourceCodeRange(S->getLocStart()); + Visit(S->getCond()); + BreakContinueStack.push_back(BreakContinue()); + // Map the '}' for the body to have the same count as the regions after + // the switch. + SourceLocation RBracLoc; + if (const auto *CS = dyn_cast(S->getBody())) { + mapSourceCodeRange(CS->getLBracLoc()); + setCurrentRegionUnreachable(S); + for (Stmt::const_child_range I = CS->children(); I; ++I) { + if (*I) + this->Visit(*I); + } + RBracLoc = CS->getRBracLoc(); + } else { + setCurrentRegionUnreachable(S); + Visit(S->getBody()); + } + // If the switch is inside a loop, add the continue counts. + BreakContinue BC = BreakContinueStack.pop_back_val(); + if (!BreakContinueStack.empty()) + BreakContinueStack.back().ContinueCount = addCounters( + BreakContinueStack.back().ContinueCount, BC.ContinueCount); + // Counter tracks the exit block of the switch. + RegionMapper ExitCnt(this, S); + ExitCnt.beginRegion(); + if (RBracLoc.isValid()) + mapSourceCodeRange(RBracLoc); + } + + void VisitCaseStmt(const CaseStmt *S) { + // Counter for this particular case. This counts only jumps from the + // switch header and does not include fallthrough from the case before + // this one. + RegionMapper Cnt(this, S); + Cnt.beginRegion(/*AddIncomingFallThrough=*/true); + mapSourceCodeRange(S->getLocStart()); + mapToken(S->getColonLoc()); + Visit(S->getSubStmt()); + } + + void VisitDefaultStmt(const DefaultStmt *S) { + // Counter for this default case. This does not include fallthrough from + // the previous case. + RegionMapper Cnt(this, S); + Cnt.beginRegion(/*AddIncomingFallThrough=*/true); + mapSourceCodeRange(S->getLocStart()); + mapToken(S->getColonLoc()); + Visit(S->getSubStmt()); + } + + void VisitIfStmt(const IfStmt *S) { + mapSourceCodeRange(S->getLocStart()); + Visit(S->getCond()); + mapToken(S->getElseLoc()); + + // Counter tracks the "then" part of an if statement. The count for + // the "else" part, if it exists, will be calculated from this counter. + RegionMapper Cnt(this, S); + Cnt.beginRegion(); + VisitSubStmtRBraceState(S->getThen()); + Cnt.adjustForControlFlow(); + + if (S->getElse()) { + Cnt.beginElseRegion(); + VisitSubStmtRBraceState(S->getElse()); + Cnt.adjustForControlFlow(); + } + Cnt.applyAdjustmentsToRegion(); + } + + void VisitCXXTryStmt(const CXXTryStmt *S) { + mapSourceCodeRange(S->getLocStart()); + Visit(S->getTryBlock()); + for (unsigned I = 0, E = S->getNumHandlers(); I < E; ++I) + Visit(S->getHandler(I)); + // Counter tracks the continuation block of the try statement. + RegionMapper Cnt(this, S); + Cnt.beginRegion(); + } + + void VisitCXXCatchStmt(const CXXCatchStmt *S) { + mapSourceCodeRange(S->getLocStart()); + // Counter tracks the catch statement's handler block. + RegionMapper Cnt(this, S); + Cnt.beginRegion(); + VisitSubStmtRBraceState(S->getHandlerBlock()); + } + + void VisitAbstractConditionalOperator(const AbstractConditionalOperator *E) { + Visit(E->getCond()); + mapToken(E->getQuestionLoc()); + auto State = getCurrentState(); + + // Counter tracks the "true" part of a conditional operator. The + // count in the "false" part will be calculated from this counter. + RegionMapper Cnt(this, E); + Cnt.beginRegion(); + Visit(E->getTrueExpr()); + Cnt.adjustForControlFlow(); + + mapToken(State, E->getColonLoc()); + + Cnt.beginElseRegion(); + Visit(E->getFalseExpr()); + Cnt.adjustForControlFlow(); + + Cnt.applyAdjustmentsToRegion(); + } + + void VisitBinLAnd(const BinaryOperator *E) { + Visit(E->getLHS()); + mapToken(E->getOperatorLoc()); + // Counter tracks the right hand side of a logical and operator. + RegionMapper Cnt(this, E); + Cnt.beginRegion(); + Visit(E->getRHS()); + Cnt.adjustForControlFlow(); + Cnt.applyAdjustmentsToRegion(); + } + + void VisitBinLOr(const BinaryOperator *E) { + Visit(E->getLHS()); + mapToken(E->getOperatorLoc()); + // Counter tracks the right hand side of a logical or operator. + RegionMapper Cnt(this, E); + Cnt.beginRegion(); + Visit(E->getRHS()); + Cnt.adjustForControlFlow(); + Cnt.applyAdjustmentsToRegion(); + } + + void VisitParenExpr(const ParenExpr *E) { + mapToken(E->getLParen()); + Visit(E->getSubExpr()); + mapToken(E->getRParen()); + } + + void VisitBinaryOperator(const BinaryOperator *E) { + Visit(E->getLHS()); + mapToken(E->getOperatorLoc()); + Visit(E->getRHS()); + } + + void VisitUnaryOperator(const UnaryOperator *E) { + bool Postfix = E->isPostfix(); + if (!Postfix) + mapToken(E->getOperatorLoc()); + Visit(E->getSubExpr()); + if (Postfix) + mapToken(E->getOperatorLoc()); + } + + void VisitMemberExpr(const MemberExpr *E) { + Visit(E->getBase()); + mapToken(E->getMemberLoc()); + } + + void VisitCallExpr(const CallExpr *E) { + Visit(E->getCallee()); + for (const auto &Arg : E->arguments()) + Visit(Arg); + mapToken(E->getRParenLoc()); + } + + void VisitArraySubscriptExpr(const ArraySubscriptExpr *E) { + Visit(E->getLHS()); + Visit(E->getRHS()); + mapToken(E->getRBracketLoc()); + } + + void VisitCStyleCastExpr(const CStyleCastExpr *E) { + mapToken(E->getLParenLoc()); + mapToken(E->getRParenLoc()); + Visit(E->getSubExpr()); + } + + // Map literals as tokens so that the macros like #define PI 3.14 + // won't generate coverage mapping regions. + + void VisitIntegerLiteral(const IntegerLiteral *E) { + mapToken(E->getLocStart()); + } + + void VisitFloatingLiteral(const FloatingLiteral *E) { + mapToken(E->getLocStart()); + } + + void VisitCharacterLiteral(const CharacterLiteral *E) { + mapToken(E->getLocStart()); + } + + void VisitStringLiteral(const StringLiteral *E) { + mapToken(E->getLocStart()); + } + + void VisitImaginaryLiteral(const ImaginaryLiteral *E) { + mapToken(E->getLocStart()); + } +}; +} + +static bool isMachO(const CodeGenModule &CGM) { + return CGM.getTarget().getTriple().isOSBinFormatMachO(); +} + +static StringRef getCoverageSection(const CodeGenModule &CGM) { + return isMachO(CGM) ? "__DATA,__llvm_covmap" : "__llvm_covmap"; +} + +void CoverageMappingModuleGen::addFunctionMappingRecord( + llvm::GlobalVariable *FunctionName, unsigned FunctionNameSize, + const std::string &CoverageMapping) { + llvm::LLVMContext &Ctx = CGM.getLLVMContext(); + auto *Int32Ty = llvm::Type::getInt32Ty(Ctx); + auto *Int8PtrTy = llvm::Type::getInt8PtrTy(Ctx); + if (!FunctionRecordTy) { + llvm::Type *FunctionRecordTypes[] = {Int8PtrTy, Int32Ty, Int32Ty}; + FunctionRecordTy = + llvm::StructType::get(Ctx, makeArrayRef(FunctionRecordTypes)); + } + + llvm::Constant *FunctionRecordVals[] = { + llvm::ConstantExpr::getBitCast(FunctionName, Int8PtrTy), + llvm::ConstantInt::get(Int32Ty, FunctionNameSize), + llvm::ConstantInt::get(Int32Ty, CoverageMapping.size())}; + FunctionRecords.push_back(llvm::ConstantStruct::get( + FunctionRecordTy, makeArrayRef(FunctionRecordVals))); + CoverageMappings += CoverageMapping; +} + +void CoverageMappingModuleGen::emit() { + if (FunctionRecords.empty()) + return; + llvm::LLVMContext &Ctx = CGM.getLLVMContext(); + auto *Int32Ty = llvm::Type::getInt32Ty(Ctx); + + // Create the filenames and merge them with coverage mappings + llvm::SmallVector FilenameStrs; + llvm::SmallVector FilenameRefs; + FilenameStrs.resize(FileEntries.size()); + FilenameRefs.resize(FileEntries.size()); + for (const auto &Entry : FileEntries) { + llvm::SmallString<256> Path(Entry.first->getName()); + llvm::sys::fs::make_absolute(Path); + + auto I = Entry.second; + FilenameStrs[I] = std::move(std::string(Path.begin(), Path.end())); + FilenameRefs[I] = FilenameStrs[I]; + } + + std::string FilenamesAndCoverageMappings; + llvm::raw_string_ostream OS(FilenamesAndCoverageMappings); + CoverageFilenamesSectionWriter(FilenameRefs).write(OS); + OS << CoverageMappings; + size_t CoverageMappingSize = CoverageMappings.size(); + size_t FilenamesSize = OS.str().size() - CoverageMappingSize; + // Append extra zeroes if necessary to ensure that the size of the filenames + // and coverage mappings is a multiple of 8. + if (size_t Rem = OS.str().size() % 8) { + CoverageMappingSize += 8 - Rem; + for (size_t I = 0, S = 8 - Rem; I < S; ++I) + OS << '\0'; + } + auto *FilenamesAndMappingsVal = + llvm::ConstantDataArray::getString(Ctx, OS.str(), false); + + // Create the deferred function records array + auto RecordsTy = + llvm::ArrayType::get(FunctionRecordTy, FunctionRecords.size()); + auto RecordsVal = llvm::ConstantArray::get(RecordsTy, FunctionRecords); + + // Create the coverage data record + llvm::Type *CovDataTypes[] = {Int32Ty, Int32Ty, + Int32Ty, Int32Ty, + RecordsTy, FilenamesAndMappingsVal->getType()}; + auto CovDataTy = llvm::StructType::get(Ctx, makeArrayRef(CovDataTypes)); + llvm::Constant *TUDataVals[] = { + llvm::ConstantInt::get(Int32Ty, FunctionRecords.size()), + llvm::ConstantInt::get(Int32Ty, FilenamesSize), + llvm::ConstantInt::get(Int32Ty, CoverageMappingSize), + llvm::ConstantInt::get(Int32Ty, + /*Version=*/CoverageMappingVersion1), + RecordsVal, FilenamesAndMappingsVal}; + auto CovDataVal = + llvm::ConstantStruct::get(CovDataTy, makeArrayRef(TUDataVals)); + auto CovData = new llvm::GlobalVariable(CGM.getModule(), CovDataTy, true, + llvm::GlobalValue::InternalLinkage, + CovDataVal, + "__llvm_coverage_mapping"); + + CovData->setSection(getCoverageSection(CGM)); + CovData->setAlignment(8); + + // Make sure the data doesn't get deleted. + CGM.addUsedGlobal(CovData); +} + +unsigned CoverageMappingModuleGen::getFileID(const FileEntry *File) { + auto It = FileEntries.find(File); + if (It != FileEntries.end()) + return It->second; + unsigned FileID = FileEntries.size(); + FileEntries.insert(std::make_pair(File, FileID)); + return FileID; +} + +void CoverageMappingGen::emitCounterMapping(const Decl *D, + llvm::raw_ostream &OS) { + assert(CounterMap); + CounterCoverageMappingBuilder Walker(CVM, *CounterMap, NumRegionCounters, SM, + LangOpts); + Walker.VisitDecl(D); + Walker.write(OS); +} + +void CoverageMappingGen::emitEmptyMapping(const Decl *D, + llvm::raw_ostream &OS) { + EmptyCoverageMappingBuilder Walker(CVM, SM, LangOpts); + Walker.VisitDecl(D); + Walker.write(OS); +} diff --git a/clang/lib/CodeGen/CoverageMappingGen.h b/clang/lib/CodeGen/CoverageMappingGen.h new file mode 100644 index 000000000000..e77dedb925d5 --- /dev/null +++ b/clang/lib/CodeGen/CoverageMappingGen.h @@ -0,0 +1,117 @@ +//===---- CoverageMappingGen.h - Coverage mapping generation ----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Instrumentation-based code coverage mapping generator +// +//===----------------------------------------------------------------------===// + +#ifndef CLANG_CODEGEN_COVERAGEMAPPINGGEN_H +#define CLANG_CODEGEN_COVERAGEMAPPINGGEN_H + +#include "clang/Basic/LLVM.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Lex/PPCallbacks.h" +#include "clang/Frontend/CodeGenOptions.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/IR/GlobalValue.h" +#include "llvm/Support/raw_ostream.h" + +namespace clang { + +class LangOptions; +class SourceManager; +class FileEntry; +class Preprocessor; +class Decl; +class Stmt; + +/// \brief Stores additional source code information like skipped ranges which +/// is required by the coverage mapping generator and is obtained from +/// the preprocessor. +class CoverageSourceInfo : public PPCallbacks { + std::vector SkippedRanges; +public: + ArrayRef getSkippedRanges() const { return SkippedRanges; } + + void SourceRangeSkipped(SourceRange Range) override; +}; + +namespace CodeGen { + +class CodeGenModule; + +/// \brief Organizes the cross-function state that is used while generating +/// code coverage mapping data. +class CoverageMappingModuleGen { + CodeGenModule &CGM; + CoverageSourceInfo &SourceInfo; + llvm::SmallDenseMap FileEntries; + std::vector FunctionRecords; + llvm::StructType *FunctionRecordTy; + std::string CoverageMappings; + +public: + CoverageMappingModuleGen(CodeGenModule &CGM, CoverageSourceInfo &SourceInfo) + : CGM(CGM), SourceInfo(SourceInfo), FunctionRecordTy(nullptr) {} + + CoverageSourceInfo &getSourceInfo() const { + return SourceInfo; + } + + /// \brief Add a function's coverage mapping record to the collection of the + /// function mapping records. + void addFunctionMappingRecord(llvm::GlobalVariable *FunctionName, + unsigned FunctionNameSize, + const std::string &CoverageMapping); + + /// \brief Emit the coverage mapping data for a translation unit. + void emit(); + + /// \brief Return the coverage mapping translation unit file id + /// for the given file. + unsigned getFileID(const FileEntry *File); +}; + +/// \brief Organizes the per-function state that is used while generating +/// code coverage mapping data. +class CoverageMappingGen { + CoverageMappingModuleGen &CVM; + SourceManager &SM; + const LangOptions &LangOpts; + llvm::DenseMap *CounterMap; + unsigned NumRegionCounters; + +public: + CoverageMappingGen(CoverageMappingModuleGen &CVM, SourceManager &SM, + const LangOptions &LangOpts) + : CVM(CVM), SM(SM), LangOpts(LangOpts), CounterMap(nullptr), + NumRegionCounters(0) {} + + CoverageMappingGen(CoverageMappingModuleGen &CVM, SourceManager &SM, + const LangOptions &LangOpts, + llvm::DenseMap *CounterMap, + unsigned NumRegionCounters) + : CVM(CVM), SM(SM), LangOpts(LangOpts), CounterMap(CounterMap), + NumRegionCounters(NumRegionCounters) {} + + /// \brief Emit the coverage mapping data which maps the regions of + /// code to counters that will be used to find the execution + /// counts for those regions. + void emitCounterMapping(const Decl *D, llvm::raw_ostream &OS); + + /// \brief Emit the coverage mapping data for an unused function. + /// It creates mapping regions with the counter of zero. + void emitEmptyMapping(const Decl *D, llvm::raw_ostream &OS); +}; + +} // end namespace CodeGen +} // end namespace clang + +#endif diff --git a/clang/lib/CodeGen/ModuleBuilder.cpp b/clang/lib/CodeGen/ModuleBuilder.cpp index 040bba14e981..7d75d8bff7b3 100644 --- a/clang/lib/CodeGen/ModuleBuilder.cpp +++ b/clang/lib/CodeGen/ModuleBuilder.cpp @@ -46,14 +46,18 @@ namespace { } }; + CoverageSourceInfo *CoverageInfo; + protected: std::unique_ptr M; std::unique_ptr Builder; public: CodeGeneratorImpl(DiagnosticsEngine &diags, const std::string& ModuleName, - const CodeGenOptions &CGO, llvm::LLVMContext& C) + const CodeGenOptions &CGO, llvm::LLVMContext& C, + CoverageSourceInfo *CoverageInfo = nullptr) : Diags(diags), CodeGenOpts(CGO), HandlingTopLevelDecls(0), + CoverageInfo(CoverageInfo), M(new llvm::Module(ModuleName, C)) {} virtual ~CodeGeneratorImpl() {} @@ -86,7 +90,7 @@ namespace { M->setDataLayout(Ctx->getTargetInfo().getTargetDescription()); TD.reset(new llvm::DataLayout(Ctx->getTargetInfo().getTargetDescription())); Builder.reset(new CodeGen::CodeGenModule(Context, CodeGenOpts, *M, *TD, - Diags)); + Diags, CoverageInfo)); for (size_t i = 0, e = CodeGenOpts.DependentLibraries.size(); i < e; ++i) HandleDependentLibrary(CodeGenOpts.DependentLibraries[i]); @@ -136,6 +140,10 @@ namespace { // void foo() { bar(); } // } A; DeferredInlineMethodDefinitions.push_back(D); + + // Always provide some coverage mapping + // even for the methods that aren't emitted. + Builder->AddDeferredUnusedCoverageMapping(D); } /// HandleTagDeclDefinition - This callback is invoked each time a TagDecl @@ -221,6 +229,7 @@ CodeGenerator *clang::CreateLLVMCodeGen(DiagnosticsEngine &Diags, const std::string& ModuleName, const CodeGenOptions &CGO, const TargetOptions &/*TO*/, - llvm::LLVMContext& C) { - return new CodeGeneratorImpl(Diags, ModuleName, CGO, C); + llvm::LLVMContext& C, + CoverageSourceInfo *CoverageInfo) { + return new CodeGeneratorImpl(Diags, ModuleName, CGO, C, CoverageInfo); } diff --git a/clang/lib/Driver/Tools.cpp b/clang/lib/Driver/Tools.cpp index fde63ca20198..9a205b3f5f4d 100644 --- a/clang/lib/Driver/Tools.cpp +++ b/clang/lib/Driver/Tools.cpp @@ -3230,6 +3230,14 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, Args.hasArg(options::OPT_coverage)) CmdArgs.push_back("-femit-coverage-data"); + if (Args.hasArg(options::OPT_fcoverage_mapping) && + !Args.hasArg(options::OPT_fprofile_instr_generate)) + D.Diag(diag::err_drv_argument_only_allowed_with) + << "-fcoverage-mapping" << "-fprofile-instr-generate"; + + if (Args.hasArg(options::OPT_fcoverage_mapping)) + CmdArgs.push_back("-fcoverage-mapping"); + if (C.getArgs().hasArg(options::OPT_c) || C.getArgs().hasArg(options::OPT_S)) { if (Output.isFilename()) { diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 0c6d77d308c2..0fbbf1aa2fa0 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -403,6 +403,7 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK, Opts.SampleProfileFile = Args.getLastArgValue(OPT_fprofile_sample_use_EQ); Opts.ProfileInstrGenerate = Args.hasArg(OPT_fprofile_instr_generate); Opts.InstrProfileInput = Args.getLastArgValue(OPT_fprofile_instr_use_EQ); + Opts.CoverageMapping = Args.hasArg(OPT_fcoverage_mapping); Opts.AsmVerbose = Args.hasArg(OPT_masm_verbose); Opts.ObjCAutoRefCountExceptions = Args.hasArg(OPT_fobjc_arc_exceptions); Opts.CUDAIsDevice = Args.hasArg(OPT_fcuda_is_device);