[clang] [llvm] [coverage] skipping code coverage for 'if constexpr' and 'if consteval' [WIP] (PR #78033)

Hana Dusíková via cfe-commits cfe-commits at lists.llvm.org
Sat Jan 13 14:27:34 PST 2024


https://github.com/hanickadot updated https://github.com/llvm/llvm-project/pull/78033

>From efcab4def5ed135f84791d5569081cbe750f57d0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Hana=20Dusi=CC=81kova=CC=81?= <hanicka at hanicka.net>
Date: Sat, 13 Jan 2024 23:27:07 +0100
Subject: [PATCH] [coverage] skipping code coverage for 'if constexpr' and 'if
 consteval'

---
 clang/lib/CodeGen/CoverageMappingGen.cpp      | 164 +++++++++++++++---
 .../ProfileData/Coverage/CoverageMapping.cpp  |  65 ++++++-
 2 files changed, 200 insertions(+), 29 deletions(-)

diff --git a/clang/lib/CodeGen/CoverageMappingGen.cpp b/clang/lib/CodeGen/CoverageMappingGen.cpp
index b245abd16c3f4a..7e02c4fbac1efb 100644
--- a/clang/lib/CodeGen/CoverageMappingGen.cpp
+++ b/clang/lib/CodeGen/CoverageMappingGen.cpp
@@ -119,6 +119,10 @@ class SourceMappingRegion {
   /// as the line execution count if there are no other regions on the line.
   bool GapRegion;
 
+  /// Whetever this region is skipped ('if constexpr' or 'if consteval' untaken
+  /// branch)
+  bool SkippedRegion{false};
+
 public:
   SourceMappingRegion(Counter Count, std::optional<SourceLocation> LocStart,
                       std::optional<SourceLocation> LocEnd,
@@ -174,6 +178,10 @@ class SourceMappingRegion {
 
   void setGap(bool Gap) { GapRegion = Gap; }
 
+  bool isSkipped() const { return SkippedRegion; }
+
+  void setSkipped(bool Skipped) { SkippedRegion = Skipped; }
+
   bool isBranch() const { return FalseCount.has_value(); }
 
   bool isMCDCDecision() const { return MCDCParams.NumConditions != 0; }
@@ -468,6 +476,10 @@ class CoverageMappingBuilder {
         MappingRegions.push_back(CounterMappingRegion::makeGapRegion(
             Region.getCounter(), *CovFileID, SR.LineStart, SR.ColumnStart,
             SR.LineEnd, SR.ColumnEnd));
+      } else if (Region.isSkipped()) {
+        MappingRegions.push_back(CounterMappingRegion::makeSkipped(
+            *CovFileID, SR.LineStart, SR.ColumnStart, SR.LineEnd,
+            SR.ColumnEnd));
       } else if (Region.isBranch()) {
         MappingRegions.push_back(CounterMappingRegion::makeBranchRegion(
             Region.getCounter(), Region.getFalseCounter(),
@@ -1252,6 +1264,29 @@ struct CounterCoverageMappingBuilder
     popRegions(Index);
   }
 
+  /// Emit a skip region between \p StartLoc and \p EndLoc with the given count.
+  void markSkipArea(SourceLocation StartLoc, SourceLocation EndLoc) {
+    if (StartLoc == EndLoc)
+      return;
+    assert(SpellingRegion(SM, StartLoc, EndLoc).isInSourceOrder());
+    handleFileExit(StartLoc);
+    size_t Index = pushRegion({}, StartLoc, EndLoc);
+    getRegion().setSkipped(true);
+    handleFileExit(EndLoc);
+    popRegions(Index);
+  }
+
+  void findGapAreaBetweenAndMarkSkipArea(SourceLocation AfterLoc,
+                                         SourceLocation BeforeLoc) {
+    // const std::optional<SourceRange> Gap = findGapAreaBetween(AfterLoc,
+    // BeforeLoc);
+    //
+    // if (Gap) {
+    //   markSkipArea(Gap->getBegin(), Gap->getEnd());
+    // }
+    markSkipArea(AfterLoc, BeforeLoc);
+  }
+
   /// Keep counts of breaks and continues inside loops.
   struct BreakContinue {
     Counter BreakCount;
@@ -1701,43 +1736,118 @@ struct CounterCoverageMappingBuilder
     Visit(S->getSubStmt());
   }
 
+  void CoverIfConsteval(const IfStmt *S) {
+    assert(S->isConsteval());
+
+    const auto *Then = S->getThen();
+    const auto *Else = S->getElse();
+
+    // I'm using 'propagateCounts' later as new region is better and allows me
+    // to properly calculate line coverage in llvm-cov utility
+    const Counter ParentCount = getRegion().getCounter();
+
+    extendRegion(S);
+
+    if (S->isNegatedConsteval()) {
+      // ignore 'if consteval'
+      findGapAreaBetweenAndMarkSkipArea(S->getIfLoc(), getStart(Then));
+      propagateCounts(ParentCount, Then);
+
+      if (Else) {
+        // ignore 'else <else>'
+        findGapAreaBetweenAndMarkSkipArea(getEnd(Then), getEnd(Else));
+      }
+    } else {
+      assert(S->isNonNegatedConsteval());
+      // ignore 'if consteval <then> [else]'
+      findGapAreaBetweenAndMarkSkipArea(S->getIfLoc(),
+                                        Else ? getStart(Else) : getEnd(Then));
+
+      if (Else)
+        propagateCounts(ParentCount, Else);
+    }
+  }
+
+  void CoverIfConstexpr(const IfStmt *S) {
+    assert(S->isConstexpr());
+
+    // evaluate constant condition...
+    const auto *E = dyn_cast<ConstantExpr>(S->getCond());
+    assert(E != nullptr);
+    const bool isTrue = E->getResultAsAPSInt().getExtValue();
+
+    extendRegion(S);
+
+    const auto *Init = S->getInit();
+    const auto *Then = S->getThen();
+    const auto *Else = S->getElse();
+
+    // I'm using 'propagateCounts' later as new region is better and allows me
+    // to properly calculate line coverage in llvm-cov utility
+    const Counter ParentCount = getRegion().getCounter();
+
+    // ignore 'if constexpr ('
+    SourceLocation startOfSkipped = S->getIfLoc();
+
+    if (Init) {
+      // don't mark initialisation as ignored
+      findGapAreaBetweenAndMarkSkipArea(startOfSkipped, getStart(Init));
+      propagateCounts(ParentCount, Init);
+      // ignore after initialisation: '; <condition>)'...
+      startOfSkipped = getEnd(Init);
+    }
+
+    if (isTrue) {
+      // ignore '<condition>)'
+      findGapAreaBetweenAndMarkSkipArea(startOfSkipped, getStart(Then));
+      propagateCounts(ParentCount, Then);
+
+      if (Else)
+        // ignore 'else <else>'
+        findGapAreaBetweenAndMarkSkipArea(getEnd(Then), getEnd(Else));
+    } else {
+      // ignore '<condition>) <then> [else]'
+      findGapAreaBetweenAndMarkSkipArea(startOfSkipped,
+                                        Else ? getStart(Else) : getEnd(Then));
+
+      if (Else) {
+        propagateCounts(ParentCount, Else);
+      }
+    }
+  }
+
   void VisitIfStmt(const IfStmt *S) {
+    // "if constexpr" and "if consteval" are not normal conditional statements,
+    // they should behave more like a preprocessor conditions
+    if (S->isConsteval())
+      return CoverIfConsteval(S);
+    else if (S->isConstexpr())
+      return CoverIfConstexpr(S);
+
     extendRegion(S);
     if (S->getInit())
       Visit(S->getInit());
 
     // Extend into the condition before we propagate through it below - this is
     // needed to handle macros that generate the "if" but not the condition.
-    if (!S->isConsteval())
-      extendRegion(S->getCond());
+    extendRegion(S->getCond());
 
     Counter ParentCount = getRegion().getCounter();
+    Counter ThenCount = getRegionCounter(S);
 
-    // If this is "if !consteval" the then-branch will never be taken, we don't
-    // need to change counter
-    Counter ThenCount =
-        S->isNegatedConsteval() ? ParentCount : getRegionCounter(S);
+    // Emitting a counter for the condition makes it easier to interpret the
+    // counter for the body when looking at the coverage.
+    propagateCounts(ParentCount, S->getCond());
 
-    if (!S->isConsteval()) {
-      // Emitting a counter for the condition makes it easier to interpret the
-      // counter for the body when looking at the coverage.
-      propagateCounts(ParentCount, S->getCond());
-
-      // The 'then' count applies to the area immediately after the condition.
-      std::optional<SourceRange> Gap =
-          findGapAreaBetween(S->getRParenLoc(), getStart(S->getThen()));
-      if (Gap)
-        fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(), ThenCount);
-    }
+    // The 'then' count applies to the area immediately after the condition.
+    std::optional<SourceRange> Gap =
+        findGapAreaBetween(S->getRParenLoc(), getStart(S->getThen()));
+    if (Gap)
+      fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(), ThenCount);
 
     extendRegion(S->getThen());
     Counter OutCount = propagateCounts(ThenCount, S->getThen());
-
-    // If this is "if consteval" the else-branch will never be taken, we don't
-    // need to change counter
-    Counter ElseCount = S->isNonNegatedConsteval()
-                            ? ParentCount
-                            : subtractCounters(ParentCount, ThenCount);
+    Counter ElseCount = subtractCounters(ParentCount, ThenCount);
 
     if (const Stmt *Else = S->getElse()) {
       bool ThenHasTerminateStmt = HasTerminateStmt;
@@ -1760,11 +1870,9 @@ struct CounterCoverageMappingBuilder
       GapRegionCounter = OutCount;
     }
 
-    if (!S->isConsteval()) {
-      // Create Branch Region around condition.
-      createBranchRegion(S->getCond(), ThenCount,
-                         subtractCounters(ParentCount, ThenCount));
-    }
+    // Create Branch Region around condition.
+    createBranchRegion(S->getCond(), ThenCount,
+                       subtractCounters(ParentCount, ThenCount));
   }
 
   void VisitCXXTryStmt(const CXXTryStmt *S) {
diff --git a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
index eece6a2cc71797..55d968e3e8cee7 100644
--- a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
+++ b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
@@ -1319,8 +1319,71 @@ LineCoverageStats::LineCoverageStats(
       !StartOfSkippedRegion &&
       ((WrappedSegment && WrappedSegment->HasCount) || (MinRegionCount > 0));
 
-  if (!Mapped)
+  Mapped |= std::any_of(
+      LineSegments.begin(), LineSegments.end(),
+      [](const auto *Seq) { return Seq->IsRegionEntry && Seq->HasCount; });
+
+  Mapped |= WrappedSegment && WrappedSegment->IsRegionEntry &&
+            WrappedSegment->HasCount;
+
+  /*
+  printf("line %u: ", Line);
+  printf("segments: %u,", (unsigned)LineSegments.size());
+
+  auto print_seq = [](const auto * seq) {
+    printf("(line: %u, column: %u ", seq->Line, seq->Col);
+    if (seq->HasCount) {
+      printf(" count: %llu", seq->Count);
+    } else {
+      printf(" nocount");
+    }
+
+    if (seq->IsRegionEntry) {
+      printf(" entry");
+    }
+
+    if (seq->IsGapRegion) {
+      printf(" gap");
+    }
+
+    printf(")");
+  };
+
+  for (const auto * seq: LineSegments) {
+    printf(" ");
+    print_seq(seq);
+  }
+
+  if (WrappedSegment) {
+    printf(" wrapped seq: ");
+    print_seq(WrappedSegment);
+  }
+
+
+
+  bool should_map = false;
+  for (const auto * seq: LineSegments) {
+    if (seq->IsRegionEntry && seq->HasCount) {
+      should_map = true;
+    }
+  }
+
+  if (should_map) {
+    Mapped = true;
+  }
+
+  if (WrappedSegment && WrappedSegment->IsRegionEntry &&
+  WrappedSegment->HasCount) { if (!Mapped) { Mapped = true; printf("
+  [ARGH:%u]",Line);
+    }
+  }*/
+
+  if (!Mapped) {
+    // printf(" [skip]\n");
     return;
+  }
+
+  // printf(" mapped\n");
 
   // Pick the max count from the non-gap, region entry segments and the
   // wrapped count.



More information about the cfe-commits mailing list