https://github.com/Lambdaris created https://github.com/llvm/llvm-project/pull/94137
To close #93560 . # Changes 1. Clang only sets the counter in folded branch to `Zero`. And llvm-cov shows a branch as folded as long as either of its counters is `Zero`. 2. Add two additional results, `uncoverable` and `unreachable` to MCDC conditions. `uncoverable` means the condition can not effect value of the decision, while `unreachable` means the condition is always short-circuited. Since both the two kinds of conditions is by no means covered, they are excluded from MCDC and informed to users. The scheme to identify such conditions is described at this [comment](https://github.com/llvm/llvm-project/issues/93560#issuecomment-2142551335). >From d2fe89a625e84b585362e8800d7fac2d034ec85a Mon Sep 17 00:00:00 2001 From: Lambdaris <lambda...@outlook.com> Date: Sun, 2 Jun 2024 10:19:31 +0800 Subject: [PATCH 1/2] [coverage] Mark branches with either counter is zero as folded --- clang/lib/CodeGen/CoverageMappingGen.cpp | 27 +++++++++---- .../CoverageMapping/branch-constfolded.cpp | 40 +++++++++---------- clang/test/CoverageMapping/if.cpp | 4 +- clang/test/CoverageMapping/macro-expansion.c | 10 ++--- .../test/CoverageMapping/mcdc-scratch-space.c | 4 +- .../CoverageMapping/mcdc-system-headers.cpp | 4 +- .../ProfileData/Coverage/CoverageMapping.h | 4 +- .../ProfileData/Coverage/CoverageMapping.cpp | 4 +- .../test/tools/llvm-cov/branch-c-general.test | 12 +++--- 9 files changed, 61 insertions(+), 48 deletions(-) diff --git a/clang/lib/CodeGen/CoverageMappingGen.cpp b/clang/lib/CodeGen/CoverageMappingGen.cpp index 6ce2d32dd292e..37e6543795558 100644 --- a/clang/lib/CodeGen/CoverageMappingGen.cpp +++ b/clang/lib/CodeGen/CoverageMappingGen.cpp @@ -1084,9 +1084,13 @@ struct CounterCoverageMappingBuilder } /// Determine whether the given condition can be constant folded. - bool ConditionFoldsToBool(const Expr *Cond) { + bool ConditionFoldsToBool(const Expr *Cond, bool &ResultBool) { Expr::EvalResult Result; - return (Cond->EvaluateAsInt(Result, CVM.getCodeGenModule().getContext())); + if (Cond->EvaluateAsInt(Result, CVM.getCodeGenModule().getContext())) { + ResultBool = Result.Val.getInt().getBoolValue(); + return true; + } + return false; } /// Create a Branch Region around an instrumentable condition for coverage @@ -1113,15 +1117,22 @@ struct CounterCoverageMappingBuilder BranchParams = mcdc::BranchParameters{ID, Conds}; // 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. + // will be removed. Create a region with the relative counter 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(), BranchParams)); - else + bool ConstantBool = false; + if (ConditionFoldsToBool(C, ConstantBool)) { + if (ConstantBool) { + popRegions(pushRegion(TrueCnt, getStart(C), getEnd(C), + Counter::getZero(), BranchParams)); + } else { + popRegions(pushRegion(Counter::getZero(), getStart(C), getEnd(C), + FalseCnt, BranchParams)); + } + + } else // Otherwise, create a region with the True counter and False counter. popRegions(pushRegion(TrueCnt, getStart(C), getEnd(C), FalseCnt, BranchParams)); diff --git a/clang/test/CoverageMapping/branch-constfolded.cpp b/clang/test/CoverageMapping/branch-constfolded.cpp index c8755d5d752b6..1e1639ba796df 100644 --- a/clang/test/CoverageMapping/branch-constfolded.cpp +++ b/clang/test/CoverageMapping/branch-constfolded.cpp @@ -5,94 +5,94 @@ // CHECK-LABEL: _Z6fand_0b: bool fand_0(bool a) { // MCDC: Decision,File 0, [[@LINE+1]]:10 -> [[@LINE+1]]:20 = M:0, C:2 - return false && a; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:15 = 0, 0 + return false && a; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:15 = 0, (#0 - #1) } // CHECK: Branch,File 0, [[@LINE-1]]:19 -> [[@LINE-1]]:20 = #2, (#1 - #2) // CHECK-LABEL: _Z6fand_1b: bool fand_1(bool a) { // MCDC: Decision,File 0, [[@LINE+1]]:10 -> [[@LINE+1]]:19 = M:0, C:2 return a && true; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:11 = #1, (#0 - #1) -} // CHECK: Branch,File 0, [[@LINE-1]]:15 -> [[@LINE-1]]:19 = 0, 0 +} // CHECK: Branch,File 0, [[@LINE-1]]:15 -> [[@LINE-1]]:19 = #2, 0 // CHECK-LABEL: _Z6fand_2bb: bool fand_2(bool a, bool b) {// MCDC: Decision,File 0, [[@LINE+1]]:10 -> [[@LINE+1]]:25 = M:0, C:3 - return false && a && b; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:15 = 0, 0 + return false && a && b; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:15 = 0, (#0 - #3) } // CHECK: Branch,File 0, [[@LINE-1]]:19 -> [[@LINE-1]]:20 = #4, (#3 - #4) // CHECK: Branch,File 0, [[@LINE-2]]:24 -> [[@LINE-2]]:25 = #2, (#1 - #2) // CHECK-LABEL: _Z6fand_3bb: bool fand_3(bool a, bool b) {// MCDC: Decision,File 0, [[@LINE+1]]:10 -> [[@LINE+1]]:24 = M:0, C:3 return a && true && b; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:11 = #3, (#0 - #3) -} // CHECK: Branch,File 0, [[@LINE-1]]:15 -> [[@LINE-1]]:19 = 0, 0 +} // CHECK: Branch,File 0, [[@LINE-1]]:15 -> [[@LINE-1]]:19 = #4, 0 // CHECK: Branch,File 0, [[@LINE-2]]:23 -> [[@LINE-2]]:24 = #2, (#1 - #2) // CHECK-LABEL: _Z6fand_4bb: bool fand_4(bool a, bool b) {// MCDC: Decision,File 0, [[@LINE+1]]:10 -> [[@LINE+1]]:25 = M:0, C:3 return a && b && false; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:11 = #3, (#0 - #3) } // CHECK: Branch,File 0, [[@LINE-1]]:15 -> [[@LINE-1]]:16 = #4, (#3 - #4) - // CHECK: Branch,File 0, [[@LINE-2]]:20 -> [[@LINE-2]]:25 = 0, 0 + // CHECK: Branch,File 0, [[@LINE-2]]:20 -> [[@LINE-2]]:25 = 0, (#1 - #2) // CHECK-LABEL: _Z6fand_5b: bool fand_5(bool a) { // MCDC: Decision,File 0, [[@LINE+1]]:10 -> [[@LINE+1]]:23 = M:0, C:2 - return false && true; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:15 = 0, 0 -} // CHECK: Branch,File 0, [[@LINE-1]]:19 -> [[@LINE-1]]:23 = 0, 0 + return false && true; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:15 = 0, (#0 - #1) +} // CHECK: Branch,File 0, [[@LINE-1]]:19 -> [[@LINE-1]]:23 = #2, 0 // CHECK-LABEL: _Z6fand_6b: bool fand_6(bool a) { // MCDC: Decision,File 0, [[@LINE+1]]:10 -> [[@LINE+1]]:19 = M:0, C:2 - return true && a; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:14 = 0, 0 + return true && a; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:14 = #1, 0 } // CHECK: Branch,File 0, [[@LINE-1]]:18 -> [[@LINE-1]]:19 = #2, (#1 - #2) // CHECK-LABEL: _Z6fand_7b: bool fand_7(bool a) { // MCDC: Decision,File 0, [[@LINE+1]]:10 -> [[@LINE+1]]:20 = M:0, C:2 return a && false; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:11 = #1, (#0 - #1) -} // CHECK: Branch,File 0, [[@LINE-1]]:15 -> [[@LINE-1]]:20 = 0, 0 +} // CHECK: Branch,File 0, [[@LINE-1]]:15 -> [[@LINE-1]]:20 = 0, (#1 - #2) // CHECK-LABEL: _Z5for_0b: bool for_0(bool a) { // MCDC: Decision,File 0, [[@LINE+1]]:10 -> [[@LINE+1]]:19 = M:0, C:2 - return true || a; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:14 = 0, 0 + return true || a; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:14 = (#0 - #1), 0 } // CHECK: Branch,File 0, [[@LINE-1]]:18 -> [[@LINE-1]]:19 = (#1 - #2), #2 // CHECK-LABEL: _Z5for_1b: bool for_1(bool a) { // MCDC: Decision,File 0, [[@LINE+1]]:10 -> [[@LINE+1]]:20 = M:0, C:2 return a || false; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:11 = (#0 - #1), #1 -} // CHECK: Branch,File 0, [[@LINE-1]]:15 -> [[@LINE-1]]:20 = 0, 0 +} // CHECK: Branch,File 0, [[@LINE-1]]:15 -> [[@LINE-1]]:20 = 0, #2 // CHECK-LABEL: _Z5for_2bb: bool for_2(bool a, bool b) {// MCDC: Decision,File 0, [[@LINE+1]]:10 -> [[@LINE+1]]:24 = M:0, C:3 - return true || a || b; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:14 = 0, 0 + return true || a || b; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:14 = (#0 - #3), 0 } // CHECK: Branch,File 0, [[@LINE-1]]:18 -> [[@LINE-1]]:19 = (#3 - #4), #4 // CHECK: Branch,File 0, [[@LINE-2]]:23 -> [[@LINE-2]]:24 = (#1 - #2), #2 // CHECK-LABEL: _Z5for_3bb: bool for_3(bool a, bool b) {// MCDC: Decision,File 0, [[@LINE+1]]:10 -> [[@LINE+1]]:25 = M:0, C:3 return a || false || b; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:11 = (#0 - #3), #3 -} // CHECK: Branch,File 0, [[@LINE-1]]:15 -> [[@LINE-1]]:20 = 0, 0 +} // CHECK: Branch,File 0, [[@LINE-1]]:15 -> [[@LINE-1]]:20 = 0, #4 // CHECK: Branch,File 0, [[@LINE-2]]:24 -> [[@LINE-2]]:25 = (#1 - #2), #2 // CHECK-LABEL: _Z5for_4bb: bool for_4(bool a, bool b) {// MCDC: Decision,File 0, [[@LINE+1]]:10 -> [[@LINE+1]]:24 = M:0, C:3 return a || b || true; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:11 = (#0 - #3), #3 } // CHECK: Branch,File 0, [[@LINE-1]]:15 -> [[@LINE-1]]:16 = (#3 - #4), #4 - // CHECK: Branch,File 0, [[@LINE-2]]:20 -> [[@LINE-2]]:24 = 0, 0 + // CHECK: Branch,File 0, [[@LINE-2]]:20 -> [[@LINE-2]]:24 = (#1 - #2), 0 // CHECK-LABEL: _Z5for_5b: bool for_5(bool a) { // MCDC: Decision,File 0, [[@LINE+1]]:10 -> [[@LINE+1]]:23 = M:0, C:2 - return true || false; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:14 = 0, 0 -} // CHECK: Branch,File 0, [[@LINE-1]]:18 -> [[@LINE-1]]:23 = 0, 0 + return true || false; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:14 = (#0 - #1), 0 +} // CHECK: Branch,File 0, [[@LINE-1]]:18 -> [[@LINE-1]]:23 = 0, #2 // CHECK-LABEL: _Z5for_6b: bool for_6(bool a) { // MCDC: Decision,File 0, [[@LINE+1]]:10 -> [[@LINE+1]]:20 = M:0, C:2 - return false || a; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:15 = 0, 0 + return false || a; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:15 = 0, #1 } // CHECK: Branch,File 0, [[@LINE-1]]:19 -> [[@LINE-1]]:20 = (#1 - #2), #2 // CHECK-LABEL: _Z5for_7b: bool for_7(bool a) { // MCDC: Decision,File 0, [[@LINE+1]]:10 -> [[@LINE+1]]:19 = M:0, C:2 return a || true; // CHECK: Branch,File 0, [[@LINE]]:10 -> [[@LINE]]:11 = (#0 - #1), #1 -} // CHECK: Branch,File 0, [[@LINE-1]]:15 -> [[@LINE-1]]:19 = 0, 0 +} // CHECK: Branch,File 0, [[@LINE-1]]:15 -> [[@LINE-1]]:19 = (#1 - #2), 0 // CHECK-LABEL: _Z5for_8b: bool for_8(bool a) { // MCDC: Decision,File 0, [[@LINE+3]]:7 -> [[@LINE+3]]:20 = M:0, C:2 - // CHECK: Branch,File 0, [[@LINE+2]]:7 -> [[@LINE+2]]:11 = 0, 0 - // CHECK: Branch,File 0, [[@LINE+1]]:15 -> [[@LINE+1]]:20 = 0, 0 + // CHECK: Branch,File 0, [[@LINE+2]]:7 -> [[@LINE+2]]:11 = #2, 0 + // CHECK: Branch,File 0, [[@LINE+1]]:15 -> [[@LINE+1]]:20 = 0, (#2 - #3) if (true && false) return true; else diff --git a/clang/test/CoverageMapping/if.cpp b/clang/test/CoverageMapping/if.cpp index 445cdfc20e2af..b6fd525e930f9 100644 --- a/clang/test/CoverageMapping/if.cpp +++ b/clang/test/CoverageMapping/if.cpp @@ -14,7 +14,7 @@ struct S { // CHECK-LABEL: _Z3foov: // CHECK-NEXT: [[@LINE+3]]:12 -> [[@LINE+8]]:2 = #0 // CHECK-NEXT: [[@LINE+3]]:15 -> [[@LINE+3]]:19 = #0 - // CHECK-NEXT: Branch,File 0, [[@LINE+2]]:15 -> [[@LINE+2]]:19 = 0, 0 + // CHECK-NEXT: Branch,File 0, [[@LINE+2]]:15 -> [[@LINE+2]]:19 = #2, 0 void foo() { // CHECK-NEXT: Gap,File 0, [[@LINE+1]]:21 -> [[@LINE+1]]:22 = #2 if (int j = true ? nop() // CHECK-NEXT: [[@LINE]]:22 -> [[@LINE]]:27 = #2 : nop(); // CHECK-NEXT: [[@LINE]]:22 -> [[@LINE]]:27 = (#0 - #2) @@ -168,7 +168,7 @@ int main() { // CHECK: File 0, [[@LINE]]:12 -> {{[0-9]+}}:2 = // GH-45481 S s; s.the_prop = 0? 1 : 2; // CHECK-NEXT: File 0, [[@LINE]]:16 -> [[@LINE]]:17 = #0 - // CHECK-NEXT: Branch,File 0, [[@LINE-1]]:16 -> [[@LINE-1]]:17 = 0, 0 + // CHECK-NEXT: Branch,File 0, [[@LINE-1]]:16 -> [[@LINE-1]]:17 = 0, (#0 - #7) // CHECK-NEXT: Gap,File 0, [[@LINE-2]]:18 -> [[@LINE-2]]:19 = #7 // CHECK-NEXT: File 0, [[@LINE-3]]:19 -> [[@LINE-3]]:20 = #7 // CHECK-NEXT: File 0, [[@LINE-4]]:23 -> [[@LINE-4]]:24 = (#0 - #7) diff --git a/clang/test/CoverageMapping/macro-expansion.c b/clang/test/CoverageMapping/macro-expansion.c index ad71fb15eda42..4cd2c93437193 100644 --- a/clang/test/CoverageMapping/macro-expansion.c +++ b/clang/test/CoverageMapping/macro-expansion.c @@ -4,29 +4,29 @@ // CHECK: File 1, [[@LINE+7]]:12 -> [[@LINE+7]]:38 = #0 // CHECK-NEXT: File 1, [[@LINE+6]]:15 -> [[@LINE+6]]:28 = (#0 + #2) // CHECK-NEXT: File 1, [[@LINE+5]]:21 -> [[@LINE+5]]:22 = (#0 + #2) -// CHECK: Branch,File 1, [[@LINE+4]]:21 -> [[@LINE+4]]:22 = 0, 0 +// CHECK: Branch,File 1, [[@LINE+4]]:21 -> [[@LINE+4]]:22 = 0, ((#0 + #2) - #3) // CHECK-NEXT: File 1, [[@LINE+3]]:24 -> [[@LINE+3]]:26 = #3 // CHECK-NEXT: File 1, [[@LINE+2]]:36 -> [[@LINE+2]]:37 = (#0 + #2) -// CHECK-NEXT: Branch,File 1, [[@LINE+1]]:36 -> [[@LINE+1]]:37 = 0, 0 +// CHECK-NEXT: Branch,File 1, [[@LINE+1]]:36 -> [[@LINE+1]]:37 = 0, #0 #define M1 do { if (0) {} } while (0) // CHECK-NEXT: File 2, [[@LINE+12]]:15 -> [[@LINE+12]]:41 = #0 // CHECK-NEXT: File 2, [[@LINE+11]]:18 -> [[@LINE+11]]:31 = (#0 + #4) // CHECK-NEXT: File 2, [[@LINE+10]]:24 -> [[@LINE+10]]:25 = (#0 + #4) // CHECK: File 2, [[@LINE+9]]:27 -> [[@LINE+9]]:29 = #5 // CHECK-NEXT: File 2, [[@LINE+8]]:39 -> [[@LINE+8]]:40 = (#0 + #4) -// CHECK-NEXT: Branch,File 2, [[@LINE+7]]:39 -> [[@LINE+7]]:40 = 0, 0 +// CHECK-NEXT: Branch,File 2, [[@LINE+7]]:39 -> [[@LINE+7]]:40 = 0, #0 // CHECK-NEXT: File 3, [[@LINE+6]]:15 -> [[@LINE+6]]:41 = #0 // CHECK-NEXT: File 3, [[@LINE+5]]:18 -> [[@LINE+5]]:31 = (#0 + #6) // CHECK-NEXT: File 3, [[@LINE+4]]:24 -> [[@LINE+4]]:25 = (#0 + #6) // CHECK: File 3, [[@LINE+3]]:27 -> [[@LINE+3]]:29 = #7 // CHECK-NEXT: File 3, [[@LINE+2]]:39 -> [[@LINE+2]]:40 = (#0 + #6) -// CHECK-NEXT: Branch,File 3, [[@LINE+1]]:39 -> [[@LINE+1]]:40 = 0, 0 +// CHECK-NEXT: Branch,File 3, [[@LINE+1]]:39 -> [[@LINE+1]]:40 = 0, #0 #define M2(x) do { if (x) {} } while (0) // CHECK-NEXT: File 4, [[@LINE+5]]:15 -> [[@LINE+5]]:38 = #0 // CHECK-NEXT: File 4, [[@LINE+4]]:18 -> [[@LINE+4]]:28 = (#0 + #8) // CHECK-NEXT: Expansion,File 4, [[@LINE+3]]:20 -> [[@LINE+3]]:22 = (#0 + #8) // CHECK-NEXT: File 4, [[@LINE+2]]:36 -> [[@LINE+2]]:37 = (#0 + #8) -// CHECK-NEXT: Branch,File 4, [[@LINE+1]]:36 -> [[@LINE+1]]:37 = 0, 0 +// CHECK-NEXT: Branch,File 4, [[@LINE+1]]:36 -> [[@LINE+1]]:37 = 0, #0 #define M3(x) do { M2(x); } while (0) // CHECK-NEXT: File 5, [[@LINE+4]]:15 -> [[@LINE+4]]:27 = #0 // CHECK-NEXT: File 5, [[@LINE+3]]:16 -> [[@LINE+3]]:19 = #0 diff --git a/clang/test/CoverageMapping/mcdc-scratch-space.c b/clang/test/CoverageMapping/mcdc-scratch-space.c index 2b5b12d9dcad6..1b22bb7e421b6 100644 --- a/clang/test/CoverageMapping/mcdc-scratch-space.c +++ b/clang/test/CoverageMapping/mcdc-scratch-space.c @@ -3,7 +3,7 @@ // CHECK: builtin_macro0: int builtin_macro0(int a) { // CHECK: Decision,File 0, [[@LINE+1]]:11 -> [[@LINE+2]]:15 = M:0, C:2 - return (__LINE__ // CHECK: Branch,File 0, [[@LINE]]:11 -> [[@LINE]]:11 = 0, 0 [1,2,0] + return (__LINE__ // CHECK: Branch,File 0, [[@LINE]]:11 -> [[@LINE]]:11 = #1, 0 [1,2,0] && a); // CHECK: Branch,File 0, [[@LINE]]:14 -> [[@LINE]]:15 = #2, (#1 - #2) [2,0,0] } @@ -11,7 +11,7 @@ int builtin_macro0(int a) { int builtin_macro1(int a) { // CHECK: Decision,File 0, [[@LINE+1]]:11 -> [[@LINE+2]]:22 = M:0, C:2 return (a // CHECK: Branch,File 0, [[@LINE]]:11 -> [[@LINE]]:12 = (#0 - #1), #1 [1,0,2] - || __LINE__); // CHECK: Branch,File 0, [[@LINE]]:14 -> [[@LINE]]:14 = 0, 0 [2,0,0] + || __LINE__); // CHECK: Branch,File 0, [[@LINE]]:14 -> [[@LINE]]:14 = (#1 - #2), 0 [2,0,0] } #define PRE(x) pre_##x diff --git a/clang/test/CoverageMapping/mcdc-system-headers.cpp b/clang/test/CoverageMapping/mcdc-system-headers.cpp index 4dfbb17c2bba8..7a867be5c9e6c 100644 --- a/clang/test/CoverageMapping/mcdc-system-headers.cpp +++ b/clang/test/CoverageMapping/mcdc-system-headers.cpp @@ -17,10 +17,10 @@ int func0(int a) { // CHECK: Decision,File 0, [[@LINE+3]]:11 -> [[@LINE+3]]:21 = M:0, C:2 // W_SYS: Expansion,File 0, [[@LINE+2]]:11 -> [[@LINE+2]]:16 = #0 (Expanded file = 1) - // X_SYS: Branch,File 0, [[@LINE+1]]:11 -> [[@LINE+1]]:11 = 0, 0 [1,2,0] + // X_SYS: Branch,File 0, [[@LINE+1]]:11 -> [[@LINE+1]]:11 = #1, 0 [1,2,0] return (CONST && a); // CHECK: Branch,File 0, [[@LINE-1]]:20 -> [[@LINE-1]]:21 = #2, (#1 - #2) [2,0,0] - // W_SYS: Branch,File 1, [[@LINE-16]]:15 -> [[@LINE-16]]:17 = 0, 0 [1,2,0] + // W_SYS: Branch,File 1, [[@LINE-16]]:15 -> [[@LINE-16]]:17 = #1, 0 [1,2,0] } // CHECK: _Z5func1ii: diff --git a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h b/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h index da03104045249..c0458b93d1343 100644 --- a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h +++ b/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h @@ -718,9 +718,9 @@ struct FunctionRecord { Region.Kind == CounterMappingRegion::MCDCBranchRegion) { CountedBranchRegions.emplace_back(Region, Count, FalseCount, HasSingleByteCoverage); - // If both counters are hard-coded to zero, then this region represents a + // If either counter is hard-coded to zero, then this region represents a // constant-folded branch. - if (Region.Count.isZero() && Region.FalseCount.isZero()) + if (Region.Count.isZero() || Region.FalseCount.isZero()) CountedBranchRegions.back().Folded = true; return; } diff --git a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp index 8c81bbe8e9c4e..e5df21a3bcf7d 100644 --- a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp +++ b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp @@ -500,7 +500,7 @@ class MCDCRecordProcessor : NextIDsBuilder, mcdc::TVIdxBuilder { const auto &BranchParams = B->getBranchParams(); PosToID[I] = BranchParams.ID; CondLoc[I] = B->startLoc(); - Folded[I++] = (B->Count.isZero() && B->FalseCount.isZero()); + Folded[I++] = (B->Count.isZero() || B->FalseCount.isZero()); } // Using Profile Bitmap from runtime, mark the executed test vectors. @@ -605,6 +605,8 @@ static unsigned getMaxCounterID(const CounterMappingContext &Ctx, unsigned MaxCounterID = 0; for (const auto &Region : Record.MappingRegions) { MaxCounterID = std::max(MaxCounterID, Ctx.getMaxCounterID(Region.Count)); + MaxCounterID = + std::max(MaxCounterID, Ctx.getMaxCounterID(Region.FalseCount)); } return MaxCounterID; } diff --git a/llvm/test/tools/llvm-cov/branch-c-general.test b/llvm/test/tools/llvm-cov/branch-c-general.test index 9b5889babde36..419e92d3fa521 100644 --- a/llvm/test/tools/llvm-cov/branch-c-general.test +++ b/llvm/test/tools/llvm-cov/branch-c-general.test @@ -47,7 +47,7 @@ // CHECK: Branch (103:9): [True: 9, False: 1] // CHECK: switches() -// CHECK: Branch (113:3): [True: 1, False: 0] +// CHECK: Branch (113:3): [Folded - Ignored] // CHECK: Branch (117:63): [True: 15, False: 0] // CHECK: Branch (119:5): [True: 1, False: 14] // CHECK: Branch (120:11): [True: 0, False: 1] @@ -57,7 +57,7 @@ // CHECK: Branch (126:11): [True: 3, False: 0] // CHECK: Branch (128:5): [True: 4, False: 11] // CHECK: Branch (129:11): [True: 4, False: 0] -// CHECK: Branch (131:7): [True: 4, False: 0] +// CHECK: Branch (131:7): [Folded - Ignored] // CHECK: Branch (132:13): [True: 4, False: 0] // CHECK: Branch (136:5): [True: 5, False: 10] // CHECK: Branch (137:11): [True: 1, False: 4] @@ -120,7 +120,7 @@ // REPORT-NEXT: conditionals 24 0 100.00% 15 0 100.00% 16 2 87.50% // REPORT-NEXT: early_exits 20 4 80.00% 25 2 92.00% 16 6 62.50% // REPORT-NEXT: jumps 39 12 69.23% 48 2 95.83% 26 9 65.38% -// REPORT-NEXT: switches 28 5 82.14% 38 4 89.47% 30 9 70.00% +// REPORT-NEXT: switches 28 5 82.14% 38 4 89.47% 26 7 73.08% // REPORT-NEXT: big_switch 25 1 96.00% 32 0 100.00% 30 6 80.00% // REPORT-NEXT: boolean_operators 16 0 100.00% 13 0 100.00% 22 2 90.91% // REPORT-NEXT: boolop_loops 19 0 100.00% 14 0 100.00% 16 2 87.50% @@ -129,12 +129,12 @@ // REPORT-NEXT: main 1 0 100.00% 16 0 100.00% 0 0 0.00% // REPORT-NEXT: c-general.c:static_func 4 0 100.00% 4 0 100.00% 2 0 100.00% // REPORT-NEXT: --- -// REPORT-NEXT: TOTAL 197 24 87.82% 234 8 96.58% 174 38 78.16% +// REPORT-NEXT: TOTAL 197 24 87.82% 234 8 96.58% 170 36 78.82% // Test file-level report. // RUN: llvm-profdata merge %S/Inputs/branch-c-general.proftext -o %t.profdata // RUN: llvm-cov report %S/Inputs/branch-c-general.o32l -instr-profile %t.profdata -path-equivalence=/tmp,%S/Inputs %S/Inputs/branch-c-general.c | FileCheck %s -check-prefix=FILEREPORT -// FILEREPORT: TOTAL{{.*}}174 38 78.16% +// FILEREPORT: TOTAL{{.*}}170 36 78.82% // Test color True/False output. // RUN: llvm-cov show --use-color --show-branches=count %S/Inputs/branch-c-general.o32l -instr-profile %t.profdata -path-equivalence=/tmp,%S/Inputs %S/Inputs/branch-c-general.c | FileCheck %s -check-prefix=USECOLOR @@ -161,6 +161,6 @@ // HTML-INDEX: <td class='column-entry-yellow'> // HTML-INDEX: 87.82% (173/197) // HTML-INDEX: <td class='column-entry-red'> -// HTML-INDEX: 78.16% (136/174) +// HTML-INDEX: 78.82% (134/170) // HTML-INDEX: <tr class='light-row-bold'> // HTML-INDEX: Totals >From f0250b88ac0b3f2de738d7d6caf2684b314658dd Mon Sep 17 00:00:00 2001 From: Lambdaris <lambda...@outlook.com> Date: Sun, 2 Jun 2024 10:58:56 +0800 Subject: [PATCH 2/2] [coverage] MCDC reports unrechable and uncoverable conditions --- clang/docs/SourceBasedCodeCoverage.rst | 12 ++ .../ProfileData/Coverage/CoverageMapping.h | 67 +++++++--- .../ProfileData/Coverage/CoverageMapping.cpp | 114 ++++++++++++++++-- .../llvm-cov/Inputs/mcdc-const-folding.o | Bin 34504 -> 34592 bytes llvm/test/tools/llvm-cov/Inputs/mcdc-const.o | Bin 5208 -> 5296 bytes llvm/test/tools/llvm-cov/Inputs/mcdc-macro.o | Bin 6480 -> 6560 bytes llvm/test/tools/llvm-cov/mcdc-const.test | 94 +++++++-------- llvm/tools/llvm-cov/CoverageReport.cpp | 5 +- llvm/tools/llvm-cov/CoverageSummaryInfo.cpp | 2 +- .../tools/llvm-cov/SourceCoverageViewHTML.cpp | 7 +- .../tools/llvm-cov/SourceCoverageViewText.cpp | 14 ++- 11 files changed, 229 insertions(+), 86 deletions(-) diff --git a/clang/docs/SourceBasedCodeCoverage.rst b/clang/docs/SourceBasedCodeCoverage.rst index cee706289284d..f875187c80608 100644 --- a/clang/docs/SourceBasedCodeCoverage.rst +++ b/clang/docs/SourceBasedCodeCoverage.rst @@ -496,6 +496,18 @@ starts a new boolean expression that is separated from the other conditions by the operator ``func()``. When this is encountered, a warning will be generated and the boolean expression will not be instrumented. +Similar as branch coverage, MCDC also is not tracked for constant folded conditions. +Moreover it's even not tracked for conditions that can not effect outcomes of decisions +due to other constants. These conditions will be shown as ``uncoverable`` or +``unreachable``, determined by whether they can be visited. For instance, in +``a || true || b``, value of the decision is always ``true`` by reason of the constant +condition. Thus ``a`` can not lead the decision to ``false`` even though it could branch, +while ``b`` is always short-circuited. Hence ``a`` is shown as ``uncoverable`` +and ``b`` is marked as ``unreachable``. Statistics of MCDC does not count constant +conditions which do not vary or such conditions which make no difference on value of +decisions. If a decision is proved to no branch theoretically, it shows mark ``Folded`` +rather than coverage percent. + Switch statements ----------------- diff --git a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h b/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h index c0458b93d1343..157ac83d69ee6 100644 --- a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h +++ b/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h @@ -384,6 +384,13 @@ struct MCDCRecord { /// are effectively ignored. enum CondState { MCDC_DontCare = -1, MCDC_False = 0, MCDC_True = 1 }; + enum CondResult { + MCDC_Normal, + MCDC_Constant, + MCDC_Uncoverable, + MCDC_Unreachable + }; + /// Emulate SmallVector<CondState> with a pair of BitVector. /// /// True False DontCare (Impossible) @@ -442,22 +449,23 @@ struct MCDCRecord { using TVPairMap = llvm::DenseMap<unsigned, TVRowPair>; using CondIDMap = llvm::DenseMap<unsigned, unsigned>; using LineColPairMap = llvm::DenseMap<unsigned, LineColPair>; + using ResultVector = llvm::SmallVector<CondResult>; private: CounterMappingRegion Region; TestVectors TV; TVPairMap IndependencePairs; - BoolVector Folded; + ResultVector CondResults; CondIDMap PosToID; LineColPairMap CondLoc; public: MCDCRecord(const CounterMappingRegion &Region, TestVectors &&TV, - TVPairMap &&IndependencePairs, BoolVector &&Folded, + TVPairMap &&IndependencePairs, ResultVector &&CondResults, CondIDMap &&PosToID, LineColPairMap &&CondLoc) : Region(Region), TV(std::move(TV)), IndependencePairs(std::move(IndependencePairs)), - Folded(std::move(Folded)), PosToID(std::move(PosToID)), + CondResults(std::move(CondResults)), PosToID(std::move(PosToID)), CondLoc(std::move(CondLoc)){}; CounterMappingRegion getDecisionRegion() const { return Region; } @@ -465,7 +473,12 @@ struct MCDCRecord { return Region.getDecisionParams().NumConditions; } unsigned getNumTestVectors() const { return TV.size(); } - bool isCondFolded(unsigned Condition) const { return Folded[Condition]; } + bool isCondCoverable(unsigned Condition) const { + return getCondResult(Condition) == CondResult::MCDC_Normal; + } + CondResult getCondResult(unsigned Condition) const { + return CondResults[Condition]; + } /// Return the evaluation of a condition (indicated by Condition) in an /// executed test vector (indicated by TestVectorIndex), which will be True, @@ -505,20 +518,25 @@ struct MCDCRecord { return IndependencePairs[PosToID[Condition]]; } - float getPercentCovered() const { - unsigned Folded = 0; + /// Return if the decision is coverable and percent of covered conditions. + /// Only coverable conditions are counted as denominator. + std::pair<bool, float> getPercentCovered() const { + unsigned Excluded = 0; unsigned Covered = 0; for (unsigned C = 0; C < getNumConditions(); C++) { - if (isCondFolded(C)) - Folded++; + if (!isCondCoverable(C)) + Excluded++; else if (isConditionIndependencePairCovered(C)) Covered++; } - unsigned Total = getNumConditions() - Folded; + unsigned Total = getNumConditions() - Excluded; if (Total == 0) - return 0.0; - return (static_cast<double>(Covered) / static_cast<double>(Total)) * 100.0; + return {false, 0.0}; + return { + true, + (static_cast<double>(Covered) / static_cast<double>(Total)) * 100.0, + }; } std::string getConditionHeaderString(unsigned Condition) { @@ -553,7 +571,7 @@ struct MCDCRecord { // Add individual condition values to the string. OS << " " << TestVectorIndex + 1 << " { "; for (unsigned Condition = 0; Condition < NumConditions; Condition++) { - if (isCondFolded(Condition)) + if (getCondResult(Condition) == CondResult::MCDC_Constant) OS << "C"; else { switch (getTVCondition(TestVectorIndex, Condition)) { @@ -589,14 +607,25 @@ struct MCDCRecord { std::ostringstream OS; OS << " C" << Condition + 1 << "-Pair: "; - if (isCondFolded(Condition)) { + switch (getCondResult(Condition)) { + case CondResult::MCDC_Normal: + if (isConditionIndependencePairCovered(Condition)) { + TVRowPair rows = getConditionIndependencePair(Condition); + OS << "covered: (" << rows.first << ","; + OS << rows.second << ")\n"; + } else + OS << "not covered\n"; + break; + case CondResult::MCDC_Constant: OS << "constant folded\n"; - } else if (isConditionIndependencePairCovered(Condition)) { - TVRowPair rows = getConditionIndependencePair(Condition); - OS << "covered: (" << rows.first << ","; - OS << rows.second << ")\n"; - } else - OS << "not covered\n"; + break; + case CondResult::MCDC_Uncoverable: + OS << "uncoverable\n"; + break; + case CondResult::MCDC_Unreachable: + OS << "unreachable\n"; + break; + } return OS.str(); } diff --git a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp index e5df21a3bcf7d..760cd0c0ef520 100644 --- a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp +++ b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp @@ -364,11 +364,15 @@ class MCDCRecordProcessor : NextIDsBuilder, mcdc::TVIdxBuilder { unsigned NumConditions; /// Vector used to track whether a condition is constant folded. - MCDCRecord::BoolVector Folded; + MCDCRecord::ResultVector CondResults; /// Mapping of calculated MC/DC Independence Pairs for each condition. MCDCRecord::TVPairMap IndependencePairs; + /// All possible Test Vectors for the boolean expression derived from + /// binary decision diagran of the expression. + MCDCRecord::TestVectors TestVectors; + /// Storage for ExecVectors /// ExecVectors is the alias of its 0th element. std::array<MCDCRecord::TestVectors, 2> ExecVectorsByCond; @@ -391,7 +395,8 @@ class MCDCRecordProcessor : NextIDsBuilder, mcdc::TVIdxBuilder { : NextIDsBuilder(Branches), TVIdxBuilder(this->NextIDs), Bitmap(Bitmap), Region(Region), DecisionParams(Region.getDecisionParams()), Branches(Branches), NumConditions(DecisionParams.NumConditions), - Folded(NumConditions, false), IndependencePairs(NumConditions), + CondResults(NumConditions, MCDCRecord::CondResult::MCDC_Normal), + IndependencePairs(NumConditions), ExecVectors(ExecVectorsByCond[false]) {} private: @@ -415,6 +420,8 @@ class MCDCRecordProcessor : NextIDsBuilder, mcdc::TVIdxBuilder { assert(TVIdx < SavedNodes[ID].Width); assert(TVIdxs.insert(NextTVIdx).second && "Duplicate TVIdx"); + TestVectors.push_back({TV, MCDCCond}); + if (!Bitmap[DecisionParams.BitmapIdx * CHAR_BIT + TV.getIndex()]) continue; @@ -439,7 +446,6 @@ class MCDCRecordProcessor : NextIDsBuilder, mcdc::TVIdxBuilder { buildTestVector(TV, 0, 0); assert(TVIdxs.size() == unsigned(NumTestVectors) && "TVIdxs wasn't fulfilled"); - // Fill ExecVectors order by False items and True items. // ExecVectors is the alias of ExecVectorsByCond[false], so // Append ExecVectorsByCond[true] on it. @@ -471,48 +477,130 @@ class MCDCRecordProcessor : NextIDsBuilder, mcdc::TVIdxBuilder { } } + void findCoverablePairs(const MCDCRecord::CondIDMap &PosToID) { + llvm::SmallVector<unsigned> FoldedCondPos; + for (unsigned I = 0; I < CondResults.size(); ++I) { + if (CondResults[I] == MCDCRecord::MCDC_Constant || + CondResults[I] == MCDCRecord::MCDC_Unreachable) { + FoldedCondPos.push_back(I); + } + } + if (FoldedCondPos.empty()) { + return; + } + std::array<MCDCRecord::TestVectors, 2> PracticalTestVectorsByCond; + for (const auto &TVWithCond : TestVectors) { + const bool Practical = + llvm::all_of(FoldedCondPos, [&](const unsigned &Pos) { + const auto &[TV, Cond] = TVWithCond; + const auto ID = PosToID.at(Pos); + if (TV[ID] == MCDCRecord::MCDC_DontCare) { + return true; + } + if (CondResults[Pos] == MCDCRecord::MCDC_Constant) { + const auto ConstantValue = Branches[Pos]->Count.isZero() + ? MCDCRecord::MCDC_False + : MCDCRecord::MCDC_True; + if (TV[ID] == ConstantValue) { + return true; + } + } + return false; + }); + + if (Practical) { + PracticalTestVectorsByCond[TVWithCond.second].push_back(TVWithCond); + } + } + + // If a condition: + // - is uncoverable, all test vectors in exact one element of + // `PracticalTestVectorsByCond` show it is `DontCare`; + // - is unreachable, all test vectors in both elements of + // `PracticalTestVectorsByCond` show it is `DontCare`; + // + // Otherwise, the condition is coverable as long as it has not been marked + // as constant or unreachable before. + for (unsigned Pos = 0; Pos < Branches.size(); ++Pos) { + if (CondResults[Pos] != MCDCRecord::MCDC_Normal) { + continue; + } + const auto ID = PosToID.at(Pos); + unsigned InaccessibleCondCount = + llvm::count_if(PracticalTestVectorsByCond, + [=](const MCDCRecord::TestVectors &TestVectors) { + for (const auto &[TV, Cond] : TestVectors) { + if (TV[ID] != MCDCRecord::MCDC_DontCare) { + return false; + } + } + return true; + }); + switch (InaccessibleCondCount) { + case 1: + CondResults[Pos] = MCDCRecord::CondResult::MCDC_Uncoverable; + break; + case 2: + CondResults[Pos] = MCDCRecord::CondResult::MCDC_Unreachable; + break; + default: + break; + } + } + } + public: /// Process the MC/DC Record in order to produce a result for a boolean /// expression. This process includes tracking the conditions that comprise /// the decision region, calculating the list of all possible test vectors, /// marking the executed test vectors, and then finding an Independence Pair /// out of the executed test vectors for each condition in the boolean - /// expression. A condition is tracked to ensure that its ID can be mapped to - /// its ordinal position in the boolean expression. The condition's source - /// location is also tracked, as well as whether it is constant folded (in - /// which case it is excuded from the metric). + /// expression. A condition is tracked to ensure that its ID can be mapped + /// to its ordinal position in the boolean expression. The condition's + /// source location is also tracked, as well as whether it is constant + /// folded (in which case it is excuded from the metric). MCDCRecord processMCDCRecord() { unsigned I = 0; MCDCRecord::CondIDMap PosToID; MCDCRecord::LineColPairMap CondLoc; // Walk the Record's BranchRegions (representing Conditions) in order to: - // - Hash the condition based on its corresponding ID. This will be used to + // - Hash the condition based on its corresponding ID. This will be used + // to // calculate the test vectors. // - Keep a map of the condition's ordinal position (1, 2, 3, 4) to its // actual ID. This will be used to visualize the conditions in the // correct order. // - Keep track of the condition source location. This will be used to // visualize where the condition is. - // - Record whether the condition is constant folded so that we exclude it + // - Record whether the condition is folded so that we exclude it // from being measured. for (const auto *B : Branches) { const auto &BranchParams = B->getBranchParams(); PosToID[I] = BranchParams.ID; CondLoc[I] = B->startLoc(); - Folded[I++] = (B->Count.isZero() || B->FalseCount.isZero()); + if (B->Count.isZero() && B->FalseCount.isZero()) { + CondResults[I] = MCDCRecord::CondResult::MCDC_Unreachable; + } else if (B->Count.isZero() || B->FalseCount.isZero()) { + CondResults[I] = MCDCRecord::CondResult::MCDC_Constant; + } + ++I; } // Using Profile Bitmap from runtime, mark the executed test vectors. findExecutedTestVectors(); - // Compare executed test vectors against each other to find an independence - // pairs for each condition. This processing takes the most time. + // Compare executed test vectors against each other to find an + // independence pairs for each condition. This processing takes the most + // time. findIndependencePairs(); + // Identify all conditions making no difference on outcome of the decision. + findCoverablePairs(PosToID); + // Record Test vectors, executed vectors, and independence pairs. return MCDCRecord(Region, std::move(ExecVectors), - std::move(IndependencePairs), std::move(Folded), + std::move(IndependencePairs), std::move(CondResults), std::move(PosToID), std::move(CondLoc)); } }; diff --git a/llvm/test/tools/llvm-cov/Inputs/mcdc-const-folding.o b/llvm/test/tools/llvm-cov/Inputs/mcdc-const-folding.o index 4f54fa7b8a1a1492a9a455bbf6fa651844345c59..544716b291c9b8f01b95c8447ea947163a12bc26 100644 GIT binary patch delta 2040 zcmaJ>U2Icj7(U;rEj^`W>;4!Z*dk;b8LV4NJH>dWF@hmkaN}pcrDtomc63|E4r9PZ z%g-hWQJ1|pBwpB)Z~;jZA<TGg48*IMn21-JV9Xkc5^n^vsEJ(oeCM2vX~ZQv@ArPs z^Stl#e&3fazAxrK5a((|U)$|nVJB-M#aX#eWOlpcV73ZTWXvsiY6Ven3uOU8WTJqT z$1Zr0N)BXvB?$AW&5Pgel&>0kyP>Bqoi96A81%a{%<QyVidIP|W~l_>nlJm|WX-li zO{7VyeM|n`R}`4x!0PLeRx3!Ur~AiWn15pEt-g|LyX07DLSa_~jYnM3%4aG;_~MtX znWfH8dk;|2RcW&^5B=fV8Wfq$7|&yj=XKc72jkg=Dq&+la`CQM;VbOe(OUKOUmv#{ zdOP$UFDdJHZG4V|uCJHT<Kg-V=A~};PIiDUxGP<T&Mrx;mVPLy3SvqdKV7rE!)wD3 ztL<HeeXIT9&)<%1%LfZa8LK_>)KxoB;J-BVmZt^%XFR<BQ_l|c$K30z{<LpHq;l<a z*B)H?<g<6CCe}`;ZFv544>$BOiQX*fK=qGGtm#&(KPU-Txs3a;+(!q#`fblo*Z<-E zjne;df4MY@`ahQP`mcGvr;4&#YAI_rV&07N<z^bQ;yo++D$M;H;sX93TQOE?=J&1G zQf211tmxZl=5uD8uQt<+6_>1dXp^~r&x(P~W}dfV=@v83Ay$-?-dWsooOSqWld75- zf3=}uFr_9>_3h~&9d1acC(O_8%*oNAM8CQRpf3_^-Wv~w2Lg%4xDwo(2oD6CU+L?Q zM<U@+Qz#IKH!Dry#ze4=Kgv5bTfG&VTKN)x?Rc(!ah;jq+e+^??q5Y?jH4xM(Az-c zRxTP^<E0xraq>1jLw`23FRg0hG`0pkXP`A}&~MhDFJj*>HatV`GzNd&#-5PBEz7<m ztxcv_M+Ggng;*=yY>&_qGPRv&b)5%kxjjuwo!B{?rdStvhYeoWK~j&bC}Q2%KLQ@2 zdKA1K<VQm+PB(kNJH}Uay{L5zoL+D`z=?v>VQ`}0bn;c|I5?eY^2MB#>~WLYm1ZrZ z^pw+bm%)TPp8|dNN|skl=5lwMb%2?rd>`uetl++W;Jq!}NqdB4sMwn&UmO*pY0Af) z^eC#cE2JHVF5#rwn9h<^jL9rdeg(`yC*4qVc8!!indzkUftLiYUuTO{?3dYn^2fm& z0xzz!KS@c*>=9`R@J@g?pyP)|Ny7g?Jrl@3Tuz}u%r^}Fu$vYJ3*2>5X4M9^kc6ud z6APsZa8^voEMV$}pvzdgp#t0}CuIDp@R|!Jpc}Jv=>i-S(=v+zgST<MucL!9R!-_j zJ)0zL1pZX`8`V*P?=O$mvpMo-z@Gp=ql2Z4$?OJcW8j_!_oR-jcv5Ef$UhF=8Sut+ zydXuD`8!g<I}6?^ojIvEiJGVC*<<ogfI9{5gwC3cef4xb7}_*^pN8*gU{h`iO&0js zPs+?^V4*XZH)~=GXA1ld&dR7@=*~iyvvlVQ{4UPP?2xIOg6<;6jQg0DQB|9Uw@dJL z-nfq(yq&LSIr3+*KMQVF=l774(M8K;`R!l0%TND;%x;nYVwT_i#k>5>FUjbpUCQ#C qzkHXU^yT;1Fr{W9Y>KoQ8C8ojvRHk9w$Hwf2dB)+Y>Bkln!f?4_(j(M delta 1572 zcmZWnUrbw77{BM}lK!DkSZRS;C<$cMsbOQ%7MI>e8O*FP9dxXKbY)}X&k7cqjEk%n zm>_2Usr_D9_Fx};z+{PCh_NNH7k#P;M&2~h#3n9g@kQ{#a3_g==c2tg@jjgI_kG{* zeCPar=cYfPh4*RFMGNO2JkzF2>FrfdSSTf=j5E17%9U|dEgU?jx9d`4b%pPn`8yEQ z+u>VXqXD}dSIJdz9Q>(kBa9WP=F}?3tvSUHXOGnf!Edl1t@z(q4kitCI{nx|*YuOA zJxChtU@|z3ni0@uaO2=fL&Tz;`}J>*c(L#ELC6>#@VCJR4~@_2nwnm<e)reMJ+NC| zd*p~-Pa42q-h{gc${et6d=b$@<BXCwL(t?Pu9VS=>9Xm#F7#z*v_9Q>ACl(7y~m+g z(S-Wo!1NTHHJw&?Z1aJs2@h}pU;ctsxssK$p$+)0{kZzs7q86El+O2P&VN+ihnruj z2;+LMq8ZO|z|R$@a4R#hG$V3t`;X^;{Y1S-hxQ(;=6>A%i<u2MUHKF2+s)vwt^t3Q zr-aigP90UtQ4QYGV5ddpEc}aGx?QlV!O`kN4y*5Iu-~fIGaBr)sr96aQ#EQiuE9kO z4%VuiEe&p|Q|oCBHrv(u!Xe%$*e{U{(NSq30r=>I4cAXkbm6+EVExH1av9E_wBb6Y z;0Fr+M!}V>tp0Q><4iHU@owwy_2e!H-6B~4xtk}W;O$}KNXSj%kW=tt!~@<CW8`pM z1@DFb7)i*L@EUk~MUsYGuSnM$p#5Seo`Pd&rU5ofNftZd1bBKZupN#;7Gt3Al)^j* z+X`9?or6Gs3_KT*gE5OSFmT9mgQpj(O19XGyw{L-5i1z87z2X}FO0JeQOFJ1FxDf; z>q8#KEXKg4`{3!fKv^_KJRn3RVg~OJ57_|=yf+jhL6C<e;)mR29z27{yAnfdgewx7 z;vGg_1bM?TX4<gCL|^4e66C8QSp(skM0Z|>_7NXlcf#CAf@YlXcqEU%b#9cWTbPU{ zXaQSedC~&jai0Ew$#{Ymu{EA2QOI5A=`JSM6LcS2u{;j<PVhtr!i0!dl_w;+)d8E6 zKH>y<QY0=AUYF=@2eik1Oi*0J%gb>I+qo&8$(a&K7=$+@#NKK4&cd{aV&rLwz3UC0 zNx8vZS9nvRnKRHn<6~N8*ptdL5-pyA&6_@&X@ti&6ExchbF+D7n!wWnCPISlU`xm| z)8=?u#AGf(A7SgQJTq;cr~8=9C(s6YUh(V}&!pTENeu`I^sE!wMIVzQis*|hO3bkZ zo+()n$ypE<CFa)>&y*~Q=##u8F}H5>Ov!DL%!2T?#JpO5ht$GX%Wmp&z+R;l%quP& W>|H_oyI^j`hezgCcvLH|9Qy|}%NHB~ diff --git a/llvm/test/tools/llvm-cov/Inputs/mcdc-const.o b/llvm/test/tools/llvm-cov/Inputs/mcdc-const.o index 1145fcc6f7125452262d0745b9de0591c1e48cda..d5bbebf7ae21905929b8c7f3f2682bb0d8c2a8a4 100644 GIT binary patch delta 340 zcmXAjy-&h$5Qgu51)?T}VnIPdtdn3ATFOUdanMBm1XezZ9|0+f1{aCOff2mLkwgYI zt!UyT4(>X#nm9R_usK?EdG5*kUM@EZ-G$pBCiCXXGX`1!(D~HfZmikAI7mNczN&~E zdfh=nrp5hW0}gm_x<Q118$95Z!NJ+t#Dd_51$k-44s2r6un3Y2JXM&VT5Lp@yg&wb zmX$)g^}2DCjz)L(>buAJNTFJZmdhvqSThc*`+A`su}UeWYKfwnEXlf3%&7@ISyHw2 ze4&_1CF8NUEElz0EUD;f*!mX+-`o|$-r(GE>%e<b_$L04X|YX;a~pef>P+D^na&}0 zspe{8pT@2s`p6VTdKFgjiN-<`U#R9DV&*U1nti|ub+~yFM2T(M=!4BsmlG%}`p9ri TQA5<QVXbaVj#+#s#k2ei65C+x delta 236 zcmWlRF-yZ>7=-VAza)@gQ)5ygidZM3QwO2Jp*m^1>FVSnIN8-L;82K@dViqQ#Z9HS z8YiJH;#S=RCl~t@OqRO`9_}7CrpZ;J)49jZXNd;@u7A?^+p_O2&bFVrzkLyv292g$ zfCYmZ`~V@A14L-F0Ap)KD~PewT61h@MJ`s+EG_#QbD&?gRbGN8CLixvH!7p?r*rSf z5%XFmL%Mz@DRX~cM*Q&GvP-vKNSBj(CI{@z7y9DS3p#SlUXaNtCqbbzkCzLt67w)@ c>!0DbE^-(qIx~C=JJO^Z?NsYB&zVQ-|A<&TXaE2J diff --git a/llvm/test/tools/llvm-cov/Inputs/mcdc-macro.o b/llvm/test/tools/llvm-cov/Inputs/mcdc-macro.o index c0a9d6044553030ed1cc28bce2e764bcf3877869..c734cf01bddbee3e517d5cd6509de75a50991c6e 100644 GIT binary patch delta 258 zcmca$w7__R24liT&7I7Qs*@ixJ24tgR%20N4BYI^@|2O40SI^}o3fRJC}?DqloS+O z>FcLwmSmJB=_Tjq>gVK?K?vP~qWrAX<Ptrgq=LDzg=vbhS(-trVM?O0X{uS8v4v$) za*DaRnTe5!fkBEzqLG<ls<GzgwQRdN8Lv&w6sTvcnEVn*PMDl2SkE|P@=GAOU~;C= ze#RA(HHGULH%wkByr1#I<V+EJ#siaAiqtcnn5-$fpGkpl@+(n0#v7A0#p)R!OkOFr zpD|!^rg%GJ?&Oc+=1l+CCNr{%O?HsbU}9jOoF!q$*f4peggsF6OCb4SvZkau03F#? A&j0`b delta 178 zcmWlSy$-=p0EK({ml&E<OSQXDn-Gy26qCeaG<pPsL8rT6Y;s<pvx%54Ltnu=h==GP zah>7IS-x|M;cJk`aBn?nK;zq3D0a*?wEN2Zz;ZMdj_kepFZZwgtl>!M3dBt60y9d* zB@<6zP3e``F!Kerto#xMWh2FoRYTywtXb+Whm&B6GqXS-XBCv{Pqt2oI1k7A#U`wP Xc^K<gn~!jcKC3WQauTI#E{nQ9tH3vg diff --git a/llvm/test/tools/llvm-cov/mcdc-const.test b/llvm/test/tools/llvm-cov/mcdc-const.test index 5424625cf6a6b..d81de535c4c29 100644 --- a/llvm/test/tools/llvm-cov/mcdc-const.test +++ b/llvm/test/tools/llvm-cov/mcdc-const.test @@ -28,9 +28,9 @@ // CHECKGENERALCASE-NEXT: | C1-Pair: covered: (1,2) // CHECKGENERALCASE-NEXT: | C2-Pair: constant folded // CHECKGENERALCASE-NEXT: | C3-Pair: constant folded -// CHECKGENERALCASE-NEXT: | C4-Pair: not covered +// CHECKGENERALCASE-NEXT: | C4-Pair: unreachable // CHECKGENERALCASE-NEXT: | C5-Pair: constant folded -// CHECKGENERALCASE-NEXT: | MC/DC Coverage for Decision: 50.00% +// CHECKGENERALCASE-NEXT: | MC/DC Coverage for Decision: 100.00% // CHECKGENERALCASE-NEXT: | // CHECKGENERALCASE-NEXT: ------------------ @@ -40,13 +40,13 @@ // CHECKFULLCASE: | 1 { C, - = F } // CHECKFULLCASE: | C1-Pair: constant folded -// CHECKFULLCASE-NEXT: | C2-Pair: not covered -// CHECKFULLCASE: | MC/DC Coverage for Decision: 0.00% +// CHECKFULLCASE-NEXT: | C2-Pair: unreachable +// CHECKFULLCASE: | MC/DC Coverage for Decision: Folded // CHECKFULLCASE: | 1 { F, C = F } // CHECKFULLCASE-NEXT: | 2 { T, C = F } -// CHECKFULLCASE: | C1-Pair: not covered +// CHECKFULLCASE: | C1-Pair: uncoverable // CHECKFULLCASE-NEXT: | C2-Pair: constant folded -// CHECKFULLCASE: | MC/DC Coverage for Decision: 0.00% +// CHECKFULLCASE: | MC/DC Coverage for Decision: Folded // CHECKFULLCASE: | 1 { C, F = F } // CHECKFULLCASE-NEXT: | 2 { C, T = T } // CHECKFULLCASE: | C1-Pair: constant folded @@ -59,13 +59,13 @@ // CHECKFULLCASE: | MC/DC Coverage for Decision: 100.00% // CHECKFULLCASE: | 1 { C, - = T } // CHECKFULLCASE: | C1-Pair: constant folded -// CHECKFULLCASE-NEXT: | C2-Pair: not covered -// CHECKFULLCASE: | MC/DC Coverage for Decision: 0.00% +// CHECKFULLCASE-NEXT: | C2-Pair: unreachable +// CHECKFULLCASE: | MC/DC Coverage for Decision: Folded // CHECKFULLCASE: | 1 { F, C = T } // CHECKFULLCASE-NEXT: | 2 { T, C = T } -// CHECKFULLCASE: | C1-Pair: not covered +// CHECKFULLCASE: | C1-Pair: uncoverable // CHECKFULLCASE-NEXT: | C2-Pair: constant folded -// CHECKFULLCASE: | MC/DC Coverage for Decision: 0.00% +// CHECKFULLCASE: | MC/DC Coverage for Decision: Folded // CHECKFULLCASE: | 1 { C, F = F } // CHECKFULLCASE-NEXT: | 2 { C, T = T } // CHECKFULLCASE: | C1-Pair: constant folded @@ -78,15 +78,15 @@ // CHECKFULLCASE: | MC/DC Coverage for Decision: 100.00% // CHECKFULLCASE: | 1 { C, -, - = F } // CHECKFULLCASE: | C1-Pair: constant folded -// CHECKFULLCASE-NEXT: | C2-Pair: not covered -// CHECKFULLCASE-NEXT: | C3-Pair: not covered -// CHECKFULLCASE: | MC/DC Coverage for Decision: 0.00% +// CHECKFULLCASE-NEXT: | C2-Pair: unreachable +// CHECKFULLCASE-NEXT: | C3-Pair: unreachable +// CHECKFULLCASE: | MC/DC Coverage for Decision: Folded // CHECKFULLCASE: | 1 { F, C, - = F } // CHECKFULLCASE-NEXT: | 2 { T, C, - = F } -// CHECKFULLCASE: | C1-Pair: not covered +// CHECKFULLCASE: | C1-Pair: uncoverable // CHECKFULLCASE-NEXT: | C2-Pair: constant folded -// CHECKFULLCASE-NEXT: | C3-Pair: not covered -// CHECKFULLCASE: | MC/DC Coverage for Decision: 0.00% +// CHECKFULLCASE-NEXT: | C3-Pair: unreachable +// CHECKFULLCASE: | MC/DC Coverage for Decision: Folded // CHECKFULLCASE: | 1 { C, F, - = F } // CHECKFULLCASE-NEXT: | 2 { C, T, F = F } // CHECKFULLCASE-NEXT: | 3 { C, T, T = T } @@ -103,15 +103,15 @@ // CHECKFULLCASE: | MC/DC Coverage for Decision: 100.00% // CHECKFULLCASE: | 1 { C, -, - = T } // CHECKFULLCASE: | C1-Pair: constant folded -// CHECKFULLCASE-NEXT: | C2-Pair: not covered -// CHECKFULLCASE-NEXT: | C3-Pair: not covered -// CHECKFULLCASE: | MC/DC Coverage for Decision: 0.00% +// CHECKFULLCASE-NEXT: | C2-Pair: unreachable +// CHECKFULLCASE-NEXT: | C3-Pair: unreachable +// CHECKFULLCASE: | MC/DC Coverage for Decision: Folded // CHECKFULLCASE: | 1 { F, C, - = T } // CHECKFULLCASE-NEXT: | 2 { T, C, - = T } -// CHECKFULLCASE: | C1-Pair: not covered +// CHECKFULLCASE: | C1-Pair: uncoverable // CHECKFULLCASE-NEXT: | C2-Pair: constant folded -// CHECKFULLCASE-NEXT: | C3-Pair: not covered -// CHECKFULLCASE: | MC/DC Coverage for Decision: 0.00% +// CHECKFULLCASE-NEXT: | C3-Pair: unreachable +// CHECKFULLCASE: | MC/DC Coverage for Decision: Folded // CHECKFULLCASE: | 1 { C, F, T = T } // CHECKFULLCASE-NEXT: | 2 { C, T, - = T } // CHECKFULLCASE: | C1-Pair: constant folded @@ -127,16 +127,16 @@ // CHECKFULLCASE: | 1 { F, -, C = F } // CHECKFULLCASE-NEXT: | 2 { T, F, C = F } // CHECKFULLCASE-NEXT: | 3 { T, T, C = F } -// CHECKFULLCASE: | C1-Pair: not covered -// CHECKFULLCASE-NEXT: | C2-Pair: not covered +// CHECKFULLCASE: | C1-Pair: uncoverable +// CHECKFULLCASE-NEXT: | C2-Pair: uncoverable // CHECKFULLCASE-NEXT: | C3-Pair: constant folded -// CHECKFULLCASE: | MC/DC Coverage for Decision: 0.00% +// CHECKFULLCASE: | MC/DC Coverage for Decision: Folded // CHECKFULLCASE: | 1 { F, C, - = F } // CHECKFULLCASE-NEXT: | 2 { T, C, - = F } -// CHECKFULLCASE: | C1-Pair: not covered +// CHECKFULLCASE: | C1-Pair: uncoverable // CHECKFULLCASE-NEXT: | C2-Pair: constant folded -// CHECKFULLCASE-NEXT: | C3-Pair: not covered -// CHECKFULLCASE: | MC/DC Coverage for Decision: 0.00% +// CHECKFULLCASE-NEXT: | C3-Pair: unreachable +// CHECKFULLCASE: | MC/DC Coverage for Decision: Folded // CHECKFULLCASE: | 1 { F, -, C = F } // CHECKFULLCASE-NEXT: | 2 { T, F, C = F } // CHECKFULLCASE-NEXT: | 3 { T, T, C = T } @@ -153,16 +153,16 @@ // CHECKFULLCASE: | MC/DC Coverage for Decision: 100.00% // CHECKFULLCASE: | 1 { F, T, C = T } // CHECKFULLCASE-NEXT: | 2 { T, -, C = T } -// CHECKFULLCASE: | C1-Pair: not covered -// CHECKFULLCASE-NEXT: | C2-Pair: not covered +// CHECKFULLCASE: | C1-Pair: uncoverable +// CHECKFULLCASE-NEXT: | C2-Pair: uncoverable // CHECKFULLCASE-NEXT: | C3-Pair: constant folded -// CHECKFULLCASE: | MC/DC Coverage for Decision: 0.00% +// CHECKFULLCASE: | MC/DC Coverage for Decision: Folded // CHECKFULLCASE: | 1 { F, C, - = T } // CHECKFULLCASE-NEXT: | 2 { T, C, - = T } -// CHECKFULLCASE: | C1-Pair: not covered +// CHECKFULLCASE: | C1-Pair: uncoverable // CHECKFULLCASE-NEXT: | C2-Pair: constant folded -// CHECKFULLCASE-NEXT: | C3-Pair: not covered -// CHECKFULLCASE: | MC/DC Coverage for Decision: 0.00% +// CHECKFULLCASE-NEXT: | C3-Pair: unreachable +// CHECKFULLCASE: | MC/DC Coverage for Decision: Folded // CHECKFULLCASE: | 1 { F, T, C = T } // CHECKFULLCASE-NEXT: | 2 { T, -, C = T } // CHECKFULLCASE: | C1-Pair: not covered @@ -176,31 +176,31 @@ // CHECKFULLCASE-NEXT: | C3-Pair: not covered // CHECKFULLCASE: | MC/DC Coverage for Decision: 0.00% -// REPORT: _Z5case0b {{.*}} 1 1 0.00% -// REPORT-NEXT: _Z5case1b {{.*}} 1 1 0.00% +// REPORT: _Z5case0b {{.*}} 0 0 0.00% +// REPORT-NEXT: _Z5case1b {{.*}} 0 0 0.00% // REPORT-NEXT: _Z5case2b {{.*}} 1 0 100.00% // REPORT-NEXT: _Z5case3b {{.*}} 1 0 100.00% -// REPORT-NEXT: _Z5case4b {{.*}} 1 1 0.00% -// REPORT-NEXT: _Z5case5b {{.*}} 1 1 0.00% +// REPORT-NEXT: _Z5case4b {{.*}} 0 0 0.00% +// REPORT-NEXT: _Z5case5b {{.*}} 0 0 0.00% // REPORT-NEXT: _Z5case6b {{.*}} 1 0 100.00% // REPORT-NEXT: _Z5case7b {{.*}} 1 0 100.00% -// REPORT-NEXT: _Z5case8bb {{.*}} 2 2 0.00% -// REPORT-NEXT: _Z5case9bb {{.*}} 2 2 0.00% +// REPORT-NEXT: _Z5case8bb {{.*}} 0 0 0.00% +// REPORT-NEXT: _Z5case9bb {{.*}} 0 0 0.00% // REPORT-NEXT: _Z5caseabb {{.*}} 2 0 100.00% // REPORT-NEXT: _Z5casebbb {{.*}} 2 0 100.00% -// REPORT-NEXT: _Z5casecbb {{.*}} 2 2 0.00% -// REPORT-NEXT: _Z5casedbb {{.*}} 2 2 0.00% +// REPORT-NEXT: _Z5casecbb {{.*}} 0 0 0.00% +// REPORT-NEXT: _Z5casedbb {{.*}} 0 0 0.00% // REPORT-NEXT: _Z5caseebb {{.*}} 2 2 0.00% // REPORT-NEXT: _Z5casefbb {{.*}} 2 2 0.00% -// REPORT-NEXT: _Z5casegbb {{.*}} 2 2 0.00% -// REPORT-NEXT: _Z5casehbb {{.*}} 2 2 0.00% +// REPORT-NEXT: _Z5casegbb {{.*}} 0 0 0.00% +// REPORT-NEXT: _Z5casehbb {{.*}} 0 0 0.00% // REPORT-NEXT: _Z5caseibb {{.*}} 2 0 100.00% // REPORT-NEXT: _Z5casejbb {{.*}} 2 0 100.00% -// REPORT-NEXT: _Z5casekbb {{.*}} 2 2 0.00% -// REPORT-NEXT: _Z5caselbb {{.*}} 2 2 0.00% +// REPORT-NEXT: _Z5casekbb {{.*}} 0 0 0.00% +// REPORT-NEXT: _Z5caselbb {{.*}} 0 0 0.00% // REPORT-NEXT: _Z5casembb {{.*}} 2 2 0.00% // REPORT-NEXT: _Z5casenbb {{.*}} 2 2 0.00% -// REPORT: TOTAL {{.*}} 40 28 30.00% +// REPORT: TOTAL {{.*}} 20 8 60.00% Instructions for regenerating the test: diff --git a/llvm/tools/llvm-cov/CoverageReport.cpp b/llvm/tools/llvm-cov/CoverageReport.cpp index 49a35f2a943e6..5d3b218e3c34f 100644 --- a/llvm/tools/llvm-cov/CoverageReport.cpp +++ b/llvm/tools/llvm-cov/CoverageReport.cpp @@ -17,6 +17,7 @@ #include "llvm/Support/Path.h" #include "llvm/Support/ThreadPool.h" #include "llvm/Support/Threading.h" +#include "llvm/Support/raw_ostream.h" #include <numeric> using namespace llvm; @@ -362,7 +363,9 @@ void CoverageReport::render(const FunctionCoverageSummary &Function, (unsigned)(Function.MCDCCoverage.getNumPairs() - Function.MCDCCoverage.getCoveredPairs())); Options.colored_ostream( - OS, determineCoveragePercentageColor(Function.MCDCCoverage)) + OS, Function.MCDCCoverage.getNumPairs() == 0 + ? raw_ostream::GREEN + : determineCoveragePercentageColor(Function.MCDCCoverage)) << format("%*.2f", FunctionReportColumns[12] - 1, Function.MCDCCoverage.getPercentCovered()) << '%'; diff --git a/llvm/tools/llvm-cov/CoverageSummaryInfo.cpp b/llvm/tools/llvm-cov/CoverageSummaryInfo.cpp index 4f150020ee381..268912f589fe7 100644 --- a/llvm/tools/llvm-cov/CoverageSummaryInfo.cpp +++ b/llvm/tools/llvm-cov/CoverageSummaryInfo.cpp @@ -50,7 +50,7 @@ sumMCDCPairs(const ArrayRef<MCDCRecord> &Records) { for (const auto &Record : Records) { const auto NumConditions = Record.getNumConditions(); for (unsigned C = 0; C < NumConditions; C++) { - if (!Record.isCondFolded(C)) + if (Record.isCondCoverable(C)) ++NumPairs; if (Record.isConditionIndependencePairCovered(C)) ++CoveredPairs; diff --git a/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp b/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp index b93d8cb035306..f2484658685b7 100644 --- a/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp +++ b/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp @@ -1017,7 +1017,12 @@ void SourceCoverageViewHTML::renderMCDCView(raw_ostream &OS, MCDCView &MRV, for (unsigned i = 0; i < Record.getNumConditions(); i++) OS << Record.getConditionCoverageString(i); OS << " MC/DC Coverage for Expression: "; - OS << format("%0.2f", Record.getPercentCovered()) << "%\n"; + const auto [Coverable, Percent] = Record.getPercentCovered(); + if (Coverable) { + OS << format("%0.2f", Percent) << "%\n"; + } else { + OS << "Folded\n"; + } OS << EndPre; OS << EndExpansionDiv; } diff --git a/llvm/tools/llvm-cov/SourceCoverageViewText.cpp b/llvm/tools/llvm-cov/SourceCoverageViewText.cpp index 580da45ecfc0d..01142e71a1080 100644 --- a/llvm/tools/llvm-cov/SourceCoverageViewText.cpp +++ b/llvm/tools/llvm-cov/SourceCoverageViewText.cpp @@ -379,10 +379,16 @@ void SourceCoverageViewText::renderMCDCView(raw_ostream &OS, MCDCView &MRV, } renderLinePrefix(OS, ViewDepth); OS << " MC/DC Coverage for Decision: "; - colored_ostream(OS, raw_ostream::RED, - getOptions().Colors && Record.getPercentCovered() < 100.0, - /*Bold=*/false, /*BG=*/true) - << format("%0.2f", Record.getPercentCovered()) << "%"; + const auto [Coverable, Percent] = Record.getPercentCovered(); + if (Coverable) { + colored_ostream(OS, raw_ostream::RED, + getOptions().Colors && Percent < 100.0, + /*Bold=*/false, /*BG=*/true) + << format("%0.2f", Percent) << "%"; + } else { + OS << "Folded"; + } + OS << "\n"; renderLinePrefix(OS, ViewDepth); OS << "\n"; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits