summaryrefslogtreecommitdiff
path: root/external/apache2/llvm/dist/clang/lib/CodeGen/CoverageMappingGen.cpp
diff options
context:
space:
mode:
authorjoerg <joerg@NetBSD.org>2021-05-30 01:25:15 +0000
committerjoerg <joerg@NetBSD.org>2021-05-30 01:25:15 +0000
commit13fbcb42fff2606c3ce926a7c4bb8989aa18758c (patch)
treedcace6c89967106796d44b1a7c5e2269a20967e2 /external/apache2/llvm/dist/clang/lib/CodeGen/CoverageMappingGen.cpp
parent13338e33506ade63440b4ba74479551bdc21c0f6 (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.cpp797
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;
}