diff --git a/llvm/docs/CoverageMappingFormat.rst b/llvm/docs/CoverageMappingFormat.rst index 46cc9d10f319..30b11fe2f31d 100644 --- a/llvm/docs/CoverageMappingFormat.rst +++ b/llvm/docs/CoverageMappingFormat.rst @@ -258,7 +258,7 @@ The coverage mapping variable generated by Clang has 3 fields: i32 2, ; The number of function records i32 20, ; The length of the string that contains the encoded translation unit filenames i32 20, ; The length of the string that contains the encoded coverage mapping data - i32 1, ; Coverage mapping format version + i32 2, ; Coverage mapping format version }, [2 x { i64, i32, i64 }] [ ; Function records { i64, i32, i64 } { @@ -274,6 +274,8 @@ The coverage mapping variable generated by Clang has 3 fields: [40 x i8] c"..." ; Encoded data (dissected later) }, section "__llvm_covmap", align 8 +The current version of the format is version 3. The only difference from version 2 is that a special encoding for column end locations was introduced to indicate gap regions. + The function record layout has evolved since version 1. In version 1, the function record for *foo* is defined as follows: .. code-block:: llvm @@ -296,7 +298,7 @@ The coverage mapping header has the following fields: * The length of the string in the third field of *__llvm_coverage_mapping* that contains the encoded coverage mapping data. -* The format version. The current version is 2 (encoded as a 1). +* The format version. The current version is 3 (encoded as a 2). .. _function records: @@ -602,4 +604,6 @@ The source range record contains the following fields: * *numLines*: The difference between the ending line and the starting line of the current mapping region. -* *columnEnd*: The ending column of the mapping region. +* *columnEnd*: The ending column of the mapping region. If the high bit is set, + the current mapping region is a gap area. A count for a gap area is only used + as the line execution count if there are no other regions on a line. diff --git a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h b/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h index eb560d9c3dff..13a9093bd84f 100644 --- a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h +++ b/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h @@ -213,7 +213,11 @@ struct CounterMappingRegion { /// A SkippedRegion represents a source range with code that was skipped /// by a preprocessor or similar means. - SkippedRegion + SkippedRegion, + + /// A GapRegion is like a CodeRegion, but its count is only set as the + /// line execution count when its the only region in the line. + GapRegion }; Counter Count; @@ -250,6 +254,13 @@ struct CounterMappingRegion { LineEnd, ColumnEnd, SkippedRegion); } + static CounterMappingRegion + makeGapRegion(Counter Count, unsigned FileID, unsigned LineStart, + unsigned ColumnStart, unsigned LineEnd, unsigned ColumnEnd) { + return CounterMappingRegion(Count, FileID, 0, LineStart, ColumnStart, + LineEnd, (1U << 31) | ColumnEnd, GapRegion); + } + inline LineColPair startLoc() const { return LineColPair(LineStart, ColumnStart); } @@ -377,19 +388,23 @@ struct CoverageSegment { bool HasCount; /// Whether this enters a new region or returns to a previous count. bool IsRegionEntry; + /// Whether this enters a gap region. + bool IsGapRegion; CoverageSegment(unsigned Line, unsigned Col, bool IsRegionEntry) : Line(Line), Col(Col), Count(0), HasCount(false), - IsRegionEntry(IsRegionEntry) {} + IsRegionEntry(IsRegionEntry), IsGapRegion(false) {} CoverageSegment(unsigned Line, unsigned Col, uint64_t Count, - bool IsRegionEntry) + bool IsRegionEntry, bool IsGapRegion = false) : Line(Line), Col(Col), Count(Count), HasCount(true), - IsRegionEntry(IsRegionEntry) {} + IsRegionEntry(IsRegionEntry), IsGapRegion(IsGapRegion) {} friend bool operator==(const CoverageSegment &L, const CoverageSegment &R) { - return std::tie(L.Line, L.Col, L.Count, L.HasCount, L.IsRegionEntry) == - std::tie(R.Line, R.Col, R.Count, R.HasCount, R.IsRegionEntry); + return std::tie(L.Line, L.Col, L.Count, L.HasCount, L.IsRegionEntry, + L.IsGapRegion) == std::tie(R.Line, R.Col, R.Count, + R.HasCount, R.IsRegionEntry, + R.IsGapRegion); } }; @@ -660,7 +675,10 @@ enum CovMapVersion { // name string pointer to MD5 to support name section compression. Name // section is also compressed. Version2 = 1, - // The current version is Version2 + // A new interpretation of the columnEnd field is added in order to mark + // regions as gap areas. + Version3 = 2, + // The current version is Version3 CurrentVersion = INSTR_PROF_COVMAP_VERSION }; diff --git a/llvm/include/llvm/ProfileData/InstrProfData.inc b/llvm/include/llvm/ProfileData/InstrProfData.inc index be0dd4ad04bf..66d63a462a07 100644 --- a/llvm/include/llvm/ProfileData/InstrProfData.inc +++ b/llvm/include/llvm/ProfileData/InstrProfData.inc @@ -630,7 +630,7 @@ serializeValueProfDataFrom(ValueProfRecordClosure *Closure, /* Indexed profile format version (start from 1). */ #define INSTR_PROF_INDEX_VERSION 4 /* Coverage mapping format vresion (start from 0). */ -#define INSTR_PROF_COVMAP_VERSION 1 +#define INSTR_PROF_COVMAP_VERSION 2 /* Profile version is always of type uint64_t. Reserve the upper 8 bits in the * version for other variants of profile. We set the lowest bit of the upper 8 diff --git a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp index 81e1c6bc6450..4c257cf38e59 100644 --- a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp +++ b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp @@ -320,7 +320,7 @@ class SegmentBuilder { /// Emit a segment with the count from \p Region starting at \p StartLoc. // - /// \p IsRegionEntry: The segment is at the start of a new region. + /// \p IsRegionEntry: The segment is at the start of a new non-gap region. /// \p EmitSkippedRegion: The segment must be emitted as a skipped region. void startSegment(const CountedRegion &Region, LineColPair StartLoc, bool IsRegionEntry, bool EmitSkippedRegion = false) { @@ -337,7 +337,8 @@ class SegmentBuilder { if (HasCount) Segments.emplace_back(StartLoc.first, StartLoc.second, - Region.ExecutionCount, IsRegionEntry); + Region.ExecutionCount, IsRegionEntry, + Region.Kind == CounterMappingRegion::GapRegion); else Segments.emplace_back(StartLoc.first, StartLoc.second, IsRegionEntry); @@ -346,7 +347,8 @@ class SegmentBuilder { dbgs() << "Segment at " << Last.Line << ":" << Last.Col << " (count = " << Last.Count << ")" << (Last.IsRegionEntry ? ", RegionEntry" : "") - << (!Last.HasCount ? ", Skipped" : "") << "\n"; + << (!Last.HasCount ? ", Skipped" : "") + << (Last.IsGapRegion ? ", Gap" : "") << "\n"; }); } @@ -419,20 +421,22 @@ class SegmentBuilder { completeRegionsUntil(CurStartLoc, FirstCompletedRegion); } + bool GapRegion = CR.value().Kind == CounterMappingRegion::GapRegion; + // Try to emit a segment for the current region. if (CurStartLoc == CR.value().endLoc()) { // Avoid making zero-length regions active. If it's the last region, // emit a skipped segment. Otherwise use its predecessor's count. const bool Skipped = (CR.index() + 1) == Regions.size(); startSegment(ActiveRegions.empty() ? CR.value() : *ActiveRegions.back(), - CurStartLoc, true, Skipped); + CurStartLoc, !GapRegion, Skipped); continue; } if (CR.index() + 1 == Regions.size() || CurStartLoc != Regions[CR.index() + 1].startLoc()) { // Emit a segment if the next region doesn't start at the same location // as this one. - startSegment(CR.value(), CurStartLoc, true); + startSegment(CR.value(), CurStartLoc, !GapRegion); } // This region is active (i.e not completed). diff --git a/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp b/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp index 9bf70c2e3aad..467a36ca7483 100644 --- a/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp +++ b/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp @@ -216,6 +216,13 @@ Error RawCoverageMappingReader::readMappingRegionsSubArray( if (auto Err = readIntMax(ColumnEnd, std::numeric_limits::max())) return Err; LineStart += LineStartDelta; + + // If the high bit of ColumnEnd is set, this is a gap region. + if (ColumnEnd & (1U << 31)) { + Kind = CounterMappingRegion::GapRegion; + ColumnEnd &= ~(1U << 31); + } + // Adjust the column locations for the empty regions that are supposed to // cover whole lines. Those regions should be encoded with the // column range (1 -> std::numeric_limits::max()), but because @@ -534,11 +541,16 @@ Expected> CovMapFuncRecordReader::get( return llvm::make_unique>(P, R, F); case CovMapVersion::Version2: + case CovMapVersion::Version3: // Decompress the name data. if (Error E = P.create(P.getNameData())) return std::move(E); - return llvm::make_unique>(P, R, F); + if (Version == CovMapVersion::Version2) + return llvm::make_unique>(P, R, F); + else + return llvm::make_unique>(P, R, F); } llvm_unreachable("Unsupported version"); } diff --git a/llvm/lib/ProfileData/Coverage/CoverageMappingWriter.cpp b/llvm/lib/ProfileData/Coverage/CoverageMappingWriter.cpp index 4a1ce16c4869..49e82e481059 100644 --- a/llvm/lib/ProfileData/Coverage/CoverageMappingWriter.cpp +++ b/llvm/lib/ProfileData/Coverage/CoverageMappingWriter.cpp @@ -171,6 +171,7 @@ void CoverageMappingWriter::write(raw_ostream &OS) { Counter Count = Minimizer.adjust(I->Count); switch (I->Kind) { case CounterMappingRegion::CodeRegion: + case CounterMappingRegion::GapRegion: writeCounter(MinExpressions, Count, OS); break; case CounterMappingRegion::ExpansionRegion: { diff --git a/llvm/test/tools/llvm-cov/Inputs/deferred-regions.covmapping b/llvm/test/tools/llvm-cov/Inputs/deferred-regions.covmapping index 757f3eea7204..4434b66513c0 100644 Binary files a/llvm/test/tools/llvm-cov/Inputs/deferred-regions.covmapping and b/llvm/test/tools/llvm-cov/Inputs/deferred-regions.covmapping differ diff --git a/llvm/test/tools/llvm-cov/deferred-region.cpp b/llvm/test/tools/llvm-cov/deferred-region.cpp index 1b191ac0f1a7..6f6da7ec804b 100644 --- a/llvm/test/tools/llvm-cov/deferred-region.cpp +++ b/llvm/test/tools/llvm-cov/deferred-region.cpp @@ -1,7 +1,7 @@ // RUN: llvm-cov show %S/Inputs/deferred-regions.covmapping -instr-profile %S/Inputs/deferred-regions.profdata -show-line-counts-or-regions -dump -path-equivalence=/Users/vk/src/llvm.org-coverage-braces/llvm/test/tools,%S/.. %s 2>%t.markers > %t.out && FileCheck %s -input-file %t.out && FileCheck %s -input-file %t.markers -check-prefix=MARKER void foo(int x) { - if (x == 0) { + if (x == 0) { // CHECK: [[@LINE]]|{{ +}}2| return; // CHECK: [[@LINE]]|{{ +}}1| } @@ -17,10 +17,10 @@ void for_loop() { return; // CHECK: [[@LINE]]|{{ +}}0| for (int i = 0; i < 10; ++i) { // CHECK: [[@LINE]]|{{ +}}2| - if (i % 2 == 0) + if (i % 2 == 0) // CHECK: [[@LINE]]|{{ +}}2| continue; // CHECK: [[@LINE]]|{{ +}}1| - if (i % 5 == 0) + if (i % 5 == 0) // CHECK: [[@LINE]]|{{ +}}1| break; // CHECK: [[@LINE]]|{{ +}}0| int x = i; @@ -37,19 +37,19 @@ void while_loop() { int x = 0; while (++x < 10) { // CHECK: [[@LINE]]|{{ +}}3| - if (x == 1) + if (x == 1) // CHECK: [[@LINE]]|{{ +}}2| continue; // CHECK: [[@LINE]]|{{ +}}1| while (++x < 4) { // CHECK: [[@LINE]]|{{ +}}1| - if (x == 3) + if (x == 3) // CHECK: [[@LINE]]|{{ +}}1| break; // CHECK: [[@LINE]]|{{ +}}1| // CHECK: [[@LINE]]|{{ +}}0| while (++x < 5) {} // CHECK: [[@LINE]]|{{ +}}0| } // CHECK: [[@LINE]]|{{ +}}1| - if (x == 0) + if (x == 0) // CHECK: [[@LINE]]|{{ +}}1| throw Error(); // CHECK: [[@LINE]]|{{ +}}0| - + // CHECK: [[@LINE]]|{{ +}}1| while (++x < 9) { // CHECK: [[@LINE]]|{{ +}}6| if (x == 0) // CHECK: [[@LINE]]|{{ +}}5| break; // CHECK: [[@LINE]]|{{ +}}0| @@ -59,12 +59,12 @@ void while_loop() { } void gotos() { - if (false) + if (false) // CHECK: [[@LINE]]|{{ +}}1| goto out; // CHECK: [[@LINE]]|{{ +}}0| + // CHECK: [[@LINE]]|{{ +}}1| + return; // CHECK: [[@LINE]]|{{ +}}1| - return; - -out: // CHECK: [[@LINE]]|{{ +}}1| +out: // CHECK: [[@LINE]]|{{ +}}0| return; } @@ -80,23 +80,16 @@ int main() { // MARKER: Marker at 4:7 = 2 // MARKER-NEXT: Highlighted line 17, 5 -> 11 -// MARKER-NEXT: Marker at 17:5 = 0 // MARKER-NEXT: Marker at 19:3 = 1 // MARKER-NEXT: Marker at 19:19 = 2 // MARKER-NEXT: Marker at 19:27 = 1 -// MARKER-NEXT: Marker at 21:7 = 1 // MARKER-NEXT: Marker at 23:5 = 1 // MARKER-NEXT: Marker at 23:9 = 1 // MARKER-NEXT: Highlighted line 24, 7 -> 12 -// MARKER-NEXT: Marker at 24:7 = 0 // MARKER-NEXT: Highlighted line 36, 5 -> 11 -// MARKER-NEXT: Marker at 36:5 = 0 // MARKER-NEXT: Marker at 39:10 = 3 -// MARKER-NEXT: Marker at 41:7 = 1 // MARKER-NEXT: Marker at 43:5 = 1 // MARKER-NEXT: Marker at 43:12 = 1 -// MARKER-NEXT: Highlighted line 45, 14 -> ? -// MARKER-NEXT: Marker at 45:9 = 1 // MARKER-NEXT: Highlighted line 46, 1 -> ? // MARKER-NEXT: Highlighted line 47, 1 -> 7 // MARKER-NEXT: Highlighted line 47, 7 -> 14 @@ -107,13 +100,10 @@ int main() { // MARKER-NEXT: Marker at 47:14 = 0 // MARKER-NEXT: Marker at 47:23 = 0 // MARKER-NEXT: Highlighted line 51, 7 -> 20 -// MARKER-NEXT: Marker at 51:7 = 0 // MARKER-NEXT: Marker at 53:5 = 1 // MARKER-NEXT: Marker at 53:12 = 6 // MARKER-NEXT: Highlighted line 55, 9 -> 14 // MARKER-NEXT: Highlighted line 63, 5 -> 13 -// MARKER-NEXT: Marker at 63:5 = 0 // MARKER-NEXT: Highlighted line 67, 1 -> ? // MARKER-NEXT: Highlighted line 68, 1 -> 8 -// MARKER-NEXT: Highlighted line 68, 8 -> ? // MARKER-NEXT: Highlighted line 69, 1 -> 2 diff --git a/llvm/tools/llvm-cov/SourceCoverageView.cpp b/llvm/tools/llvm-cov/SourceCoverageView.cpp index 79c2c69571c8..1965595cdb7f 100644 --- a/llvm/tools/llvm-cov/SourceCoverageView.cpp +++ b/llvm/tools/llvm-cov/SourceCoverageView.cpp @@ -89,7 +89,7 @@ LineCoverageStats::LineCoverageStats( // Find the minimum number of regions which start in this line. unsigned MinRegionCount = 0; auto isStartOfRegion = [](const coverage::CoverageSegment *S) { - return S->HasCount && S->IsRegionEntry; + return !S->IsGapRegion && S->HasCount && S->IsRegionEntry; }; for (unsigned I = 0; I < LineSegments.size() && MinRegionCount < 2; ++I) if (isStartOfRegion(LineSegments[I])) @@ -112,16 +112,19 @@ LineCoverageStats::LineCoverageStats( // avoid erroneously using the wrapped count, and to avoid picking region // counts which come from deferred regions. if (LineSegments.size() > 1) { - for (unsigned I = 0; I < LineSegments.size() - 1; ++I) - ExecutionCount = std::max(ExecutionCount, LineSegments[I]->Count); + for (unsigned I = 0; I < LineSegments.size() - 1; ++I) { + if (!LineSegments[I]->IsGapRegion) + ExecutionCount = std::max(ExecutionCount, LineSegments[I]->Count); + } return; } - // Just pick the maximum count. - if (WrappedSegment && WrappedSegment->HasCount) + // If a non-gap region starts here, use its count. Otherwise use the wrapped + // count. + if (MinRegionCount == 1) + ExecutionCount = LineSegments[0]->Count; + else ExecutionCount = WrappedSegment->Count; - if (!LineSegments.empty()) - ExecutionCount = std::max(ExecutionCount, LineSegments[0]->Count); } unsigned SourceCoverageView::getFirstUncoveredLineNo() { diff --git a/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp b/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp index 1a21b72b14e2..300a0162bd11 100644 --- a/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp +++ b/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp @@ -527,7 +527,7 @@ void SourceCoverageViewHTML::renderLine( const auto *CurSeg = Segments[I]; if (CurSeg->Col == ExpansionCol) Color = "cyan"; - else if (CheckIfUncovered(CurSeg)) + else if (!CurSeg->IsGapRegion && CheckIfUncovered(CurSeg)) Color = "red"; else Color = None; diff --git a/llvm/tools/llvm-cov/SourceCoverageViewText.cpp b/llvm/tools/llvm-cov/SourceCoverageViewText.cpp index a88558fceca5..a78c0575cdc0 100644 --- a/llvm/tools/llvm-cov/SourceCoverageViewText.cpp +++ b/llvm/tools/llvm-cov/SourceCoverageViewText.cpp @@ -120,7 +120,7 @@ void SourceCoverageViewText::renderLine( Col = End; if (Col == ExpansionCol) Highlight = raw_ostream::CYAN; - else if (S->HasCount && S->Count == 0) + else if (!S->IsGapRegion && S->HasCount && S->Count == 0) Highlight = raw_ostream::RED; else Highlight = None;