diff options
| author | joerg <joerg@NetBSD.org> | 2021-05-30 01:25:15 +0000 |
|---|---|---|
| committer | joerg <joerg@NetBSD.org> | 2021-05-30 01:25:15 +0000 |
| commit | 13fbcb42fff2606c3ce926a7c4bb8989aa18758c (patch) | |
| tree | dcace6c89967106796d44b1a7c5e2269a20967e2 /external/apache2/llvm/dist/clang/lib/CodeGen/CoverageMappingGen.cpp | |
| parent | 13338e33506ade63440b4ba74479551bdc21c0f6 (diff) | |
Import clang 249b40b558955afe5ac2b549edcf2d7f859c8cc9.
Diffstat (limited to 'external/apache2/llvm/dist/clang/lib/CodeGen/CoverageMappingGen.cpp')
| -rw-r--r-- | external/apache2/llvm/dist/clang/lib/CodeGen/CoverageMappingGen.cpp | 797 |
1 files changed, 542 insertions, 255 deletions
diff --git a/external/apache2/llvm/dist/clang/lib/CodeGen/CoverageMappingGen.cpp b/external/apache2/llvm/dist/clang/lib/CodeGen/CoverageMappingGen.cpp index 0a7a4fe33ac..8a11da600e4 100644 --- a/external/apache2/llvm/dist/clang/lib/CodeGen/CoverageMappingGen.cpp +++ b/external/apache2/llvm/dist/clang/lib/CodeGen/CoverageMappingGen.cpp @@ -13,10 +13,13 @@ #include "CoverageMappingGen.h" #include "CodeGenFunction.h" #include "clang/AST/StmtVisitor.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/FileManager.h" +#include "clang/Frontend/FrontendDiagnostic.h" #include "clang/Lex/Lexer.h" +#include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/StringExtras.h" -#include "llvm/ADT/Optional.h" #include "llvm/ProfileData/Coverage/CoverageMapping.h" #include "llvm/ProfileData/Coverage/CoverageMappingReader.h" #include "llvm/ProfileData/Coverage/CoverageMappingWriter.h" @@ -24,42 +27,106 @@ #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" +// This selects the coverage mapping format defined when `InstrProfData.inc` +// is textually included. +#define COVMAP_V3 + +static llvm::cl::opt<bool> EmptyLineCommentCoverage( + "emptyline-comment-coverage", + llvm::cl::desc("Emit emptylines and comment lines as skipped regions (only " + "disable it on test)"), + llvm::cl::init(true), llvm::cl::Hidden); + using namespace clang; using namespace CodeGen; using namespace llvm::coverage; +CoverageSourceInfo * +CoverageMappingModuleGen::setUpCoverageCallbacks(Preprocessor &PP) { + CoverageSourceInfo *CoverageInfo = + new CoverageSourceInfo(PP.getSourceManager()); + PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(CoverageInfo)); + if (EmptyLineCommentCoverage) { + PP.addCommentHandler(CoverageInfo); + PP.setEmptylineHandler(CoverageInfo); + PP.setPreprocessToken(true); + PP.setTokenWatcher([CoverageInfo](clang::Token Tok) { + // Update previous token location. + CoverageInfo->PrevTokLoc = Tok.getLocation(); + if (Tok.getKind() != clang::tok::eod) + CoverageInfo->updateNextTokLoc(Tok.getLocation()); + }); + } + return CoverageInfo; +} + +void CoverageSourceInfo::AddSkippedRange(SourceRange Range) { + if (EmptyLineCommentCoverage && !SkippedRanges.empty() && + PrevTokLoc == SkippedRanges.back().PrevTokLoc && + SourceMgr.isWrittenInSameFile(SkippedRanges.back().Range.getEnd(), + Range.getBegin())) + SkippedRanges.back().Range.setEnd(Range.getEnd()); + else + SkippedRanges.push_back({Range, PrevTokLoc}); +} + void CoverageSourceInfo::SourceRangeSkipped(SourceRange Range, SourceLocation) { - SkippedRanges.push_back(Range); + AddSkippedRange(Range); +} + +void CoverageSourceInfo::HandleEmptyline(SourceRange Range) { + AddSkippedRange(Range); +} + +bool CoverageSourceInfo::HandleComment(Preprocessor &PP, SourceRange Range) { + AddSkippedRange(Range); + return false; +} + +void CoverageSourceInfo::updateNextTokLoc(SourceLocation Loc) { + if (!SkippedRanges.empty() && SkippedRanges.back().NextTokLoc.isInvalid()) + SkippedRanges.back().NextTokLoc = Loc; } namespace { /// A region of source code that can be mapped to a counter. class SourceMappingRegion { + /// Primary Counter that is also used for Branch Regions for "True" branches. Counter Count; + /// Secondary Counter used for Branch Regions for "False" branches. + Optional<Counter> FalseCount; + /// The region's starting location. Optional<SourceLocation> LocStart; /// The region's ending location. Optional<SourceLocation> LocEnd; - /// Whether this region should be emitted after its parent is emitted. - bool DeferRegion; - /// Whether this region is a gap region. The count from a gap region is set /// as the line execution count if there are no other regions on the line. bool GapRegion; public: SourceMappingRegion(Counter Count, Optional<SourceLocation> LocStart, - Optional<SourceLocation> LocEnd, bool DeferRegion = false, - bool GapRegion = false) - : Count(Count), LocStart(LocStart), LocEnd(LocEnd), - DeferRegion(DeferRegion), GapRegion(GapRegion) {} + Optional<SourceLocation> LocEnd, bool GapRegion = false) + : Count(Count), LocStart(LocStart), LocEnd(LocEnd), GapRegion(GapRegion) { + } + + SourceMappingRegion(Counter Count, Optional<Counter> FalseCount, + Optional<SourceLocation> LocStart, + Optional<SourceLocation> LocEnd, bool GapRegion = false) + : Count(Count), FalseCount(FalseCount), LocStart(LocStart), + LocEnd(LocEnd), GapRegion(GapRegion) {} const Counter &getCounter() const { return Count; } + const Counter &getFalseCounter() const { + assert(FalseCount && "Region has no alternate counter"); + return *FalseCount; + } + void setCounter(Counter C) { Count = C; } bool hasStartLoc() const { return LocStart.hasValue(); } @@ -83,13 +150,11 @@ public: return *LocEnd; } - bool isDeferred() const { return DeferRegion; } - - void setDeferred(bool Deferred) { DeferRegion = Deferred; } - bool isGap() const { return GapRegion; } void setGap(bool Gap) { GapRegion = Gap; } + + bool isBranch() const { return FalseCount.hasValue(); } }; /// Spelling locations for the start and end of a source region. @@ -267,8 +332,31 @@ public: return None; } + /// This shrinks the skipped range if it spans a line that contains a + /// non-comment token. If shrinking the skipped range would make it empty, + /// this returns None. + Optional<SpellingRegion> adjustSkippedRange(SourceManager &SM, + SourceLocation LocStart, + SourceLocation LocEnd, + SourceLocation PrevTokLoc, + SourceLocation NextTokLoc) { + SpellingRegion SR{SM, LocStart, LocEnd}; + SR.ColumnStart = 1; + if (PrevTokLoc.isValid() && SM.isWrittenInSameFile(LocStart, PrevTokLoc) && + SR.LineStart == SM.getSpellingLineNumber(PrevTokLoc)) + SR.LineStart++; + if (NextTokLoc.isValid() && SM.isWrittenInSameFile(LocEnd, NextTokLoc) && + SR.LineEnd == SM.getSpellingLineNumber(NextTokLoc)) { + SR.LineEnd--; + SR.ColumnEnd++; + } + if (SR.isInSourceOrder()) + return SR; + return None; + } + /// Gather all the regions that were skipped by the preprocessor - /// using the constructs like #if. + /// using the constructs like #if or comments. void gatherSkippedRegions() { /// An array of the minimum lineStarts and the maximum lineEnds /// for mapping regions from the appropriate source files. @@ -284,18 +372,23 @@ public: } auto SkippedRanges = CVM.getSourceInfo().getSkippedRanges(); - for (const auto &I : SkippedRanges) { - auto LocStart = I.getBegin(); - auto LocEnd = I.getEnd(); + for (auto &I : SkippedRanges) { + SourceRange Range = I.Range; + auto LocStart = Range.getBegin(); + auto LocEnd = Range.getEnd(); assert(SM.isWrittenInSameFile(LocStart, LocEnd) && "region spans multiple files"); auto CovFileID = getCoverageFileID(LocStart); if (!CovFileID) continue; - SpellingRegion SR{SM, LocStart, LocEnd}; + Optional<SpellingRegion> SR = + adjustSkippedRange(SM, LocStart, LocEnd, I.PrevTokLoc, I.NextTokLoc); + if (!SR.hasValue()) + continue; auto Region = CounterMappingRegion::makeSkipped( - *CovFileID, SR.LineStart, SR.ColumnStart, SR.LineEnd, SR.ColumnEnd); + *CovFileID, SR->LineStart, SR->ColumnStart, SR->LineEnd, + SR->ColumnEnd); // Make sure that we only collect the regions that are inside // the source code of this function. if (Region.LineStart >= FileLineRanges[*CovFileID].first && @@ -341,6 +434,10 @@ public: MappingRegions.push_back(CounterMappingRegion::makeGapRegion( Region.getCounter(), *CovFileID, SR.LineStart, SR.ColumnStart, SR.LineEnd, SR.ColumnEnd)); + } else if (Region.isBranch()) { + MappingRegions.push_back(CounterMappingRegion::makeBranchRegion( + Region.getCounter(), Region.getFalseCounter(), *CovFileID, + SR.LineStart, SR.ColumnStart, SR.LineEnd, SR.ColumnEnd)); } else { MappingRegions.push_back(CounterMappingRegion::makeRegion( Region.getCounter(), *CovFileID, SR.LineStart, SR.ColumnStart, @@ -438,10 +535,6 @@ struct CounterCoverageMappingBuilder /// A stack of currently live regions. std::vector<SourceMappingRegion> RegionStack; - /// The currently deferred region: its end location and count can be set once - /// its parent has been popped from the region stack. - Optional<SourceMappingRegion> DeferredRegion; - CounterExpressionBuilder Builder; /// A location in the most recently visited file or macro. @@ -450,8 +543,11 @@ struct CounterCoverageMappingBuilder /// expressions cross file or macro boundaries. SourceLocation MostRecentLocation; - /// Location of the last terminated region. - Optional<std::pair<SourceLocation, size_t>> LastTerminatedRegion; + /// Whether the visitor at a terminate statement. + bool HasTerminateStmt = false; + + /// Gap region counter after terminate statement. + Counter GapRegionCounter; /// Return a counter for the subtraction of \c RHS from \c LHS Counter subtractCounters(Counter LHS, Counter RHS) { @@ -479,76 +575,16 @@ struct CounterCoverageMappingBuilder /// Returns the index on the stack where the region was pushed. This can be /// used with popRegions to exit a "scope", ending the region that was pushed. size_t pushRegion(Counter Count, Optional<SourceLocation> StartLoc = None, - Optional<SourceLocation> EndLoc = None) { - if (StartLoc) { - MostRecentLocation = *StartLoc; - completeDeferred(Count, MostRecentLocation); - } - RegionStack.emplace_back(Count, StartLoc, EndLoc); + Optional<SourceLocation> EndLoc = None, + Optional<Counter> FalseCount = None) { - return RegionStack.size() - 1; - } - - /// Complete any pending deferred region by setting its end location and - /// count, and then pushing it onto the region stack. - size_t completeDeferred(Counter Count, SourceLocation DeferredEndLoc) { - size_t Index = RegionStack.size(); - if (!DeferredRegion) - return Index; - - // Consume the pending region. - SourceMappingRegion DR = DeferredRegion.getValue(); - DeferredRegion = None; - - // If the region ends in an expansion, find the expansion site. - FileID StartFile = SM.getFileID(DR.getBeginLoc()); - if (SM.getFileID(DeferredEndLoc) != StartFile) { - if (isNestedIn(DeferredEndLoc, StartFile)) { - do { - DeferredEndLoc = getIncludeOrExpansionLoc(DeferredEndLoc); - } while (StartFile != SM.getFileID(DeferredEndLoc)); - } else { - return Index; - } + if (StartLoc && !FalseCount.hasValue()) { + MostRecentLocation = *StartLoc; } - // The parent of this deferred region ends where the containing decl ends, - // so the region isn't useful. - if (DR.getBeginLoc() == DeferredEndLoc) - return Index; - - // If we're visiting statements in non-source order (e.g switch cases or - // a loop condition) we can't construct a sensible deferred region. - if (!SpellingRegion(SM, DR.getBeginLoc(), DeferredEndLoc).isInSourceOrder()) - return Index; - - DR.setGap(true); - DR.setCounter(Count); - DR.setEndLoc(DeferredEndLoc); - handleFileExit(DeferredEndLoc); - RegionStack.push_back(DR); - return Index; - } - - /// Complete a deferred region created after a terminated region at the - /// top-level. - void completeTopLevelDeferredRegion(Counter Count, - SourceLocation DeferredEndLoc) { - if (DeferredRegion || !LastTerminatedRegion) - return; - - if (LastTerminatedRegion->second != RegionStack.size()) - return; + RegionStack.emplace_back(Count, FalseCount, StartLoc, EndLoc); - SourceLocation Start = LastTerminatedRegion->first; - if (SM.getFileID(Start) != SM.getMainFileID()) - return; - - SourceMappingRegion DR = RegionStack.back(); - DR.setStartLoc(Start); - DR.setDeferred(false); - DeferredRegion = DR; - completeDeferred(Count, DeferredEndLoc); + return RegionStack.size() - 1; } size_t locationDepth(SourceLocation Loc) { @@ -566,7 +602,6 @@ struct CounterCoverageMappingBuilder /// function's \c SourceRegions. void popRegions(size_t ParentIndex) { assert(RegionStack.size() >= ParentIndex && "parent not in stack"); - bool ParentOfDeferredRegion = false; while (RegionStack.size() > ParentIndex) { SourceMappingRegion &Region = RegionStack.back(); if (Region.hasStartLoc()) { @@ -574,79 +609,71 @@ struct CounterCoverageMappingBuilder SourceLocation EndLoc = Region.hasEndLoc() ? Region.getEndLoc() : RegionStack[ParentIndex].getEndLoc(); + bool isBranch = Region.isBranch(); size_t StartDepth = locationDepth(StartLoc); size_t EndDepth = locationDepth(EndLoc); while (!SM.isWrittenInSameFile(StartLoc, EndLoc)) { bool UnnestStart = StartDepth >= EndDepth; bool UnnestEnd = EndDepth >= StartDepth; if (UnnestEnd) { - // The region ends in a nested file or macro expansion. Create a - // separate region for each expansion. + // The region ends in a nested file or macro expansion. If the + // region is not a branch region, create a separate region for each + // expansion, and for all regions, update the EndLoc. Branch + // regions should not be split in order to keep a straightforward + // correspondance between the region and its associated branch + // condition, even if the condition spans multiple depths. SourceLocation NestedLoc = getStartOfFileOrMacro(EndLoc); assert(SM.isWrittenInSameFile(NestedLoc, EndLoc)); - if (!isRegionAlreadyAdded(NestedLoc, EndLoc)) - SourceRegions.emplace_back(Region.getCounter(), NestedLoc, EndLoc); + if (!isBranch && !isRegionAlreadyAdded(NestedLoc, EndLoc)) + SourceRegions.emplace_back(Region.getCounter(), NestedLoc, + EndLoc); EndLoc = getPreciseTokenLocEnd(getIncludeOrExpansionLoc(EndLoc)); if (EndLoc.isInvalid()) - llvm::report_fatal_error("File exit not handled before popRegions"); + llvm::report_fatal_error( + "File exit not handled before popRegions"); EndDepth--; } if (UnnestStart) { - // The region begins in a nested file or macro expansion. Create a - // separate region for each expansion. + // The region ends in a nested file or macro expansion. If the + // region is not a branch region, create a separate region for each + // expansion, and for all regions, update the StartLoc. Branch + // regions should not be split in order to keep a straightforward + // correspondance between the region and its associated branch + // condition, even if the condition spans multiple depths. SourceLocation NestedLoc = getEndOfFileOrMacro(StartLoc); assert(SM.isWrittenInSameFile(StartLoc, NestedLoc)); - if (!isRegionAlreadyAdded(StartLoc, NestedLoc)) - SourceRegions.emplace_back(Region.getCounter(), StartLoc, NestedLoc); + if (!isBranch && !isRegionAlreadyAdded(StartLoc, NestedLoc)) + SourceRegions.emplace_back(Region.getCounter(), StartLoc, + NestedLoc); StartLoc = getIncludeOrExpansionLoc(StartLoc); if (StartLoc.isInvalid()) - llvm::report_fatal_error("File exit not handled before popRegions"); + llvm::report_fatal_error( + "File exit not handled before popRegions"); StartDepth--; } } Region.setStartLoc(StartLoc); Region.setEndLoc(EndLoc); - MostRecentLocation = EndLoc; - // If this region happens to span an entire expansion, we need to make - // sure we don't overlap the parent region with it. - if (StartLoc == getStartOfFileOrMacro(StartLoc) && - EndLoc == getEndOfFileOrMacro(EndLoc)) - MostRecentLocation = getIncludeOrExpansionLoc(EndLoc); + if (!isBranch) { + MostRecentLocation = EndLoc; + // If this region happens to span an entire expansion, we need to + // make sure we don't overlap the parent region with it. + if (StartLoc == getStartOfFileOrMacro(StartLoc) && + EndLoc == getEndOfFileOrMacro(EndLoc)) + MostRecentLocation = getIncludeOrExpansionLoc(EndLoc); + } assert(SM.isWrittenInSameFile(Region.getBeginLoc(), EndLoc)); assert(SpellingRegion(SM, Region).isInSourceOrder()); SourceRegions.push_back(Region); - - if (ParentOfDeferredRegion) { - ParentOfDeferredRegion = false; - - // If there's an existing deferred region, keep the old one, because - // it means there are two consecutive returns (or a similar pattern). - if (!DeferredRegion.hasValue() && - // File IDs aren't gathered within macro expansions, so it isn't - // useful to try and create a deferred region inside of one. - !EndLoc.isMacroID()) - DeferredRegion = - SourceMappingRegion(Counter::getZero(), EndLoc, None); } - } else if (Region.isDeferred()) { - assert(!ParentOfDeferredRegion && "Consecutive deferred regions"); - ParentOfDeferredRegion = true; - } RegionStack.pop_back(); - - // If the zero region pushed after the last terminated region no longer - // exists, clear its cached information. - if (LastTerminatedRegion && - RegionStack.size() < LastTerminatedRegion->second) - LastTerminatedRegion = None; } - assert(!ParentOfDeferredRegion && "Deferred region with no parent"); } /// Return the currently active region. @@ -675,14 +702,61 @@ struct CounterCoverageMappingBuilder return ExitCount; } + /// Determine whether the given condition can be constant folded. + bool ConditionFoldsToBool(const Expr *Cond) { + Expr::EvalResult Result; + return (Cond->EvaluateAsInt(Result, CVM.getCodeGenModule().getContext())); + } + + /// Create a Branch Region around an instrumentable condition for coverage + /// and add it to the function's SourceRegions. A branch region tracks a + /// "True" counter and a "False" counter for boolean expressions that + /// result in the generation of a branch. + void createBranchRegion(const Expr *C, Counter TrueCnt, Counter FalseCnt) { + // Check for NULL conditions. + if (!C) + return; + + // Ensure we are an instrumentable condition (i.e. no "&&" or "||"). Push + // region onto RegionStack but immediately pop it (which adds it to the + // function's SourceRegions) because it doesn't apply to any other source + // code other than the Condition. + if (CodeGenFunction::isInstrumentedCondition(C)) { + // If a condition can fold to true or false, the corresponding branch + // will be removed. Create a region with both counters hard-coded to + // zero. This allows us to visualize them in a special way. + // Alternatively, we can prevent any optimization done via + // constant-folding by ensuring that ConstantFoldsToSimpleInteger() in + // CodeGenFunction.c always returns false, but that is very heavy-handed. + if (ConditionFoldsToBool(C)) + popRegions(pushRegion(Counter::getZero(), getStart(C), getEnd(C), + Counter::getZero())); + else + // Otherwise, create a region with the True counter and False counter. + popRegions(pushRegion(TrueCnt, getStart(C), getEnd(C), FalseCnt)); + } + } + + /// Create a Branch Region around a SwitchCase for code coverage + /// and add it to the function's SourceRegions. + void createSwitchCaseRegion(const SwitchCase *SC, Counter TrueCnt, + Counter FalseCnt) { + // Push region onto RegionStack but immediately pop it (which adds it to + // the function's SourceRegions) because it doesn't apply to any other + // source other than the SwitchCase. + popRegions(pushRegion(TrueCnt, getStart(SC), SC->getColonLoc(), FalseCnt)); + } + /// Check whether a region with bounds \c StartLoc and \c EndLoc /// is already added to \c SourceRegions. - bool isRegionAlreadyAdded(SourceLocation StartLoc, SourceLocation EndLoc) { + bool isRegionAlreadyAdded(SourceLocation StartLoc, SourceLocation EndLoc, + bool isBranch = false) { return SourceRegions.rend() != std::find_if(SourceRegions.rbegin(), SourceRegions.rend(), [&](const SourceMappingRegion &Region) { return Region.getBeginLoc() == StartLoc && - Region.getEndLoc() == EndLoc; + Region.getEndLoc() == EndLoc && + Region.isBranch() == isBranch; }); } @@ -699,7 +773,7 @@ struct CounterCoverageMappingBuilder if (getRegion().hasEndLoc() && MostRecentLocation == getEndOfFileOrMacro(MostRecentLocation) && isRegionAlreadyAdded(getStartOfFileOrMacro(MostRecentLocation), - MostRecentLocation)) + MostRecentLocation, getRegion().isBranch())) MostRecentLocation = getIncludeOrExpansionLoc(MostRecentLocation); } @@ -743,9 +817,14 @@ struct CounterCoverageMappingBuilder // The most nested region for each start location is the one with the // correct count. We avoid creating redundant regions by stopping once // we've seen this region. - if (StartLocs.insert(Loc).second) - SourceRegions.emplace_back(I.getCounter(), Loc, - getEndOfFileOrMacro(Loc)); + if (StartLocs.insert(Loc).second) { + if (I.isBranch()) + SourceRegions.emplace_back(I.getCounter(), I.getFalseCounter(), Loc, + getEndOfFileOrMacro(Loc), I.isBranch()); + else + SourceRegions.emplace_back(I.getCounter(), Loc, + getEndOfFileOrMacro(Loc)); + } Loc = getIncludeOrExpansionLoc(Loc); } I.setStartLoc(getPreciseTokenLocEnd(Loc)); @@ -778,8 +857,6 @@ struct CounterCoverageMappingBuilder handleFileExit(StartLoc); if (!Region.hasStartLoc()) Region.setStartLoc(StartLoc); - - completeDeferred(Region.getCounter(), StartLoc); } /// Mark \c S as a terminator, starting a zero region. @@ -790,30 +867,56 @@ struct CounterCoverageMappingBuilder if (!Region.hasEndLoc()) Region.setEndLoc(EndLoc); pushRegion(Counter::getZero()); - auto &ZeroRegion = getRegion(); - ZeroRegion.setDeferred(true); - LastTerminatedRegion = {EndLoc, RegionStack.size()}; + HasTerminateStmt = true; } /// Find a valid gap range between \p AfterLoc and \p BeforeLoc. Optional<SourceRange> findGapAreaBetween(SourceLocation AfterLoc, SourceLocation BeforeLoc) { + // If AfterLoc is in function-like macro, use the right parenthesis + // location. + if (AfterLoc.isMacroID()) { + FileID FID = SM.getFileID(AfterLoc); + const SrcMgr::ExpansionInfo *EI = &SM.getSLocEntry(FID).getExpansion(); + if (EI->isFunctionMacroExpansion()) + AfterLoc = EI->getExpansionLocEnd(); + } + + size_t StartDepth = locationDepth(AfterLoc); + size_t EndDepth = locationDepth(BeforeLoc); + while (!SM.isWrittenInSameFile(AfterLoc, BeforeLoc)) { + bool UnnestStart = StartDepth >= EndDepth; + bool UnnestEnd = EndDepth >= StartDepth; + if (UnnestEnd) { + assert(SM.isWrittenInSameFile(getStartOfFileOrMacro(BeforeLoc), + BeforeLoc)); + + BeforeLoc = getIncludeOrExpansionLoc(BeforeLoc); + assert(BeforeLoc.isValid()); + EndDepth--; + } + if (UnnestStart) { + assert(SM.isWrittenInSameFile(AfterLoc, + getEndOfFileOrMacro(AfterLoc))); + + AfterLoc = getIncludeOrExpansionLoc(AfterLoc); + assert(AfterLoc.isValid()); + AfterLoc = getPreciseTokenLocEnd(AfterLoc); + assert(AfterLoc.isValid()); + StartDepth--; + } + } + AfterLoc = getPreciseTokenLocEnd(AfterLoc); // If the start and end locations of the gap are both within the same macro // file, the range may not be in source order. if (AfterLoc.isMacroID() || BeforeLoc.isMacroID()) return None; - if (!SM.isWrittenInSameFile(AfterLoc, BeforeLoc)) + if (!SM.isWrittenInSameFile(AfterLoc, BeforeLoc) || + !SpellingRegion(SM, AfterLoc, BeforeLoc).isInSourceOrder()) return None; return {{AfterLoc, BeforeLoc}}; } - /// Find the source range after \p AfterStmt and before \p BeforeStmt. - Optional<SourceRange> findGapAreaBetween(const Stmt *AfterStmt, - const Stmt *BeforeStmt) { - return findGapAreaBetween(getPreciseTokenLocEnd(getEnd(AfterStmt)), - getStart(BeforeStmt)); - } - /// Emit a gap region between \p StartLoc and \p EndLoc with the given count. void fillGapAreaWithCount(SourceLocation StartLoc, SourceLocation EndLoc, Counter Count) { @@ -838,15 +941,13 @@ struct CounterCoverageMappingBuilder CoverageMappingModuleGen &CVM, llvm::DenseMap<const Stmt *, unsigned> &CounterMap, SourceManager &SM, const LangOptions &LangOpts) - : CoverageMappingBuilder(CVM, SM, LangOpts), CounterMap(CounterMap), - DeferredRegion(None) {} + : CoverageMappingBuilder(CVM, SM, LangOpts), CounterMap(CounterMap) {} /// Write the mapping data to the output stream void write(llvm::raw_ostream &OS) { llvm::SmallVector<unsigned, 8> VirtualFileMapping; gatherFileIDs(VirtualFileMapping); SourceRegionFilter Filter = emitExpansionRegions(); - assert(!DeferredRegion && "Deferred region never completed"); emitSourceRegions(Filter); gatherSkippedRegions(); @@ -861,15 +962,32 @@ struct CounterCoverageMappingBuilder void VisitStmt(const Stmt *S) { if (S->getBeginLoc().isValid()) extendRegion(S); + const Stmt *LastStmt = nullptr; + bool SaveTerminateStmt = HasTerminateStmt; + HasTerminateStmt = false; + GapRegionCounter = Counter::getZero(); for (const Stmt *Child : S->children()) - if (Child) + if (Child) { + // If last statement contains terminate statements, add a gap area + // between the two statements. Skipping attributed statements, because + // they don't have valid start location. + if (LastStmt && HasTerminateStmt && !dyn_cast<AttributedStmt>(Child)) { + auto Gap = findGapAreaBetween(getEnd(LastStmt), getStart(Child)); + if (Gap) + fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(), + GapRegionCounter); + SaveTerminateStmt = true; + HasTerminateStmt = false; + } this->Visit(Child); + LastStmt = Child; + } + if (SaveTerminateStmt) + HasTerminateStmt = true; handleFileExit(getEnd(S)); } void VisitDecl(const Decl *D) { - assert(!DeferredRegion && "Deferred region never completed"); - Stmt *Body = D->getBody(); // Do not propagate region counts into system headers. @@ -887,11 +1005,6 @@ struct CounterCoverageMappingBuilder propagateCounts(getRegionCounter(Body), Body, /*VisitChildren=*/!Defaulted); assert(RegionStack.empty() && "Regions entered but never exited"); - - // Discard the last uncompleted deferred region in a decl, if one exists. - // This prevents lines at the end of a function containing only whitespace - // or closing braces from being marked as uncovered. - DeferredRegion = None; } void VisitReturnStmt(const ReturnStmt *S) { @@ -901,6 +1014,18 @@ struct CounterCoverageMappingBuilder terminateRegion(S); } + void VisitCoroutineBodyStmt(const CoroutineBodyStmt *S) { + extendRegion(S); + Visit(S->getBody()); + } + + void VisitCoreturnStmt(const CoreturnStmt *S) { + extendRegion(S); + if (S->getOperand()) + Visit(S->getOperand()); + terminateRegion(S); + } + void VisitCXXThrowExpr(const CXXThrowExpr *E) { extendRegion(E); if (E->getSubExpr()) @@ -913,8 +1038,6 @@ struct CounterCoverageMappingBuilder void VisitLabelStmt(const LabelStmt *S) { Counter LabelCount = getRegionCounter(S); SourceLocation Start = getStart(S); - completeTopLevelDeferredRegion(LabelCount, Start); - completeDeferred(LabelCount, Start); // We can't extendRegion here or we risk overlapping with our new region. handleFileExit(Start); pushRegion(LabelCount, Start); @@ -959,6 +1082,9 @@ struct CounterCoverageMappingBuilder Counter BackedgeCount = propagateCounts(BodyCount, S->getBody()); BreakContinue BC = BreakContinueStack.pop_back_val(); + bool BodyHasTerminateStmt = HasTerminateStmt; + HasTerminateStmt = false; + // Go back to handle the condition. Counter CondCount = addCounters(ParentCount, BackedgeCount, BC.ContinueCount); @@ -966,14 +1092,22 @@ struct CounterCoverageMappingBuilder adjustForOutOfOrderTraversal(getEnd(S)); // The body count applies to the area immediately after the increment. - auto Gap = findGapAreaBetween(S->getCond(), S->getBody()); + auto Gap = findGapAreaBetween(S->getRParenLoc(), getStart(S->getBody())); if (Gap) fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(), BodyCount); Counter OutCount = addCounters(BC.BreakCount, subtractCounters(CondCount, BodyCount)); - if (OutCount != ParentCount) + if (OutCount != ParentCount) { pushRegion(OutCount); + GapRegionCounter = OutCount; + if (BodyHasTerminateStmt) + HasTerminateStmt = true; + } + + // Create Branch Region around condition. + createBranchRegion(S->getCond(), BodyCount, + subtractCounters(CondCount, BodyCount)); } void VisitDoStmt(const DoStmt *S) { @@ -988,13 +1122,25 @@ struct CounterCoverageMappingBuilder propagateCounts(addCounters(ParentCount, BodyCount), S->getBody()); BreakContinue BC = BreakContinueStack.pop_back_val(); + bool BodyHasTerminateStmt = HasTerminateStmt; + HasTerminateStmt = false; + Counter CondCount = addCounters(BackedgeCount, BC.ContinueCount); propagateCounts(CondCount, S->getCond()); Counter OutCount = addCounters(BC.BreakCount, subtractCounters(CondCount, BodyCount)); - if (OutCount != ParentCount) + if (OutCount != ParentCount) { pushRegion(OutCount); + GapRegionCounter = OutCount; + } + + // Create Branch Region around condition. + createBranchRegion(S->getCond(), BodyCount, + subtractCounters(CondCount, BodyCount)); + + if (BodyHasTerminateStmt) + HasTerminateStmt = true; } void VisitForStmt(const ForStmt *S) { @@ -1015,6 +1161,9 @@ struct CounterCoverageMappingBuilder Counter BackedgeCount = propagateCounts(BodyCount, S->getBody()); BreakContinue BodyBC = BreakContinueStack.pop_back_val(); + bool BodyHasTerminateStmt = HasTerminateStmt; + HasTerminateStmt = false; + // The increment is essentially part of the body but it needs to include // the count for all the continue statements. BreakContinue IncrementBC; @@ -1033,15 +1182,22 @@ struct CounterCoverageMappingBuilder } // The body count applies to the area immediately after the increment. - auto Gap = findGapAreaBetween(getPreciseTokenLocEnd(S->getRParenLoc()), - getStart(S->getBody())); + auto Gap = findGapAreaBetween(S->getRParenLoc(), getStart(S->getBody())); if (Gap) fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(), BodyCount); Counter OutCount = addCounters(BodyBC.BreakCount, IncrementBC.BreakCount, subtractCounters(CondCount, BodyCount)); - if (OutCount != ParentCount) + if (OutCount != ParentCount) { pushRegion(OutCount); + GapRegionCounter = OutCount; + if (BodyHasTerminateStmt) + HasTerminateStmt = true; + } + + // Create Branch Region around condition. + createBranchRegion(S->getCond(), BodyCount, + subtractCounters(CondCount, BodyCount)); } void VisitCXXForRangeStmt(const CXXForRangeStmt *S) { @@ -1059,9 +1215,11 @@ struct CounterCoverageMappingBuilder Counter BackedgeCount = propagateCounts(BodyCount, S->getBody()); BreakContinue BC = BreakContinueStack.pop_back_val(); + bool BodyHasTerminateStmt = HasTerminateStmt; + HasTerminateStmt = false; + // The body count applies to the area immediately after the range. - auto Gap = findGapAreaBetween(getPreciseTokenLocEnd(S->getRParenLoc()), - getStart(S->getBody())); + auto Gap = findGapAreaBetween(S->getRParenLoc(), getStart(S->getBody())); if (Gap) fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(), BodyCount); @@ -1069,8 +1227,16 @@ struct CounterCoverageMappingBuilder addCounters(ParentCount, BackedgeCount, BC.ContinueCount); Counter OutCount = addCounters(BC.BreakCount, subtractCounters(LoopCount, BodyCount)); - if (OutCount != ParentCount) + if (OutCount != ParentCount) { pushRegion(OutCount); + GapRegionCounter = OutCount; + if (BodyHasTerminateStmt) + HasTerminateStmt = true; + } + + // Create Branch Region around condition. + createBranchRegion(S->getCond(), BodyCount, + subtractCounters(LoopCount, BodyCount)); } void VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S) { @@ -1086,8 +1252,7 @@ struct CounterCoverageMappingBuilder BreakContinue BC = BreakContinueStack.pop_back_val(); // The body count applies to the area immediately after the collection. - auto Gap = findGapAreaBetween(getPreciseTokenLocEnd(S->getRParenLoc()), - getStart(S->getBody())); + auto Gap = findGapAreaBetween(S->getRParenLoc(), getStart(S->getBody())); if (Gap) fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(), BodyCount); @@ -1095,8 +1260,10 @@ struct CounterCoverageMappingBuilder addCounters(ParentCount, BackedgeCount, BC.ContinueCount); Counter OutCount = addCounters(BC.BreakCount, subtractCounters(LoopCount, BodyCount)); - if (OutCount != ParentCount) + if (OutCount != ParentCount) { pushRegion(OutCount); + GapRegionCounter = OutCount; + } } void VisitSwitchStmt(const SwitchStmt *S) { @@ -1114,10 +1281,9 @@ struct CounterCoverageMappingBuilder // Make a region for the body of the switch. If the body starts with // a case, that case will reuse this region; otherwise, this covers // the unreachable code at the beginning of the switch body. - size_t Index = - pushRegion(Counter::getZero(), getStart(CS->body_front())); - for (const auto *Child : CS->children()) - Visit(Child); + size_t Index = pushRegion(Counter::getZero(), getStart(CS)); + getRegion().setGap(true); + Visit(Body); // Set the end for the body of the switch, if it isn't already set. for (size_t i = RegionStack.size(); i != Index; --i) { @@ -1135,14 +1301,38 @@ struct CounterCoverageMappingBuilder BreakContinueStack.back().ContinueCount = addCounters( BreakContinueStack.back().ContinueCount, BC.ContinueCount); + Counter ParentCount = getRegion().getCounter(); Counter ExitCount = getRegionCounter(S); SourceLocation ExitLoc = getEnd(S); pushRegion(ExitCount); + GapRegionCounter = ExitCount; // Ensure that handleFileExit recognizes when the end location is located // in a different file. MostRecentLocation = getStart(S); handleFileExit(ExitLoc); + + // Create a Branch Region around each Case. Subtract the case's + // counter from the Parent counter to track the "False" branch count. + Counter CaseCountSum; + bool HasDefaultCase = false; + const SwitchCase *Case = S->getSwitchCaseList(); + for (; Case; Case = Case->getNextSwitchCase()) { + HasDefaultCase = HasDefaultCase || isa<DefaultStmt>(Case); + CaseCountSum = addCounters(CaseCountSum, getRegionCounter(Case)); + createSwitchCaseRegion( + Case, getRegionCounter(Case), + subtractCounters(ParentCount, getRegionCounter(Case))); + } + + // If no explicit default case exists, create a branch region to represent + // the hidden branch, which will be added later by the CodeGen. This region + // will be associated with the switch statement's condition. + if (!HasDefaultCase) { + Counter DefaultTrue = subtractCounters(ParentCount, CaseCountSum); + Counter DefaultFalse = subtractCounters(ParentCount, DefaultTrue); + createBranchRegion(S->getCond(), DefaultTrue, DefaultFalse); + } } void VisitSwitchCase(const SwitchCase *S) { @@ -1158,6 +1348,8 @@ struct CounterCoverageMappingBuilder else pushRegion(Count, getStart(S)); + GapRegionCounter = Count; + if (const auto *CS = dyn_cast<CaseStmt>(S)) { Visit(CS->getLHS()); if (const Expr *RHS = CS->getRHS()) @@ -1183,7 +1375,7 @@ struct CounterCoverageMappingBuilder propagateCounts(ParentCount, S->getCond()); // The 'then' count applies to the area immediately after the condition. - auto Gap = findGapAreaBetween(S->getCond(), S->getThen()); + auto Gap = findGapAreaBetween(S->getRParenLoc(), getStart(S->getThen())); if (Gap) fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(), ThenCount); @@ -1192,17 +1384,29 @@ struct CounterCoverageMappingBuilder Counter ElseCount = subtractCounters(ParentCount, ThenCount); if (const Stmt *Else = S->getElse()) { + bool ThenHasTerminateStmt = HasTerminateStmt; + HasTerminateStmt = false; + // The 'else' count applies to the area immediately after the 'then'. - Gap = findGapAreaBetween(S->getThen(), Else); + Gap = findGapAreaBetween(getEnd(S->getThen()), getStart(Else)); if (Gap) fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(), ElseCount); extendRegion(Else); OutCount = addCounters(OutCount, propagateCounts(ElseCount, Else)); + + if (ThenHasTerminateStmt) + HasTerminateStmt = true; } else OutCount = addCounters(OutCount, ElseCount); - if (OutCount != ParentCount) + if (OutCount != ParentCount) { pushRegion(OutCount); + GapRegionCounter = OutCount; + } + + // Create Branch Region around condition. + createBranchRegion(S->getCond(), ThenCount, + subtractCounters(ParentCount, ThenCount)); } void VisitCXXTryStmt(const CXXTryStmt *S) { @@ -1230,7 +1434,7 @@ struct CounterCoverageMappingBuilder Counter ParentCount = getRegion().getCounter(); Counter TrueCount = getRegionCounter(E); - Visit(E->getCond()); + propagateCounts(ParentCount, E->getCond()); if (!isa<BinaryConditionalOperator>(E)) { // The 'then' count applies to the area immediately after the condition. @@ -1246,6 +1450,10 @@ struct CounterCoverageMappingBuilder extendRegion(E->getFalseExpr()); propagateCounts(subtractCounters(ParentCount, TrueCount), E->getFalseExpr()); + + // Create Branch Region around condition. + createBranchRegion(E->getCond(), TrueCount, + subtractCounters(ParentCount, TrueCount)); } void VisitBinLAnd(const BinaryOperator *E) { @@ -1253,8 +1461,26 @@ struct CounterCoverageMappingBuilder propagateCounts(getRegion().getCounter(), E->getLHS()); handleFileExit(getEnd(E->getLHS())); + // Counter tracks the right hand side of a logical and operator. extendRegion(E->getRHS()); propagateCounts(getRegionCounter(E), E->getRHS()); + + // Extract the RHS's Execution Counter. + Counter RHSExecCnt = getRegionCounter(E); + + // Extract the RHS's "True" Instance Counter. + Counter RHSTrueCnt = getRegionCounter(E->getRHS()); + + // Extract the Parent Region Counter. + Counter ParentCnt = getRegion().getCounter(); + + // Create Branch Region around LHS condition. + createBranchRegion(E->getLHS(), RHSExecCnt, + subtractCounters(ParentCnt, RHSExecCnt)); + + // Create Branch Region around RHS condition. + createBranchRegion(E->getRHS(), RHSTrueCnt, + subtractCounters(RHSExecCnt, RHSTrueCnt)); } void VisitBinLOr(const BinaryOperator *E) { @@ -1262,8 +1488,26 @@ struct CounterCoverageMappingBuilder propagateCounts(getRegion().getCounter(), E->getLHS()); handleFileExit(getEnd(E->getLHS())); + // Counter tracks the right hand side of a logical or operator. extendRegion(E->getRHS()); propagateCounts(getRegionCounter(E), E->getRHS()); + + // Extract the RHS's Execution Counter. + Counter RHSExecCnt = getRegionCounter(E); + + // Extract the RHS's "False" Instance Counter. + Counter RHSFalseCnt = getRegionCounter(E->getRHS()); + + // Extract the Parent Region Counter. + Counter ParentCnt = getRegion().getCounter(); + + // Create Branch Region around LHS condition. + createBranchRegion(E->getLHS(), subtractCounters(ParentCnt, RHSExecCnt), + RHSExecCnt); + + // Create Branch Region around RHS condition. + createBranchRegion(E->getRHS(), subtractCounters(RHSExecCnt, RHSFalseCnt), + RHSFalseCnt); } void VisitLambdaExpr(const LambdaExpr *LE) { @@ -1272,19 +1516,6 @@ struct CounterCoverageMappingBuilder } }; -std::string getCoverageSection(const CodeGenModule &CGM) { - return llvm::getInstrProfSectionName( - llvm::IPSK_covmap, - CGM.getContext().getTargetInfo().getTriple().getObjectFormat()); -} - -std::string normalizeFilename(StringRef Filename) { - llvm::SmallString<256> Path(Filename); - llvm::sys::fs::make_absolute(Path); - llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true); - return Path.str().str(); -} - } // end anonymous namespace static void dump(llvm::raw_ostream &OS, StringRef FunctionName, @@ -1306,41 +1537,116 @@ static void dump(llvm::raw_ostream &OS, StringRef FunctionName, case CounterMappingRegion::GapRegion: OS << "Gap,"; break; + case CounterMappingRegion::BranchRegion: + OS << "Branch,"; + break; } OS << "File " << R.FileID << ", " << R.LineStart << ":" << R.ColumnStart << " -> " << R.LineEnd << ":" << R.ColumnEnd << " = "; Ctx.dump(R.Count, OS); + + if (R.Kind == CounterMappingRegion::BranchRegion) { + OS << ", "; + Ctx.dump(R.FalseCount, OS); + } + if (R.Kind == CounterMappingRegion::ExpansionRegion) OS << " (Expanded file = " << R.ExpandedFileID << ")"; OS << "\n"; } } -void CoverageMappingModuleGen::addFunctionMappingRecord( - llvm::GlobalVariable *NamePtr, StringRef NameValue, uint64_t FuncHash, - const std::string &CoverageMapping, bool IsUsed) { +CoverageMappingModuleGen::CoverageMappingModuleGen( + CodeGenModule &CGM, CoverageSourceInfo &SourceInfo) + : CGM(CGM), SourceInfo(SourceInfo) { + CoveragePrefixMap = CGM.getCodeGenOpts().CoveragePrefixMap; +} + +std::string CoverageMappingModuleGen::getCurrentDirname() { + if (!CGM.getCodeGenOpts().CoverageCompilationDir.empty()) + return CGM.getCodeGenOpts().CoverageCompilationDir; + + SmallString<256> CWD; + llvm::sys::fs::current_path(CWD); + return CWD.str().str(); +} + +std::string CoverageMappingModuleGen::normalizeFilename(StringRef Filename) { + llvm::SmallString<256> Path(Filename); + llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true); + for (const auto &Entry : CoveragePrefixMap) { + if (llvm::sys::path::replace_path_prefix(Path, Entry.first, Entry.second)) + break; + } + return Path.str().str(); +} + +static std::string getInstrProfSection(const CodeGenModule &CGM, + llvm::InstrProfSectKind SK) { + return llvm::getInstrProfSectionName( + SK, CGM.getContext().getTargetInfo().getTriple().getObjectFormat()); +} + +void CoverageMappingModuleGen::emitFunctionMappingRecord( + const FunctionInfo &Info, uint64_t FilenamesRef) { llvm::LLVMContext &Ctx = CGM.getLLVMContext(); - if (!FunctionRecordTy) { + + // Assign a name to the function record. This is used to merge duplicates. + std::string FuncRecordName = "__covrec_" + llvm::utohexstr(Info.NameHash); + + // A dummy description for a function included-but-not-used in a TU can be + // replaced by full description provided by a different TU. The two kinds of + // descriptions play distinct roles: therefore, assign them different names + // to prevent `linkonce_odr` merging. + if (Info.IsUsed) + FuncRecordName += "u"; + + // Create the function record type. + const uint64_t NameHash = Info.NameHash; + const uint64_t FuncHash = Info.FuncHash; + const std::string &CoverageMapping = Info.CoverageMapping; #define COVMAP_FUNC_RECORD(Type, LLVMType, Name, Init) LLVMType, - llvm::Type *FunctionRecordTypes[] = { - #include "llvm/ProfileData/InstrProfData.inc" - }; - FunctionRecordTy = - llvm::StructType::get(Ctx, makeArrayRef(FunctionRecordTypes), - /*isPacked=*/true); - } + llvm::Type *FunctionRecordTypes[] = { +#include "llvm/ProfileData/InstrProfData.inc" + }; + auto *FunctionRecordTy = + llvm::StructType::get(Ctx, makeArrayRef(FunctionRecordTypes), + /*isPacked=*/true); - #define COVMAP_FUNC_RECORD(Type, LLVMType, Name, Init) Init, + // Create the function record constant. +#define COVMAP_FUNC_RECORD(Type, LLVMType, Name, Init) Init, llvm::Constant *FunctionRecordVals[] = { #include "llvm/ProfileData/InstrProfData.inc" }; - FunctionRecords.push_back(llvm::ConstantStruct::get( - FunctionRecordTy, makeArrayRef(FunctionRecordVals))); + auto *FuncRecordConstant = llvm::ConstantStruct::get( + FunctionRecordTy, makeArrayRef(FunctionRecordVals)); + + // Create the function record global. + auto *FuncRecord = new llvm::GlobalVariable( + CGM.getModule(), FunctionRecordTy, /*isConstant=*/true, + llvm::GlobalValue::LinkOnceODRLinkage, FuncRecordConstant, + FuncRecordName); + FuncRecord->setVisibility(llvm::GlobalValue::HiddenVisibility); + FuncRecord->setSection(getInstrProfSection(CGM, llvm::IPSK_covfun)); + FuncRecord->setAlignment(llvm::Align(8)); + if (CGM.supportsCOMDAT()) + FuncRecord->setComdat(CGM.getModule().getOrInsertComdat(FuncRecordName)); + + // Make sure the data doesn't get deleted. + CGM.addUsedGlobal(FuncRecord); +} + +void CoverageMappingModuleGen::addFunctionMappingRecord( + llvm::GlobalVariable *NamePtr, StringRef NameValue, uint64_t FuncHash, + const std::string &CoverageMapping, bool IsUsed) { + llvm::LLVMContext &Ctx = CGM.getLLVMContext(); + const uint64_t NameHash = llvm::IndexedInstrProf::ComputeHash(NameValue); + FunctionRecords.push_back({NameHash, FuncHash, CoverageMapping, IsUsed}); + if (!IsUsed) FunctionNames.push_back( llvm::ConstantExpr::getBitCast(NamePtr, llvm::Type::getInt8PtrTy(Ctx))); - CoverageMappings.push_back(CoverageMapping); if (CGM.getCodeGenOpts().DumpCoverageMapping) { // Dump the coverage mapping data for this function by decoding the @@ -1348,18 +1654,17 @@ void CoverageMappingModuleGen::addFunctionMappingRecord( // also processed by the CoverageMappingWriter which performs // additional minimization operations such as reducing the number of // expressions. + llvm::SmallVector<std::string, 16> FilenameStrs; std::vector<StringRef> Filenames; std::vector<CounterExpression> Expressions; std::vector<CounterMappingRegion> Regions; - llvm::SmallVector<std::string, 16> FilenameStrs; - llvm::SmallVector<StringRef, 16> FilenameRefs; - FilenameStrs.resize(FileEntries.size()); - FilenameRefs.resize(FileEntries.size()); + FilenameStrs.resize(FileEntries.size() + 1); + FilenameStrs[0] = normalizeFilename(getCurrentDirname()); for (const auto &Entry : FileEntries) { auto I = Entry.second; FilenameStrs[I] = normalizeFilename(Entry.first->getName()); - FilenameRefs[I] = FilenameStrs[I]; } + ArrayRef<std::string> FilenameRefs = llvm::makeArrayRef(FilenameStrs); RawCoverageMappingReader Reader(CoverageMapping, FilenameRefs, Filenames, Expressions, Regions); if (Reader.read()) @@ -1376,46 +1681,30 @@ void CoverageMappingModuleGen::emit() { // Create the filenames and merge them with coverage mappings llvm::SmallVector<std::string, 16> FilenameStrs; - llvm::SmallVector<StringRef, 16> FilenameRefs; - FilenameStrs.resize(FileEntries.size()); - FilenameRefs.resize(FileEntries.size()); + FilenameStrs.resize(FileEntries.size() + 1); + // The first filename is the current working directory. + FilenameStrs[0] = normalizeFilename(getCurrentDirname()); for (const auto &Entry : FileEntries) { auto I = Entry.second; FilenameStrs[I] = normalizeFilename(Entry.first->getName()); - FilenameRefs[I] = FilenameStrs[I]; } - std::string FilenamesAndCoverageMappings; - llvm::raw_string_ostream OS(FilenamesAndCoverageMappings); - CoverageFilenamesSectionWriter(FilenameRefs).write(OS); - - // Stream the content of CoverageMappings to OS while keeping - // memory consumption under control. - size_t CoverageMappingSize = 0; - for (auto &S : CoverageMappings) { - CoverageMappingSize += S.size(); - OS << S; - S.clear(); - S.shrink_to_fit(); + std::string Filenames; + { + llvm::raw_string_ostream OS(Filenames); + CoverageFilenamesSectionWriter(FilenameStrs).write(OS); } - CoverageMappings.clear(); - CoverageMappings.shrink_to_fit(); + auto *FilenamesVal = + llvm::ConstantDataArray::getString(Ctx, Filenames, false); + const int64_t FilenamesRef = llvm::IndexedInstrProf::ComputeHash(Filenames); - 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; - OS.write_zeros(8 - Rem); - } - 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); + // Emit the function records. + for (const FunctionInfo &Info : FunctionRecords) + emitFunctionMappingRecord(Info, FilenamesRef); + const unsigned NRecords = 0; + const size_t FilenamesSize = Filenames.size(); + const unsigned CoverageMappingSize = 0; llvm::Type *CovDataHeaderTypes[] = { #define COVMAP_HEADER(Type, LLVMType, Name, Init) LLVMType, #include "llvm/ProfileData/InstrProfData.inc" @@ -1430,18 +1719,16 @@ void CoverageMappingModuleGen::emit() { CovDataHeaderTy, makeArrayRef(CovDataHeaderVals)); // Create the coverage data record - llvm::Type *CovDataTypes[] = {CovDataHeaderTy, RecordsTy, - FilenamesAndMappingsVal->getType()}; + llvm::Type *CovDataTypes[] = {CovDataHeaderTy, FilenamesVal->getType()}; auto CovDataTy = llvm::StructType::get(Ctx, makeArrayRef(CovDataTypes)); - llvm::Constant *TUDataVals[] = {CovDataHeaderVal, RecordsVal, - FilenamesAndMappingsVal}; + llvm::Constant *TUDataVals[] = {CovDataHeaderVal, FilenamesVal}; auto CovDataVal = llvm::ConstantStruct::get(CovDataTy, makeArrayRef(TUDataVals)); auto CovData = new llvm::GlobalVariable( - CGM.getModule(), CovDataTy, true, llvm::GlobalValue::InternalLinkage, + CGM.getModule(), CovDataTy, true, llvm::GlobalValue::PrivateLinkage, CovDataVal, llvm::getCoverageMappingVarName()); - CovData->setSection(getCoverageSection(CGM)); + CovData->setSection(getInstrProfSection(CGM, llvm::IPSK_covmap)); CovData->setAlignment(llvm::Align(8)); // Make sure the data doesn't get deleted. @@ -1463,7 +1750,7 @@ unsigned CoverageMappingModuleGen::getFileID(const FileEntry *File) { auto It = FileEntries.find(File); if (It != FileEntries.end()) return It->second; - unsigned FileID = FileEntries.size(); + unsigned FileID = FileEntries.size() + 1; FileEntries.insert(std::make_pair(File, FileID)); return FileID; } |
