[clang] 8ecbb04 - Reland "[Coverage][llvm-cov] Enable MC/DC Support in LLVM Source-based Code Coverage (2/3)"

Alan Phipps via cfe-commits cfe-commits at lists.llvm.org
Wed Dec 13 13:10:19 PST 2023


Author: Alan Phipps
Date: 2023-12-13T15:10:05-06:00
New Revision: 8ecbb0404d740d1ab173554e47cef39cd5e3ef8c

URL: https://github.com/llvm/llvm-project/commit/8ecbb0404d740d1ab173554e47cef39cd5e3ef8c
DIFF: https://github.com/llvm/llvm-project/commit/8ecbb0404d740d1ab173554e47cef39cd5e3ef8c.diff

LOG: Reland "[Coverage][llvm-cov] Enable MC/DC Support in LLVM Source-based Code Coverage (2/3)"

Part 2 of 3. This includes the Visualization and Evaluation components.

Differential Revision: https://reviews.llvm.org/D138847

Added: 
    llvm/test/tools/llvm-cov/Inputs/mcdc-const-folding.cpp
    llvm/test/tools/llvm-cov/Inputs/mcdc-const-folding.o
    llvm/test/tools/llvm-cov/Inputs/mcdc-const-folding.proftext
    llvm/test/tools/llvm-cov/Inputs/mcdc-const.cpp
    llvm/test/tools/llvm-cov/Inputs/mcdc-const.o
    llvm/test/tools/llvm-cov/Inputs/mcdc-const.proftext
    llvm/test/tools/llvm-cov/Inputs/mcdc-general-none.proftext
    llvm/test/tools/llvm-cov/Inputs/mcdc-general.cpp
    llvm/test/tools/llvm-cov/Inputs/mcdc-general.o
    llvm/test/tools/llvm-cov/Inputs/mcdc-general.proftext
    llvm/test/tools/llvm-cov/mcdc-const.test
    llvm/test/tools/llvm-cov/mcdc-export-json.test
    llvm/test/tools/llvm-cov/mcdc-general-none.test
    llvm/test/tools/llvm-cov/mcdc-general.test

Modified: 
    clang/lib/CodeGen/CoverageMappingGen.cpp
    llvm/docs/CommandGuide/llvm-cov.rst
    llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h
    llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
    llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp
    llvm/lib/ProfileData/Coverage/CoverageMappingWriter.cpp
    llvm/test/tools/llvm-cov/Inputs/binary-formats.canonical.json
    llvm/tools/llvm-cov/CodeCoverage.cpp
    llvm/tools/llvm-cov/CoverageExporterJson.cpp
    llvm/tools/llvm-cov/CoverageReport.cpp
    llvm/tools/llvm-cov/CoverageSummaryInfo.cpp
    llvm/tools/llvm-cov/CoverageSummaryInfo.h
    llvm/tools/llvm-cov/CoverageViewOptions.h
    llvm/tools/llvm-cov/SourceCoverageView.cpp
    llvm/tools/llvm-cov/SourceCoverageView.h
    llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp
    llvm/tools/llvm-cov/SourceCoverageViewHTML.h
    llvm/tools/llvm-cov/SourceCoverageViewText.cpp
    llvm/tools/llvm-cov/SourceCoverageViewText.h
    llvm/unittests/ProfileData/CoverageMappingTest.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/CodeGen/CoverageMappingGen.cpp b/clang/lib/CodeGen/CoverageMappingGen.cpp
index b16358ee117ae2..56411e2240e505 100644
--- a/clang/lib/CodeGen/CoverageMappingGen.cpp
+++ b/clang/lib/CodeGen/CoverageMappingGen.cpp
@@ -1623,8 +1623,12 @@ static void dump(llvm::raw_ostream &OS, StringRef FunctionName,
       OS << "Gap,";
       break;
     case CounterMappingRegion::BranchRegion:
+    case CounterMappingRegion::MCDCBranchRegion:
       OS << "Branch,";
       break;
+    case CounterMappingRegion::MCDCDecisionRegion:
+      OS << "Decision,";
+      break;
     }
 
     OS << "File " << R.FileID << ", " << R.LineStart << ":" << R.ColumnStart

diff  --git a/llvm/docs/CommandGuide/llvm-cov.rst b/llvm/docs/CommandGuide/llvm-cov.rst
index be0c6b3b7e0463..968f3c452f5584 100644
--- a/llvm/docs/CommandGuide/llvm-cov.rst
+++ b/llvm/docs/CommandGuide/llvm-cov.rst
@@ -222,6 +222,11 @@ OPTIONS
  Show coverage for branch conditions in terms of either count or percentage.
  The supported views are: "count", "percent".
 
+.. option:: -show-mcdc
+
+ Show modified condition/decision coverage (MC/DC) for each applicable boolean
+ expression.
+
 .. option:: -show-line-counts
 
  Show the execution counts for each line. Defaults to true, unless another
@@ -426,6 +431,10 @@ OPTIONS
 
  Show statistics for all branch conditions. Defaults to true.
 
+.. option:: -show-mcdc-summary
+
+ Show MC/DC statistics. Defaults to false.
+
 .. option:: -show-functions
 
  Show coverage summaries for each function. Defaults to false.

diff  --git a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h b/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h
index e9fbc4631dd5fb..493689f6a61e7e 100644
--- a/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h
+++ b/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h
@@ -15,6 +15,7 @@
 #define LLVM_PROFILEDATA_COVERAGE_COVERAGEMAPPING_H
 
 #include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/BitVector.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/DenseSet.h"
 #include "llvm/ADT/Hashing.h"
@@ -33,6 +34,7 @@
 #include <cstdint>
 #include <iterator>
 #include <memory>
+#include <sstream>
 #include <string>
 #include <system_error>
 #include <tuple>
@@ -237,7 +239,27 @@ struct CounterMappingRegion {
     /// A BranchRegion represents leaf-level boolean expressions and is
     /// associated with two counters, each representing the number of times the
     /// expression evaluates to true or false.
-    BranchRegion
+    BranchRegion,
+
+    /// A DecisionRegion represents a top-level boolean expression and is
+    /// associated with a variable length bitmap index and condition number.
+    MCDCDecisionRegion,
+
+    /// A Branch Region can be extended to include IDs to facilitate MC/DC.
+    MCDCBranchRegion
+  };
+
+  using MCDCConditionID = unsigned int;
+  struct MCDCParameters {
+    /// Byte Index of Bitmap Coverage Object for a Decision Region.
+    unsigned BitmapIdx = 0;
+
+    /// Number of Conditions used for a Decision Region.
+    unsigned NumConditions = 0;
+
+    /// IDs used to represent a branch region and other branch regions
+    /// evaluated based on True and False branches.
+    MCDCConditionID ID = 0, TrueID = 0, FalseID = 0;
   };
 
   /// Primary Counter that is also used for Branch Regions (TrueCount).
@@ -246,8 +268,13 @@ struct CounterMappingRegion {
   /// Secondary Counter used for Branch Regions (FalseCount).
   Counter FalseCount;
 
-  unsigned FileID, ExpandedFileID;
+  /// Parameters used for Modified Condition/Decision Coverage
+  MCDCParameters MCDCParams;
+
+  unsigned FileID = 0;
+  unsigned ExpandedFileID = 0;
   unsigned LineStart, ColumnStart, LineEnd, ColumnEnd;
+
   RegionKind Kind;
 
   CounterMappingRegion(Counter Count, unsigned FileID, unsigned ExpandedFileID,
@@ -257,15 +284,24 @@ struct CounterMappingRegion {
         LineStart(LineStart), ColumnStart(ColumnStart), LineEnd(LineEnd),
         ColumnEnd(ColumnEnd), Kind(Kind) {}
 
-  CounterMappingRegion(Counter Count, Counter FalseCount, unsigned FileID,
+  CounterMappingRegion(Counter Count, Counter FalseCount,
+                       MCDCParameters MCDCParams, unsigned FileID,
                        unsigned ExpandedFileID, unsigned LineStart,
                        unsigned ColumnStart, unsigned LineEnd,
                        unsigned ColumnEnd, RegionKind Kind)
-      : Count(Count), FalseCount(FalseCount), FileID(FileID),
-        ExpandedFileID(ExpandedFileID), LineStart(LineStart),
+      : Count(Count), FalseCount(FalseCount), MCDCParams(MCDCParams),
+        FileID(FileID), ExpandedFileID(ExpandedFileID), LineStart(LineStart),
         ColumnStart(ColumnStart), LineEnd(LineEnd), ColumnEnd(ColumnEnd),
         Kind(Kind) {}
 
+  CounterMappingRegion(MCDCParameters MCDCParams, unsigned FileID,
+                       unsigned ExpandedFileID, unsigned LineStart,
+                       unsigned ColumnStart, unsigned LineEnd,
+                       unsigned ColumnEnd, RegionKind Kind)
+      : MCDCParams(MCDCParams), ExpandedFileID(ExpandedFileID),
+        LineStart(LineStart), ColumnStart(ColumnStart), LineEnd(LineEnd),
+        ColumnEnd(ColumnEnd), Kind(Kind) {}
+
   static CounterMappingRegion
   makeRegion(Counter Count, unsigned FileID, unsigned LineStart,
              unsigned ColumnStart, unsigned LineEnd, unsigned ColumnEnd) {
@@ -299,8 +335,27 @@ struct CounterMappingRegion {
   makeBranchRegion(Counter Count, Counter FalseCount, unsigned FileID,
                    unsigned LineStart, unsigned ColumnStart, unsigned LineEnd,
                    unsigned ColumnEnd) {
-    return CounterMappingRegion(Count, FalseCount, FileID, 0, LineStart,
-                                ColumnStart, LineEnd, ColumnEnd, BranchRegion);
+    return CounterMappingRegion(Count, FalseCount, MCDCParameters(), FileID, 0,
+                                LineStart, ColumnStart, LineEnd, ColumnEnd,
+                                BranchRegion);
+  }
+
+  static CounterMappingRegion
+  makeBranchRegion(Counter Count, Counter FalseCount, MCDCParameters MCDCParams,
+                   unsigned FileID, unsigned LineStart, unsigned ColumnStart,
+                   unsigned LineEnd, unsigned ColumnEnd) {
+    return CounterMappingRegion(Count, FalseCount, MCDCParams, FileID, 0,
+                                LineStart, ColumnStart, LineEnd, ColumnEnd,
+                                MCDCParams.ID == 0 ? BranchRegion
+                                                   : MCDCBranchRegion);
+  }
+
+  static CounterMappingRegion
+  makeDecisionRegion(MCDCParameters MCDCParams, unsigned FileID,
+                     unsigned LineStart, unsigned ColumnStart, unsigned LineEnd,
+                     unsigned ColumnEnd) {
+    return CounterMappingRegion(MCDCParams, FileID, 0, LineStart, ColumnStart,
+                                LineEnd, ColumnEnd, MCDCDecisionRegion);
   }
 
   inline LineColPair startLoc() const {
@@ -326,11 +381,189 @@ struct CountedRegion : public CounterMappingRegion {
         FalseExecutionCount(FalseExecutionCount), Folded(false) {}
 };
 
+/// MCDC Record grouping all information together.
+struct MCDCRecord {
+  /// CondState represents the evaluation of a condition in an executed test
+  /// vector, which can be True or False. A DontCare is used to mask an
+  /// unevaluatable condition resulting from short-circuit behavior of logical
+  /// operators in languages like C/C++. When comparing the evaluation of a
+  /// condition across executed test vectors, comparisons against a DontCare
+  /// are effectively ignored.
+  enum CondState { MCDC_DontCare = -1, MCDC_False = 0, MCDC_True = 1 };
+
+  using TestVector = llvm::SmallVector<CondState>;
+  using TestVectors = llvm::SmallVector<TestVector>;
+  using BoolVector = llvm::SmallVector<bool>;
+  using TVRowPair = std::pair<unsigned, unsigned>;
+  using TVPairMap = llvm::DenseMap<unsigned, TVRowPair>;
+  using CondIDMap = llvm::DenseMap<unsigned, unsigned>;
+  using LineColPairMap = llvm::DenseMap<unsigned, LineColPair>;
+
+private:
+  CounterMappingRegion Region;
+  TestVectors TV;
+  TVPairMap IndependencePairs;
+  BoolVector Folded;
+  CondIDMap PosToID;
+  LineColPairMap CondLoc;
+
+public:
+  MCDCRecord(CounterMappingRegion Region, TestVectors TV,
+             TVPairMap IndependencePairs, BoolVector Folded, CondIDMap PosToID,
+             LineColPairMap CondLoc)
+      : Region(Region), TV(TV), IndependencePairs(IndependencePairs),
+        Folded(Folded), PosToID(PosToID), CondLoc(CondLoc){};
+
+  CounterMappingRegion getDecisionRegion() const { return Region; }
+  unsigned getNumConditions() const {
+    assert(Region.MCDCParams.NumConditions != 0 &&
+           "In MC/DC, NumConditions should never be zero!");
+    return Region.MCDCParams.NumConditions;
+  }
+  unsigned getNumTestVectors() const { return TV.size(); }
+  bool isCondFolded(unsigned Condition) const { return Folded[Condition]; }
+
+  /// Return the evaluation of a condition (indicated by Condition) in an
+  /// executed test vector (indicated by TestVectorIndex), which will be True,
+  /// False, or DontCare if the condition is unevaluatable. Because condition
+  /// IDs are not associated based on their position in the expression,
+  /// accessing conditions in the TestVectors requires a translation from a
+  /// ordinal position to actual condition ID. This is done via PosToID[].
+  CondState getTVCondition(unsigned TestVectorIndex, unsigned Condition) {
+    return TV[TestVectorIndex][PosToID[Condition]];
+  }
+
+  /// Return the Result evaluation for an executed test vector.
+  /// See MCDCRecordProcessor::RecordTestVector().
+  CondState getTVResult(unsigned TestVectorIndex) {
+    return TV[TestVectorIndex][getNumConditions()];
+  }
+
+  /// Determine whether a given condition (indicated by Condition) is covered
+  /// by an Independence Pair. Because condition IDs are not associated based
+  /// on their position in the expression, accessing conditions in the
+  /// TestVectors requires a translation from a ordinal position to actual
+  /// condition ID. This is done via PosToID[].
+  bool isConditionIndependencePairCovered(unsigned Condition) const {
+    auto It = PosToID.find(Condition);
+    if (It != PosToID.end())
+      return (IndependencePairs.find(It->second) != IndependencePairs.end());
+    llvm_unreachable("Condition ID without an Ordinal mapping");
+  }
+
+  /// Return the Independence Pair that covers the given condition. Because
+  /// condition IDs are not associated based on their position in the
+  /// expression, accessing conditions in the TestVectors requires a
+  /// translation from a ordinal position to actual condition ID. This is done
+  /// via PosToID[].
+  TVRowPair getConditionIndependencePair(unsigned Condition) {
+    assert(isConditionIndependencePairCovered(Condition));
+    return IndependencePairs[PosToID[Condition]];
+  }
+
+  float getPercentCovered() const {
+    unsigned Folded = 0;
+    unsigned Covered = 0;
+    for (unsigned C = 0; C < getNumConditions(); C++) {
+      if (isCondFolded(C))
+        Folded++;
+      else if (isConditionIndependencePairCovered(C))
+        Covered++;
+    }
+
+    unsigned Total = getNumConditions() - Folded;
+    if (Total == 0)
+      return 0.0;
+    return (static_cast<double>(Covered) / static_cast<double>(Total)) * 100.0;
+  }
+
+  std::string getConditionHeaderString(unsigned Condition) {
+    std::ostringstream OS;
+    OS << "Condition C" << Condition + 1 << " --> (";
+    OS << CondLoc[Condition].first << ":" << CondLoc[Condition].second;
+    OS << ")\n";
+    return OS.str();
+  }
+
+  std::string getTestVectorHeaderString() const {
+    std::ostringstream OS;
+    if (getNumTestVectors() == 0) {
+      OS << "None.\n";
+      return OS.str();
+    }
+    const auto NumConditions = getNumConditions();
+    for (unsigned I = 0; I < NumConditions; I++) {
+      OS << "C" << I + 1;
+      if (I != NumConditions - 1)
+        OS << ", ";
+    }
+    OS << "    Result\n";
+    return OS.str();
+  }
+
+  std::string getTestVectorString(unsigned TestVectorIndex) {
+    assert(TestVectorIndex < getNumTestVectors() &&
+           "TestVector index out of bounds!");
+    std::ostringstream OS;
+    const auto NumConditions = getNumConditions();
+    // Add individual condition values to the string.
+    OS << "  " << TestVectorIndex + 1 << " { ";
+    for (unsigned Condition = 0; Condition < NumConditions; Condition++) {
+      if (isCondFolded(Condition))
+        OS << "C";
+      else {
+        switch (getTVCondition(TestVectorIndex, Condition)) {
+        case MCDCRecord::MCDC_DontCare:
+          OS << "-";
+          break;
+        case MCDCRecord::MCDC_True:
+          OS << "T";
+          break;
+        case MCDCRecord::MCDC_False:
+          OS << "F";
+          break;
+        }
+      }
+      if (Condition != NumConditions - 1)
+        OS << ",  ";
+    }
+
+    // Add result value to the string.
+    OS << "  = ";
+    if (getTVResult(TestVectorIndex) == MCDC_True)
+      OS << "T";
+    else
+      OS << "F";
+    OS << "      }\n";
+
+    return OS.str();
+  }
+
+  std::string getConditionCoverageString(unsigned Condition) {
+    assert(Condition < getNumConditions() &&
+           "Condition index is out of bounds!");
+    std::ostringstream OS;
+
+    OS << "  C" << Condition + 1 << "-Pair: ";
+    if (isCondFolded(Condition)) {
+      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";
+
+    return OS.str();
+  }
+};
+
 /// A Counter mapping context is used to connect the counters, expressions
 /// and the obtained counter values.
 class CounterMappingContext {
   ArrayRef<CounterExpression> Expressions;
   ArrayRef<uint64_t> CounterValues;
+  ArrayRef<uint8_t> BitmapBytes;
 
 public:
   CounterMappingContext(ArrayRef<CounterExpression> Expressions,
@@ -338,6 +571,7 @@ class CounterMappingContext {
       : Expressions(Expressions), CounterValues(CounterValues) {}
 
   void setCounts(ArrayRef<uint64_t> Counts) { CounterValues = Counts; }
+  void setBitmapBytes(ArrayRef<uint8_t> Bytes) { BitmapBytes = Bytes; }
 
   void dump(const Counter &C, raw_ostream &OS) const;
   void dump(const Counter &C) const { dump(C, dbgs()); }
@@ -346,6 +580,17 @@ class CounterMappingContext {
   /// counter was executed.
   Expected<int64_t> evaluate(const Counter &C) const;
 
+  /// Return the number of times that a region of code associated with this
+  /// counter was executed.
+  Expected<BitVector>
+  evaluateBitmap(const CounterMappingRegion *MCDCDecision) const;
+
+  /// Return an MCDC record that indicates executed test vectors and condition
+  /// pairs.
+  Expected<MCDCRecord>
+  evaluateMCDCRegion(CounterMappingRegion Region, BitVector Bitmap,
+                     ArrayRef<CounterMappingRegion> Branches);
+
   unsigned getMaxCounterID(const Counter &C) const;
 };
 
@@ -364,6 +609,8 @@ struct FunctionRecord {
   std::vector<CountedRegion> CountedRegions;
   /// Branch Regions in the function along with their counts.
   std::vector<CountedRegion> CountedBranchRegions;
+  /// MCDC Records record a DecisionRegion and associated BranchRegions.
+  std::vector<MCDCRecord> MCDCRecords;
   /// The number of times this function was executed.
   uint64_t ExecutionCount = 0;
 
@@ -373,9 +620,12 @@ struct FunctionRecord {
   FunctionRecord(FunctionRecord &&FR) = default;
   FunctionRecord &operator=(FunctionRecord &&) = default;
 
+  void pushMCDCRecord(MCDCRecord Record) { MCDCRecords.push_back(Record); }
+
   void pushRegion(CounterMappingRegion Region, uint64_t Count,
                   uint64_t FalseCount) {
-    if (Region.Kind == CounterMappingRegion::BranchRegion) {
+    if (Region.Kind == CounterMappingRegion::BranchRegion ||
+        Region.Kind == CounterMappingRegion::MCDCBranchRegion) {
       CountedBranchRegions.emplace_back(Region, Count, FalseCount);
       // If both counters are hard-coded to zero, then this region represents a
       // constant-folded branch.
@@ -546,6 +796,7 @@ class CoverageData {
   std::vector<CoverageSegment> Segments;
   std::vector<ExpansionRecord> Expansions;
   std::vector<CountedRegion> BranchRegions;
+  std::vector<MCDCRecord> MCDCRecords;
 
 public:
   CoverageData() = default;
@@ -572,6 +823,9 @@ class CoverageData {
 
   /// Branches that can be further processed.
   ArrayRef<CountedRegion> getBranches() const { return BranchRegions; }
+
+  /// MCDC Records that can be further processed.
+  ArrayRef<MCDCRecord> getMCDCRecords() const { return MCDCRecords; }
 };
 
 /// The mapping of profile information to coverage data.

diff  --git a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
index 380a3aef9b1462..80875702b02ab9 100644
--- a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
+++ b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
@@ -30,6 +30,7 @@
 #include "llvm/Support/raw_ostream.h"
 #include <algorithm>
 #include <cassert>
+#include <cmath>
 #include <cstdint>
 #include <iterator>
 #include <map>
@@ -221,6 +222,264 @@ Expected<int64_t> CounterMappingContext::evaluate(const Counter &C) const {
   return LastPoppedValue;
 }
 
+Expected<BitVector> CounterMappingContext::evaluateBitmap(
+    const CounterMappingRegion *MCDCDecision) const {
+  unsigned ID = MCDCDecision->MCDCParams.BitmapIdx;
+  unsigned NC = MCDCDecision->MCDCParams.NumConditions;
+  unsigned SizeInBits = llvm::alignTo(uint64_t(1) << NC, CHAR_BIT);
+  unsigned SizeInBytes = SizeInBits / CHAR_BIT;
+
+  ArrayRef<uint8_t> Bytes(&BitmapBytes[ID], SizeInBytes);
+
+  // Mask each bitmap byte into the BitVector. Go in reverse so that the
+  // bitvector can just be shifted over by one byte on each iteration.
+  BitVector Result(SizeInBits, false);
+  for (auto Byte = std::rbegin(Bytes); Byte != std::rend(Bytes); ++Byte) {
+    uint32_t Data = *Byte;
+    Result <<= CHAR_BIT;
+    Result.setBitsInMask(&Data, 1);
+  }
+  return Result;
+}
+
+class MCDCRecordProcessor {
+  /// A bitmap representing the executed test vectors for a boolean expression.
+  /// Each index of the bitmap corresponds to a possible test vector. An index
+  /// with a bit value of '1' indicates that the corresponding Test Vector
+  /// identified by that index was executed.
+  BitVector &ExecutedTestVectorBitmap;
+
+  /// Decision Region to which the ExecutedTestVectorBitmap applies.
+  CounterMappingRegion &Region;
+
+  /// Array of branch regions corresponding each conditions in the boolean
+  /// expression.
+  ArrayRef<CounterMappingRegion> Branches;
+
+  /// Total number of conditions in the boolean expression.
+  unsigned NumConditions;
+
+  /// Mapping of a condition ID to its corresponding branch region.
+  llvm::DenseMap<unsigned, const CounterMappingRegion *> Map;
+
+  /// Vector used to track whether a condition is constant folded.
+  MCDCRecord::BoolVector Folded;
+
+  /// Mapping of calculated MC/DC Independence Pairs for each condition.
+  MCDCRecord::TVPairMap IndependencePairs;
+
+  /// Total number of possible Test Vectors for the boolean expression.
+  MCDCRecord::TestVectors TestVectors;
+
+  /// Actual executed Test Vectors for the boolean expression, based on
+  /// ExecutedTestVectorBitmap.
+  MCDCRecord::TestVectors ExecVectors;
+
+public:
+  MCDCRecordProcessor(BitVector &Bitmap, CounterMappingRegion &Region,
+                      ArrayRef<CounterMappingRegion> Branches)
+      : ExecutedTestVectorBitmap(Bitmap), Region(Region), Branches(Branches),
+        NumConditions(Region.MCDCParams.NumConditions),
+        Folded(NumConditions, false), IndependencePairs(NumConditions),
+        TestVectors(pow(2, NumConditions)) {}
+
+private:
+  void recordTestVector(MCDCRecord::TestVector &TV,
+                        MCDCRecord::CondState Result) {
+    // Calculate an index that is used to identify the test vector in a vector
+    // of test vectors.  This index also corresponds to the index values of an
+    // MCDC Region's bitmap (see findExecutedTestVectors()).
+    unsigned Index = 0;
+    for (auto Cond = std::rbegin(TV); Cond != std::rend(TV); ++Cond) {
+      Index <<= 1;
+      Index |= (*Cond == MCDCRecord::MCDC_True) ? 0x1 : 0x0;
+    }
+
+    // Copy the completed test vector to the vector of testvectors.
+    TestVectors[Index] = TV;
+
+    // The final value (T,F) is equal to the last non-dontcare state on the
+    // path (in a short-circuiting system).
+    TestVectors[Index].push_back(Result);
+  }
+
+  void shouldCopyOffTestVectorForTruePath(MCDCRecord::TestVector &TV,
+                                          unsigned ID) {
+    // Branch regions are hashed based on an ID.
+    const CounterMappingRegion *Branch = Map[ID];
+
+    TV[ID - 1] = MCDCRecord::MCDC_True;
+    if (Branch->MCDCParams.TrueID > 0)
+      buildTestVector(TV, Branch->MCDCParams.TrueID);
+    else
+      recordTestVector(TV, MCDCRecord::MCDC_True);
+  }
+
+  void shouldCopyOffTestVectorForFalsePath(MCDCRecord::TestVector &TV,
+                                           unsigned ID) {
+    // Branch regions are hashed based on an ID.
+    const CounterMappingRegion *Branch = Map[ID];
+
+    TV[ID - 1] = MCDCRecord::MCDC_False;
+    if (Branch->MCDCParams.FalseID > 0)
+      buildTestVector(TV, Branch->MCDCParams.FalseID);
+    else
+      recordTestVector(TV, MCDCRecord::MCDC_False);
+  }
+
+  /// Starting with the base test vector, build a comprehensive list of
+  /// possible test vectors by recursively walking the branch condition IDs
+  /// provided. Once an end node is reached, record the test vector in a vector
+  /// of test vectors that can be matched against during MC/DC analysis, and
+  /// then reset the positions to 'DontCare'.
+  void buildTestVector(MCDCRecord::TestVector &TV, unsigned ID = 1) {
+    shouldCopyOffTestVectorForTruePath(TV, ID);
+    shouldCopyOffTestVectorForFalsePath(TV, ID);
+
+    // Reset back to DontCare.
+    TV[ID - 1] = MCDCRecord::MCDC_DontCare;
+  }
+
+  /// Walk the bits in the bitmap.  A bit set to '1' indicates that the test
+  /// vector at the corresponding index was executed during a test run.
+  void findExecutedTestVectors(BitVector &ExecutedTestVectorBitmap) {
+    for (unsigned Idx = 0; Idx < ExecutedTestVectorBitmap.size(); ++Idx) {
+      if (ExecutedTestVectorBitmap[Idx] == 0)
+        continue;
+      assert(!TestVectors[Idx].empty() && "Test Vector doesn't exist.");
+      ExecVectors.push_back(TestVectors[Idx]);
+    }
+  }
+
+  /// For a given condition and two executed Test Vectors, A and B, see if the
+  /// two test vectors match forming an Independence Pair for the condition.
+  /// For two test vectors to match, the following must be satisfied:
+  /// - The condition's value in each test vector must be opposite.
+  /// - The result's value in each test vector must be opposite.
+  /// - All other conditions' values must be equal or marked as "don't care".
+  bool matchTestVectors(unsigned Aidx, unsigned Bidx, unsigned ConditionIdx) {
+    const MCDCRecord::TestVector &A = ExecVectors[Aidx];
+    const MCDCRecord::TestVector &B = ExecVectors[Bidx];
+
+    // If condition values in both A and B aren't opposites, no match.
+    // Because a value can be 0 (false), 1 (true), or -1 (DontCare), a check
+    // that "XOR != 1" will ensure that the values are opposites and that
+    // neither of them is a DontCare.
+    //  1 XOR  0 ==  1 | 0 XOR  0 ==  0 | -1 XOR  0 == -1
+    //  1 XOR  1 ==  0 | 0 XOR  1 ==  1 | -1 XOR  1 == -2
+    //  1 XOR -1 == -2 | 0 XOR -1 == -1 | -1 XOR -1 ==  0
+    if ((A[ConditionIdx] ^ B[ConditionIdx]) != 1)
+      return false;
+
+    // If the results of both A and B aren't opposites, no match.
+    if ((A[NumConditions] ^ B[NumConditions]) != 1)
+      return false;
+
+    for (unsigned Idx = 0; Idx < NumConditions; ++Idx) {
+      // Look for other conditions that don't match. Skip over the given
+      // Condition as well as any conditions marked as "don't care".
+      const auto ARecordTyForCond = A[Idx];
+      const auto BRecordTyForCond = B[Idx];
+      if (Idx == ConditionIdx ||
+          ARecordTyForCond == MCDCRecord::MCDC_DontCare ||
+          BRecordTyForCond == MCDCRecord::MCDC_DontCare)
+        continue;
+
+      // If there is a condition mismatch with any of the other conditions,
+      // there is no match for the test vectors.
+      if (ARecordTyForCond != BRecordTyForCond)
+        return false;
+    }
+
+    // Otherwise, match.
+    return true;
+  }
+
+  /// Find all possible Independence Pairs for a boolean expression given its
+  /// executed Test Vectors.  This process involves looking at each condition
+  /// and attempting to find two Test Vectors that "match", giving us a pair.
+  void findIndependencePairs() {
+    unsigned NumTVs = ExecVectors.size();
+
+    // For each condition.
+    for (unsigned C = 0; C < NumConditions; ++C) {
+      bool PairFound = false;
+
+      // For each executed test vector.
+      for (unsigned I = 0; !PairFound && I < NumTVs; ++I) {
+        // Compared to every other executed test vector.
+        for (unsigned J = 0; !PairFound && J < NumTVs; ++J) {
+          if (I == J)
+            continue;
+
+          // If a matching pair of vectors is found, record them.
+          if ((PairFound = matchTestVectors(I, J, C)))
+            IndependencePairs[C] = std::make_pair(I + 1, J + 1);
+        }
+      }
+    }
+  }
+
+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).
+  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
+    //   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
+    //   from being measured.
+    for (const auto &B : Branches) {
+      Map[B.MCDCParams.ID] = &B;
+      PosToID[I] = B.MCDCParams.ID - 1;
+      CondLoc[I] = B.startLoc();
+      Folded[I++] = (B.Count.isZero() && B.FalseCount.isZero());
+    }
+
+    // Initialize a base test vector as 'DontCare'.
+    MCDCRecord::TestVector TV(NumConditions, MCDCRecord::MCDC_DontCare);
+
+    // Use the base test vector to build the list of all possible test vectors.
+    buildTestVector(TV);
+
+    // Using Profile Bitmap from runtime, mark the executed test vectors.
+    findExecutedTestVectors(ExecutedTestVectorBitmap);
+
+    // Compare executed test vectors against each other to find an independence
+    // pairs for each condition.  This processing takes the most time.
+    findIndependencePairs();
+
+    // Record Test vectors, executed vectors, and independence pairs.
+    MCDCRecord Res(Region, ExecVectors, IndependencePairs, Folded, PosToID,
+                   CondLoc);
+    return Res;
+  }
+};
+
+Expected<MCDCRecord> CounterMappingContext::evaluateMCDCRegion(
+    CounterMappingRegion Region, BitVector ExecutedTestVectorBitmap,
+    ArrayRef<CounterMappingRegion> Branches) {
+
+  MCDCRecordProcessor MCDCProcessor(ExecutedTestVectorBitmap, Region, Branches);
+  return MCDCProcessor.processMCDCRecord();
+}
+
 unsigned CounterMappingContext::getMaxCounterID(const Counter &C) const {
   struct StackElem {
     Counter ICounter;
@@ -303,6 +562,24 @@ static unsigned getMaxCounterID(const CounterMappingContext &Ctx,
   return MaxCounterID;
 }
 
+static unsigned getMaxBitmapSize(const CounterMappingContext &Ctx,
+                                 const CoverageMappingRecord &Record) {
+  unsigned MaxBitmapID = 0;
+  unsigned NumConditions = 0;
+  // The last DecisionRegion has the highest bitmap byte index used in the
+  // function, which when combined with its number of conditions, yields the
+  // full bitmap size.
+  for (const auto &Region : reverse(Record.MappingRegions)) {
+    if (Region.Kind == CounterMappingRegion::MCDCDecisionRegion) {
+      MaxBitmapID = Region.MCDCParams.BitmapIdx;
+      NumConditions = Region.MCDCParams.NumConditions;
+      break;
+    }
+  }
+  unsigned SizeInBits = llvm::alignTo(uint64_t(1) << NumConditions, CHAR_BIT);
+  return MaxBitmapID + (SizeInBits / CHAR_BIT);
+}
+
 Error CoverageMapping::loadFunctionRecord(
     const CoverageMappingRecord &Record,
     IndexedInstrProfReader &ProfileReader) {
@@ -326,12 +603,28 @@ Error CoverageMapping::loadFunctionRecord(
       FuncHashMismatches.emplace_back(std::string(Record.FunctionName),
                                       Record.FunctionHash);
       return Error::success();
-    } else if (IPE != instrprof_error::unknown_function)
+    }
+    if (IPE != instrprof_error::unknown_function)
       return make_error<InstrProfError>(IPE);
     Counts.assign(getMaxCounterID(Ctx, Record) + 1, 0);
   }
   Ctx.setCounts(Counts);
 
+  std::vector<uint8_t> BitmapBytes;
+  if (Error E = ProfileReader.getFunctionBitmapBytes(
+          Record.FunctionName, Record.FunctionHash, BitmapBytes)) {
+    instrprof_error IPE = std::get<0>(InstrProfError::take(std::move(E)));
+    if (IPE == instrprof_error::hash_mismatch) {
+      FuncHashMismatches.emplace_back(std::string(Record.FunctionName),
+                                      Record.FunctionHash);
+      return Error::success();
+    }
+    if (IPE != instrprof_error::unknown_function)
+      return make_error<InstrProfError>(IPE);
+    BitmapBytes.assign(getMaxBitmapSize(Ctx, Record) + 1, 0);
+  }
+  Ctx.setBitmapBytes(BitmapBytes);
+
   assert(!Record.MappingRegions.empty() && "Function has no regions");
 
   // This coverage record is a zero region for a function that's unused in
@@ -343,8 +636,20 @@ Error CoverageMapping::loadFunctionRecord(
       Record.MappingRegions[0].Count.isZero() && Counts[0] > 0)
     return Error::success();
 
+  unsigned NumConds = 0;
+  const CounterMappingRegion *MCDCDecision;
+  std::vector<CounterMappingRegion> MCDCBranches;
+
   FunctionRecord Function(OrigFuncName, Record.Filenames);
   for (const auto &Region : Record.MappingRegions) {
+    // If an MCDCDecisionRegion is seen, track the BranchRegions that follow
+    // it according to Region.NumConditions.
+    if (Region.Kind == CounterMappingRegion::MCDCDecisionRegion) {
+      assert(NumConds == 0);
+      MCDCDecision = &Region;
+      NumConds = Region.MCDCParams.NumConditions;
+      continue;
+    }
     Expected<int64_t> ExecutionCount = Ctx.evaluate(Region.Count);
     if (auto E = ExecutionCount.takeError()) {
       consumeError(std::move(E));
@@ -356,6 +661,44 @@ Error CoverageMapping::loadFunctionRecord(
       return Error::success();
     }
     Function.pushRegion(Region, *ExecutionCount, *AltExecutionCount);
+
+    // If a MCDCDecisionRegion was seen, store the BranchRegions that
+    // correspond to it in a vector, according to the number of conditions
+    // recorded for the region (tracked by NumConds).
+    if (NumConds > 0 && Region.Kind == CounterMappingRegion::MCDCBranchRegion) {
+      MCDCBranches.push_back(Region);
+
+      // As we move through all of the MCDCBranchRegions that follow the
+      // MCDCDecisionRegion, decrement NumConds to make sure we account for
+      // them all before we calculate the bitmap of executed test vectors.
+      if (--NumConds == 0) {
+        // Evaluating the test vector bitmap for the decision region entails
+        // calculating precisely what bits are pertinent to this region alone.
+        // This is calculated based on the recorded offset into the global
+        // profile bitmap; the length is calculated based on the recorded
+        // number of conditions.
+        Expected<BitVector> ExecutedTestVectorBitmap =
+            Ctx.evaluateBitmap(MCDCDecision);
+        if (auto E = ExecutedTestVectorBitmap.takeError()) {
+          consumeError(std::move(E));
+          return Error::success();
+        }
+
+        // Since the bitmap identifies the executed test vectors for an MC/DC
+        // DecisionRegion, all of the information is now available to process.
+        // This is where the bulk of the MC/DC progressing takes place.
+        Expected<MCDCRecord> Record = Ctx.evaluateMCDCRegion(
+            *MCDCDecision, *ExecutedTestVectorBitmap, MCDCBranches);
+        if (auto E = Record.takeError()) {
+          consumeError(std::move(E));
+          return Error::success();
+        }
+
+        // Save the MC/DC Record so that it can be visualized later.
+        Function.pushMCDCRecord(*Record);
+        MCDCBranches.clear();
+      }
+    }
   }
 
   // Don't create records for (filenames, function) pairs we've already seen.
@@ -862,6 +1205,10 @@ CoverageData CoverageMapping::getCoverageForFile(StringRef Filename) const {
     for (const auto &CR : Function.CountedBranchRegions)
       if (FileIDs.test(CR.FileID) && (CR.FileID == CR.ExpandedFileID))
         FileCoverage.BranchRegions.push_back(CR);
+    // Capture MCDC records specific to the function.
+    for (const auto &MR : Function.MCDCRecords)
+      if (FileIDs.test(MR.getDecisionRegion().FileID))
+        FileCoverage.MCDCRecords.push_back(MR);
   }
 
   LLVM_DEBUG(dbgs() << "Emitting segments for file: " << Filename << "\n");
@@ -914,6 +1261,11 @@ CoverageMapping::getCoverageForFunction(const FunctionRecord &Function) const {
     if (CR.FileID == *MainFileID)
       FunctionCoverage.BranchRegions.push_back(CR);
 
+  // Capture MCDC records specific to the function.
+  for (const auto &MR : Function.MCDCRecords)
+    if (MR.getDecisionRegion().FileID == *MainFileID)
+      FunctionCoverage.MCDCRecords.push_back(MR);
+
   LLVM_DEBUG(dbgs() << "Emitting segments for function: " << Function.Name
                     << "\n");
   FunctionCoverage.Segments = SegmentBuilder::buildSegments(Regions);

diff  --git a/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp b/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp
index d6aade6fcd0f8d..56a7ab24a2f08a 100644
--- a/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp
+++ b/llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp
@@ -244,6 +244,7 @@ Error RawCoverageMappingReader::readMappingRegionsSubArray(
   unsigned LineStart = 0;
   for (size_t I = 0; I < NumRegions; ++I) {
     Counter C, C2;
+    uint64_t BIDX = 0, NC = 0, ID = 0, TID = 0, FID = 0;
     CounterMappingRegion::RegionKind Kind = CounterMappingRegion::CodeRegion;
 
     // Read the combined counter + region kind.
@@ -294,6 +295,27 @@ Error RawCoverageMappingReader::readMappingRegionsSubArray(
           if (auto Err = readCounter(C2))
             return Err;
           break;
+        case CounterMappingRegion::MCDCBranchRegion:
+          // For a MCDC Branch Region, read two successive counters and 3 IDs.
+          Kind = CounterMappingRegion::MCDCBranchRegion;
+          if (auto Err = readCounter(C))
+            return Err;
+          if (auto Err = readCounter(C2))
+            return Err;
+          if (auto Err = readIntMax(ID, std::numeric_limits<unsigned>::max()))
+            return Err;
+          if (auto Err = readIntMax(TID, std::numeric_limits<unsigned>::max()))
+            return Err;
+          if (auto Err = readIntMax(FID, std::numeric_limits<unsigned>::max()))
+            return Err;
+          break;
+        case CounterMappingRegion::MCDCDecisionRegion:
+          Kind = CounterMappingRegion::MCDCDecisionRegion;
+          if (auto Err = readIntMax(BIDX, std::numeric_limits<unsigned>::max()))
+            return Err;
+          if (auto Err = readIntMax(NC, std::numeric_limits<unsigned>::max()))
+            return Err;
+          break;
         default:
           return make_error<CoverageMapError>(coveragemap_error::malformed,
                                               "region kind is incorrect");
@@ -347,9 +369,14 @@ Error RawCoverageMappingReader::readMappingRegionsSubArray(
       dbgs() << "\n";
     });
 
-    auto CMR = CounterMappingRegion(C, C2, InferredFileID, ExpandedFileID,
-                                    LineStart, ColumnStart,
-                                    LineStart + NumLines, ColumnEnd, Kind);
+    auto CMR = CounterMappingRegion(
+        C, C2,
+        CounterMappingRegion::MCDCParameters{
+            static_cast<unsigned>(BIDX), static_cast<unsigned>(NC),
+            static_cast<unsigned>(ID), static_cast<unsigned>(TID),
+            static_cast<unsigned>(FID)},
+        InferredFileID, ExpandedFileID, LineStart, ColumnStart,
+        LineStart + NumLines, ColumnEnd, Kind);
     if (CMR.startLoc() > CMR.endLoc())
       return make_error<CoverageMapError>(
           coveragemap_error::malformed,

diff  --git a/llvm/lib/ProfileData/Coverage/CoverageMappingWriter.cpp b/llvm/lib/ProfileData/Coverage/CoverageMappingWriter.cpp
index 2abfbbad16ee92..1c7d8a8909c488 100644
--- a/llvm/lib/ProfileData/Coverage/CoverageMappingWriter.cpp
+++ b/llvm/lib/ProfileData/Coverage/CoverageMappingWriter.cpp
@@ -237,6 +237,23 @@ void CoverageMappingWriter::write(raw_ostream &OS) {
       writeCounter(MinExpressions, Count, OS);
       writeCounter(MinExpressions, FalseCount, OS);
       break;
+    case CounterMappingRegion::MCDCBranchRegion:
+      encodeULEB128(unsigned(I->Kind)
+                        << Counter::EncodingCounterTagAndExpansionRegionTagBits,
+                    OS);
+      writeCounter(MinExpressions, Count, OS);
+      writeCounter(MinExpressions, FalseCount, OS);
+      encodeULEB128(unsigned(I->MCDCParams.ID), OS);
+      encodeULEB128(unsigned(I->MCDCParams.TrueID), OS);
+      encodeULEB128(unsigned(I->MCDCParams.FalseID), OS);
+      break;
+    case CounterMappingRegion::MCDCDecisionRegion:
+      encodeULEB128(unsigned(I->Kind)
+                        << Counter::EncodingCounterTagAndExpansionRegionTagBits,
+                    OS);
+      encodeULEB128(unsigned(I->MCDCParams.BitmapIdx), OS);
+      encodeULEB128(unsigned(I->MCDCParams.NumConditions), OS);
+      break;
     }
     assert(I->LineStart >= PrevLineStart);
     encodeULEB128(I->LineStart - PrevLineStart, OS);

diff  --git a/llvm/test/tools/llvm-cov/Inputs/binary-formats.canonical.json b/llvm/test/tools/llvm-cov/Inputs/binary-formats.canonical.json
index a30411a84b23ea..ce13fc2ff6e345 100644
--- a/llvm/test/tools/llvm-cov/Inputs/binary-formats.canonical.json
+++ b/llvm/test/tools/llvm-cov/Inputs/binary-formats.canonical.json
@@ -1,9 +1,11 @@
-CHECK: {"data":
-CHECK-SAME: [{
+CHECK: {
+  "data": CHECK - SAME: [
+    {
 CHECK-SAME:  "files":[
 CHECK-SAME:    {"branches":[],
 CHECK-SAME:     "expansions":[],
 CHECK-SAME:     "filename":"/tmp/binary-formats.c",
+CHECK-SAME:     "mcdc_records":[],
 CHECK-SAME:     "segments":
 CHECK-SAME:        4,40,100,true,true,false
 CHECK-SAME:        4,42,0,false,false,false
@@ -11,11 +13,13 @@ CHECK-SAME:     "summary":{"branches":{"count":0,"covered":0,"notcovered":0,"per
 CHECK-SAME:                "functions":{"count":1,"covered":1,"percent":100},
 CHECK-SAME:                "instantiations":{"count":1,"covered":1,"percent":100},
 CHECK-SAME:                "lines":{"count":1,"covered":1,"percent":100},
+CHECK-SAME:                "mcdc":{"count":0,"covered":0,"notcovered":0,"percent":0},
 CHECK-SAME:                "regions":{"count":1,"covered":1,"notcovered":0,"percent":100}}}
 CHECK-SAME:  ],
 CHECK-SAME:  "functions":[
 CHECK-SAME:    {"branches":[],
-CHECK-SAME:     "count":100,"filenames":["/tmp/binary-formats.c"],"name":"main",
+CHECK-SAME:     "count":100,"filenames":["/tmp/binary-formats.c"],
+CHECK-SAME:     "mcdc_records":[],"name":"main",
 CHECK-SAME:     "regions":
 CHECK-SAME:        4,40,4,42,100,0,0,0
 CHECK-SAME:    }
@@ -25,6 +29,7 @@ CHECK-SAME:    {"branches":{"count":0,"covered":0,"notcovered":0,"percent":0},
 CHECK-SAME:     "functions":{"count":1,"covered":1,"percent":100},
 CHECK-SAME:     "instantiations":{"count":1,"covered":1,"percent":100},
 CHECK-SAME:     "lines":{"count":1,"covered":1,"percent":100},
+CHECk-SAME:     "mcdc":{"count":0,"covered":0,"notcovered":0,"percent":0},
 CHECK-SAME:     "regions":{"count":1,"covered":1,"notcovered":0,"percent":100}}}
 CHECK-SAME: ],
 CHECK-SAME: "type":"llvm.coverage.json.export"

diff  --git a/llvm/test/tools/llvm-cov/Inputs/mcdc-const-folding.cpp b/llvm/test/tools/llvm-cov/Inputs/mcdc-const-folding.cpp
new file mode 100644
index 00000000000000..75f35bd059eb1c
--- /dev/null
+++ b/llvm/test/tools/llvm-cov/Inputs/mcdc-const-folding.cpp
@@ -0,0 +1,117 @@
+#include <stdlib.h>
+
+bool case0(bool a) {
+    return 0 && a;
+}
+bool case1(bool a) {
+    return a && 0;
+}
+bool case2(bool a) {
+    return 1 && a;
+}
+bool case3(bool a) {
+    return a && 1;
+}
+bool case4(bool a) {
+    return 1 || a;
+}
+bool case5(bool a) {
+    return a || 1;
+}
+bool case6(bool a) {
+    return 0 || a;
+}
+bool case7(bool a) {
+    return a || 0;
+}
+
+bool case8(bool a, bool b) {
+    return 0 && a && b;
+}
+bool case9(bool a, bool b) {
+    return a && 0 && b;
+}
+bool casea(bool a, bool b) {
+    return 1 && a && b;
+}
+bool caseb(bool a, bool b) {
+    return a && 1 && b;
+}
+bool casec(bool a, bool b) {
+    return 1 || a || b;
+}
+bool cased(bool a, bool b) {
+    return a || 1 || b;
+}
+bool casee(bool a, bool b) {
+    return 0 || a || b;
+}
+bool casef(bool a, bool b) {
+    return a || 0 || b;
+}
+
+bool caseg(bool a, bool b) {
+    return b && a && 0;
+}
+bool caseh(bool a, bool b) {
+    return b && 0 && a;
+}
+bool casei(bool a, bool b) {
+    return b && a && 1;
+}
+bool casej(bool a, bool b) {
+    return b && 1 && a;
+}
+bool casek(bool a, bool b) {
+    return b || a || 1;
+}
+bool casel(bool a, bool b) {
+    return b || 1 || a;
+}
+bool casem(bool a, bool b) {
+    return b || a || 0;
+}
+bool casen(bool a, bool b) {
+    return b || 0 || a;
+}
+
+extern "C" {
+  extern void __llvm_profile_write_file(void);
+}
+
+int main(int argc, char *argv[])
+{
+    bool a = atoi(argv[1]);
+    bool b = atoi(argv[2]);
+    volatile bool c;
+
+    c = case0(a);
+    c = case1(a);
+    c = case2(a);
+    c = case3(a);
+    c = case4(a);
+    c = case5(a);
+    c = case6(a);
+    c = case7(a);
+
+    c = case8(a, b);
+    c = case9(a, b);
+    c = casea(a, b);
+    c = caseb(a, b);
+    c = casec(a, b);
+    c = cased(a, b);
+    c = casee(a, b);
+    c = casef(a, b);
+
+    c = caseg(a, b);
+    c = caseh(a, b);
+    c = casei(a, b);
+    c = casej(a, b);
+    c = casek(a, b);
+    c = casel(a, b);
+    c = casem(a, b);
+    c = casen(a, b);
+
+    __llvm_profile_write_file();
+    return 0;
+}

diff  --git a/llvm/test/tools/llvm-cov/Inputs/mcdc-const-folding.o b/llvm/test/tools/llvm-cov/Inputs/mcdc-const-folding.o
new file mode 100755
index 00000000000000..2fb33f3f601a3f
Binary files /dev/null and b/llvm/test/tools/llvm-cov/Inputs/mcdc-const-folding.o 
diff er

diff  --git a/llvm/test/tools/llvm-cov/Inputs/mcdc-const-folding.proftext b/llvm/test/tools/llvm-cov/Inputs/mcdc-const-folding.proftext
new file mode 100644
index 00000000000000..eaac8048f0ceb6
--- /dev/null
+++ b/llvm/test/tools/llvm-cov/Inputs/mcdc-const-folding.proftext
@@ -0,0 +1,398 @@
+_Z5case8bb
+# Func Hash:
+99214
+# Num Counters:
+5
+# Counter Values:
+4
+0
+0
+0
+0
+# Num Bitmask Bytes:
+$1
+# Bitmask Byte Values:
+0x1
+
+
+_Z5case5b
+# Func Hash:
+1551
+# Num Counters:
+3
+# Counter Values:
+4
+1
+0
+# Num Bitmask Bytes:
+$1
+# Bitmask Byte Values:
+0x6
+
+
+_Z5caseabb
+# Func Hash:
+99214
+# Num Counters:
+5
+# Counter Values:
+4
+3
+2
+4
+3
+# Num Bitmask Bytes:
+$1
+# Bitmask Byte Values:
+0xa2
+
+
+_Z5case6b
+# Func Hash:
+1551
+# Num Counters:
+3
+# Counter Values:
+4
+4
+1
+# Num Bitmask Bytes:
+$1
+# Bitmask Byte Values:
+0x5
+
+
+_Z5casegbb
+# Func Hash:
+99214
+# Num Counters:
+5
+# Counter Values:
+4
+2
+0
+3
+2
+# Num Bitmask Bytes:
+$1
+# Bitmask Byte Values:
+0x23
+
+
+_Z5case1b
+# Func Hash:
+1550
+# Num Counters:
+3
+# Counter Values:
+4
+3
+0
+# Num Bitmask Bytes:
+$1
+# Bitmask Byte Values:
+0x3
+
+
+_Z5case7b
+# Func Hash:
+1551
+# Num Counters:
+3
+# Counter Values:
+4
+1
+1
+# Num Bitmask Bytes:
+$1
+# Bitmask Byte Values:
+0x3
+
+
+_Z5casedbb
+# Func Hash:
+99279
+# Num Counters:
+5
+# Counter Values:
+4
+0
+0
+1
+0
+# Num Bitmask Bytes:
+$1
+# Bitmask Byte Values:
+0x12
+
+
+_Z5casekbb
+# Func Hash:
+99279
+# Num Counters:
+5
+# Counter Values:
+4
+0
+0
+1
+0
+# Num Bitmask Bytes:
+$1
+# Bitmask Byte Values:
+0x12
+
+
+_Z5casehbb
+# Func Hash:
+99214
+# Num Counters:
+5
+# Counter Values:
+4
+0
+0
+3
+0
+# Num Bitmask Bytes:
+$1
+# Bitmask Byte Values:
+0x3
+
+
+_Z5case4b
+# Func Hash:
+1551
+# Num Counters:
+3
+# Counter Values:
+4
+0
+0
+# Num Bitmask Bytes:
+$1
+# Bitmask Byte Values:
+0x2
+
+
+_Z5caseibb
+# Func Hash:
+99214
+# Num Counters:
+5
+# Counter Values:
+4
+2
+2
+3
+2
+# Num Bitmask Bytes:
+$1
+# Bitmask Byte Values:
+0x83
+
+
+_Z5case2b
+# Func Hash:
+1550
+# Num Counters:
+3
+# Counter Values:
+4
+4
+3
+# Num Bitmask Bytes:
+$1
+# Bitmask Byte Values:
+0xa
+
+
+_Z5casefbb
+# Func Hash:
+99279
+# Num Counters:
+5
+# Counter Values:
+4
+1
+0
+1
+1
+# Num Bitmask Bytes:
+$1
+# Bitmask Byte Values:
+0x6
+
+
+_Z5caselbb
+# Func Hash:
+99279
+# Num Counters:
+5
+# Counter Values:
+4
+0
+0
+1
+0
+# Num Bitmask Bytes:
+$1
+# Bitmask Byte Values:
+0x12
+
+
+_Z5casenbb
+# Func Hash:
+99279
+# Num Counters:
+5
+# Counter Values:
+4
+1
+0
+1
+1
+# Num Bitmask Bytes:
+$1
+# Bitmask Byte Values:
+0x6
+
+
+_Z5case9bb
+# Func Hash:
+99214
+# Num Counters:
+5
+# Counter Values:
+4
+0
+0
+3
+0
+# Num Bitmask Bytes:
+$1
+# Bitmask Byte Values:
+0x3
+
+
+_Z5casecbb
+# Func Hash:
+99279
+# Num Counters:
+5
+# Counter Values:
+4
+0
+0
+0
+0
+# Num Bitmask Bytes:
+$1
+# Bitmask Byte Values:
+0x2
+
+
+_Z5casebbb
+# Func Hash:
+99214
+# Num Counters:
+5
+# Counter Values:
+4
+3
+2
+3
+3
+# Num Bitmask Bytes:
+$1
+# Bitmask Byte Values:
+0xa1
+
+
+_Z5case0b
+# Func Hash:
+1550
+# Num Counters:
+3
+# Counter Values:
+4
+0
+0
+# Num Bitmask Bytes:
+$1
+# Bitmask Byte Values:
+0x1
+
+
+_Z5casejbb
+# Func Hash:
+99214
+# Num Counters:
+5
+# Counter Values:
+4
+3
+2
+3
+3
+# Num Bitmask Bytes:
+$1
+# Bitmask Byte Values:
+0xa1
+
+
+_Z5caseebb
+# Func Hash:
+99279
+# Num Counters:
+5
+# Counter Values:
+4
+1
+0
+4
+1
+# Num Bitmask Bytes:
+$1
+# Bitmask Byte Values:
+0x14
+
+
+main
+# Func Hash:
+24
+# Num Counters:
+1
+# Counter Values:
+4
+
+_Z5case3b
+# Func Hash:
+1550
+# Num Counters:
+3
+# Counter Values:
+4
+3
+3
+# Num Bitmask Bytes:
+$1
+# Bitmask Byte Values:
+0x9
+
+
+_Z5casembb
+# Func Hash:
+99279
+# Num Counters:
+5
+# Counter Values:
+4
+0
+0
+1
+0
+# Num Bitmask Bytes:
+$1
+# Bitmask Byte Values:
+0x12

diff  --git a/llvm/test/tools/llvm-cov/Inputs/mcdc-const.cpp b/llvm/test/tools/llvm-cov/Inputs/mcdc-const.cpp
new file mode 100644
index 00000000000000..4a777d11ae1f2d
--- /dev/null
+++ b/llvm/test/tools/llvm-cov/Inputs/mcdc-const.cpp
@@ -0,0 +1,27 @@
+
+#include <stdio.h>
+
+extern "C" {
+extern void __llvm_profile_write_file(void);
+}
+
+extern int foo();
+
+void test(bool a, bool b, bool c, bool d) {
+
+  if ((a && 1) || (0 && d) || 0)
+    printf("test1 decision true\n");
+}
+
+int main()
+{
+    test(true,false,true,false);
+    test(true,false,true,true);
+    test(true,true,false,false);
+    test(false,true,true,false);
+
+    test(true,false,false,false);
+
+    __llvm_profile_write_file();
+    return 0;
+}

diff  --git a/llvm/test/tools/llvm-cov/Inputs/mcdc-const.o b/llvm/test/tools/llvm-cov/Inputs/mcdc-const.o
new file mode 100755
index 00000000000000..092c2a3e8402e0
Binary files /dev/null and b/llvm/test/tools/llvm-cov/Inputs/mcdc-const.o 
diff er

diff  --git a/llvm/test/tools/llvm-cov/Inputs/mcdc-const.proftext b/llvm/test/tools/llvm-cov/Inputs/mcdc-const.proftext
new file mode 100644
index 00000000000000..86ac65eb779ac2
--- /dev/null
+++ b/llvm/test/tools/llvm-cov/Inputs/mcdc-const.proftext
@@ -0,0 +1,30 @@
+main
+# Func Hash:
+24
+# Num Counters:
+1
+# Counter Values:
+1
+
+_Z4testbbbb
+# Func Hash:
+703556281489
+# Num Counters:
+9
+# Counter Values:
+5
+4
+1
+1
+1
+4
+4
+0
+0
+# Num Bitmask Bytes:
+$4
+# Bitmask Byte Values:
+1
+2
+0
+0

diff  --git a/llvm/test/tools/llvm-cov/Inputs/mcdc-general-none.proftext b/llvm/test/tools/llvm-cov/Inputs/mcdc-general-none.proftext
new file mode 100644
index 00000000000000..d929766c12545b
--- /dev/null
+++ b/llvm/test/tools/llvm-cov/Inputs/mcdc-general-none.proftext
@@ -0,0 +1,42 @@
+main
+# Func Hash:
+24
+# Num Counters:
+1
+# Counter Values:
+1
+
+_Z4testbbbb
+# Func Hash:
+9819241276358969079
+# Num Counters:
+19
+# Counter Values:
+7
+3
+5
+5
+2
+3
+1
+2
+3
+2
+1
+1
+1
+1
+2
+4
+2
+2
+1
+# Num Bitmask Bytes:
+$6
+# Bitmask Byte Values:
+0
+0
+0
+0
+0
+0

diff  --git a/llvm/test/tools/llvm-cov/Inputs/mcdc-general.cpp b/llvm/test/tools/llvm-cov/Inputs/mcdc-general.cpp
new file mode 100644
index 00000000000000..d1ce365d780742
--- /dev/null
+++ b/llvm/test/tools/llvm-cov/Inputs/mcdc-general.cpp
@@ -0,0 +1,36 @@
+
+#include <stdio.h>
+
+extern "C" {
+extern void __llvm_profile_write_file(void);
+}
+
+extern int foo();
+
+void test(bool a, bool b, bool c, bool d) {
+
+  if ((a && b) || (c && d))
+    printf("test1 decision true\n");
+
+  if (b && c) if (a && d)
+    printf("test2 decision true\n");
+
+  if ((c && d) &&
+      (a && b))
+    printf("test3 decision true\n");
+}
+
+int main()
+{
+    test(false,false,false,false);
+    test(true,false,true,false);
+    test(true,false,true,true);
+    test(true,true,false,false);
+
+    test(true,false,false,false);
+    test(true,true,true,true);
+    test(false,true,true,false);
+
+    __llvm_profile_write_file();
+    return 0;
+}

diff  --git a/llvm/test/tools/llvm-cov/Inputs/mcdc-general.o b/llvm/test/tools/llvm-cov/Inputs/mcdc-general.o
new file mode 100755
index 00000000000000..4044be1ca3886c
Binary files /dev/null and b/llvm/test/tools/llvm-cov/Inputs/mcdc-general.o 
diff er

diff  --git a/llvm/test/tools/llvm-cov/Inputs/mcdc-general.proftext b/llvm/test/tools/llvm-cov/Inputs/mcdc-general.proftext
new file mode 100644
index 00000000000000..eadc9157c72cda
--- /dev/null
+++ b/llvm/test/tools/llvm-cov/Inputs/mcdc-general.proftext
@@ -0,0 +1,42 @@
+main
+# Func Hash:
+24
+# Num Counters:
+1
+# Counter Values:
+1
+
+_Z4testbbbb
+# Func Hash:
+9819241276358969079
+# Num Counters:
+19
+# Counter Values:
+7
+3
+5
+5
+2
+3
+1
+2
+3
+2
+1
+1
+1
+1
+2
+4
+2
+2
+1
+# Num Bitmask Bytes:
+$6
+# Bitmask Byte Values:
+0x2f
+0x8
+0xb
+0x9
+0x83
+0x80

diff  --git a/llvm/test/tools/llvm-cov/mcdc-const.test b/llvm/test/tools/llvm-cov/mcdc-const.test
new file mode 100644
index 00000000000000..bf9d34b09da0f6
--- /dev/null
+++ b/llvm/test/tools/llvm-cov/mcdc-const.test
@@ -0,0 +1,218 @@
+// Test visualization of MC/DC constructs for constant-folded condition masking.
+
+// RUN: llvm-profdata merge %S/Inputs/mcdc-const.proftext -o %t.profdata
+// RUN: llvm-cov show --show-branches=count --show-mcdc %S/Inputs/mcdc-const.o -instr-profile %t.profdata -path-equivalence=/tmp,%S/Inputs %S/Inputs/mcdc-const.cpp | FileCheck %s -check-prefix=CHECKGENERALCASE
+
+// CHECKGENERALCASE:  ------------------
+// CHECKGENERALCASE-NEXT:  |  Branch (12:8): [True: 4, False: 1]
+// CHECKGENERALCASE-NEXT:  |  Branch (12:13): [Folded - Ignored]
+// CHECKGENERALCASE-NEXT:  |  Branch (12:20): [Folded - Ignored]
+// CHECKGENERALCASE-NEXT:  |  Branch (12:25): [True: 0, False: 0]
+// CHECKGENERALCASE-NEXT:  |  Branch (12:31): [Folded - Ignored]
+// CHECKGENERALCASE-NEXT:  ------------------
+// CHECKGENERALCASE-NEXT:  |---> MC/DC Decision Region (12:7) to (12:32)
+// CHECKGENERALCASE-NEXT:  |
+// CHECKGENERALCASE-NEXT:  |  Number of Conditions: 5
+// CHECKGENERALCASE-NEXT:  |     Condition C1 --> (12:8)
+// CHECKGENERALCASE-NEXT:  |     Condition C2 --> (12:13)
+// CHECKGENERALCASE-NEXT:  |     Condition C3 --> (12:20)
+// CHECKGENERALCASE-NEXT:  |     Condition C4 --> (12:25)
+// CHECKGENERALCASE-NEXT:  |     Condition C5 --> (12:31)
+// CHECKGENERALCASE-NEXT:  |
+// CHECKGENERALCASE-NEXT:  |  Executed MC/DC Test Vectors:
+// CHECKGENERALCASE-NEXT:  |
+// CHECKGENERALCASE-NEXT:  |     C1, C2, C3, C4, C5    Result
+// CHECKGENERALCASE-NEXT:  |  1 { F,  C,  C,  -,  C  = F      }
+// CHECKGENERALCASE-NEXT:  |  2 { T,  C,  C,  -,  C  = T      }
+// CHECKGENERALCASE-NEXT:  |
+// 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:  |  C5-Pair: constant folded
+// CHECKGENERALCASE-NEXT:  |  MC/DC Coverage for Decision: 50.00%
+// CHECKGENERALCASE-NEXT:  |
+// CHECKGENERALCASE-NEXT:  ------------------
+
+// RUN: llvm-profdata merge %S/Inputs/mcdc-const-folding.proftext -o %t.profdata
+// RUN: llvm-cov show --show-mcdc %S/Inputs/mcdc-const-folding.o -instr-profile %t.profdata -path-equivalence=/tmp,%S/Inputs %S/Inputs/mcdc-const-folding.cpp | FileCheck %s -check-prefix=CHECKFULLCASE
+// RUN: llvm-cov report --show-mcdc-summary %S/Inputs/mcdc-const-folding.o -instr-profile %t.profdata -show-functions -path-equivalence=/tmp,%S/Inputs %S/Inputs/mcdc-const-folding.cpp | FileCheck %s -check-prefix=REPORT
+
+// CHECKFULLCASE: |  1 { C,  -  = F      }
+// CHECKFULLCASE: |  C1-Pair: constant folded
+// CHECKFULLCASE-NEXT: |  C2-Pair: not covered
+// CHECKFULLCASE: |  MC/DC Coverage for Decision: 0.00%
+// CHECKFULLCASE: |  1 { F,  C  = F      }
+// CHECKFULLCASE-NEXT: |  2 { T,  C  = F      }
+// CHECKFULLCASE: |  C1-Pair: not covered
+// CHECKFULLCASE-NEXT: |  C2-Pair: constant folded
+// CHECKFULLCASE: |  MC/DC Coverage for Decision: 0.00%
+// CHECKFULLCASE: |  1 { C,  F  = F      }
+// CHECKFULLCASE-NEXT: |  2 { C,  T  = T      }
+// CHECKFULLCASE: |  C1-Pair: constant folded
+// CHECKFULLCASE-NEXT: |  C2-Pair: covered: (1,2)
+// CHECKFULLCASE: |  MC/DC Coverage for Decision: 100.00%
+// CHECKFULLCASE: |  1 { F,  C  = F      }
+// CHECKFULLCASE-NEXT: |  2 { T,  C  = T      }
+// CHECKFULLCASE: |  C1-Pair: covered: (1,2)
+// CHECKFULLCASE-NEXT: |  C2-Pair: constant folded
+// 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: |  1 { T,  C  = T      }
+// CHECKFULLCASE-NEXT: |  2 { F,  C  = T      }
+// CHECKFULLCASE: |  C1-Pair: not covered
+// CHECKFULLCASE-NEXT: |  C2-Pair: constant folded
+// CHECKFULLCASE: |  MC/DC Coverage for Decision: 0.00%
+// CHECKFULLCASE: |  1 { C,  F  = F      }
+// CHECKFULLCASE-NEXT: |  2 { C,  T  = T      }
+// CHECKFULLCASE: |  C1-Pair: constant folded
+// CHECKFULLCASE-NEXT: |  C2-Pair: covered: (1,2)
+// CHECKFULLCASE: |  MC/DC Coverage for Decision: 100.00%
+// CHECKFULLCASE: |  1 { F,  C  = F      }
+// CHECKFULLCASE-NEXT: |  2 { T,  C  = T      }
+// CHECKFULLCASE: |  C1-Pair: covered: (1,2)
+// CHECKFULLCASE-NEXT: |  C2-Pair: constant folded
+// 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: |  1 { F,  C,  -  = F      }
+// CHECKFULLCASE-NEXT: |  2 { T,  C,  -  = F      }
+// CHECKFULLCASE: |  C1-Pair: not covered
+// CHECKFULLCASE-NEXT: |  C2-Pair: constant folded
+// CHECKFULLCASE-NEXT: |  C3-Pair: not covered
+// CHECKFULLCASE: |  MC/DC Coverage for Decision: 0.00%
+// CHECKFULLCASE: |  1 { C,  F,  -  = F      }
+// CHECKFULLCASE-NEXT: |  2 { C,  T,  F  = F      }
+// CHECKFULLCASE-NEXT: |  3 { C,  T,  T  = T      }
+// CHECKFULLCASE: |  C1-Pair: constant folded
+// CHECKFULLCASE-NEXT: |  C2-Pair: covered: (1,3)
+// CHECKFULLCASE-NEXT: |  C3-Pair: covered: (2,3)
+// CHECKFULLCASE: |  MC/DC Coverage for Decision: 100.00%
+// CHECKFULLCASE: |  1 { F,  C,  -  = F      }
+// CHECKFULLCASE-NEXT: |  2 { T,  C,  F  = F      }
+// CHECKFULLCASE-NEXT: |  3 { T,  C,  T  = T      }
+// CHECKFULLCASE: |  C1-Pair: covered: (1,3)
+// CHECKFULLCASE-NEXT: |  C2-Pair: constant folded
+// CHECKFULLCASE-NEXT: |  C3-Pair: covered: (2,3)
+// 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: |  1 { T,  C,  -  = T      }
+// CHECKFULLCASE-NEXT: |  2 { F,  C,  -  = T      }
+// CHECKFULLCASE: |  C1-Pair: not covered
+// CHECKFULLCASE-NEXT: |  C2-Pair: constant folded
+// CHECKFULLCASE-NEXT: |  C3-Pair: not covered
+// CHECKFULLCASE: |  MC/DC Coverage for Decision: 0.00%
+// CHECKFULLCASE: |  1 { C,  F,  T  = T      }
+// CHECKFULLCASE-NEXT: |  2 { C,  T,  -  = 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: |  1 { T,  C,  -  = T      }
+// CHECKFULLCASE-NEXT: |  2 { F,  C,  T  = T      }
+// CHECKFULLCASE: |  C1-Pair: not covered
+// CHECKFULLCASE-NEXT: |  C2-Pair: constant folded
+// CHECKFULLCASE-NEXT: |  C3-Pair: not covered
+// CHECKFULLCASE: |  MC/DC Coverage for Decision: 0.00%
+// 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-NEXT: |  C3-Pair: constant folded
+// CHECKFULLCASE: |  MC/DC Coverage for Decision: 0.00%
+// CHECKFULLCASE: |  1 { F,  C,  -  = F      }
+// CHECKFULLCASE-NEXT: |  2 { T,  C,  -  = F      }
+// CHECKFULLCASE: |  C1-Pair: not covered
+// CHECKFULLCASE-NEXT: |  C2-Pair: constant folded
+// CHECKFULLCASE-NEXT: |  C3-Pair: not covered
+// CHECKFULLCASE: |  MC/DC Coverage for Decision: 0.00%
+// CHECKFULLCASE: |  1 { F,  -,  C  = F      }
+// CHECKFULLCASE-NEXT: |  2 { T,  F,  C  = F      }
+// CHECKFULLCASE-NEXT: |  3 { T,  T,  C  = T      }
+// CHECKFULLCASE: |  C1-Pair: covered: (1,3)
+// CHECKFULLCASE-NEXT: |  C2-Pair: covered: (2,3)
+// CHECKFULLCASE-NEXT: |  C3-Pair: constant folded
+// CHECKFULLCASE: |  MC/DC Coverage for Decision: 100.00%
+// CHECKFULLCASE: |  1 { F,  C,  -  = F      }
+// CHECKFULLCASE-NEXT: |  2 { T,  C,  F  = F      }
+// CHECKFULLCASE-NEXT: |  3 { T,  C,  T  = T      }
+// CHECKFULLCASE: |  C1-Pair: covered: (1,3)
+// CHECKFULLCASE-NEXT: |  C2-Pair: constant folded
+// CHECKFULLCASE-NEXT: |  C3-Pair: covered: (2,3)
+// CHECKFULLCASE: |  MC/DC Coverage for Decision: 100.00%
+// CHECKFULLCASE: |  1 { T,  -,  C  = T      }
+// CHECKFULLCASE-NEXT: |  2 { F,  T,  C  = T      }
+// CHECKFULLCASE: |  C1-Pair: not covered
+// CHECKFULLCASE-NEXT: |  C2-Pair: not covered
+// CHECKFULLCASE-NEXT: |  C3-Pair: constant folded
+// CHECKFULLCASE: |  MC/DC Coverage for Decision: 0.00%
+// CHECKFULLCASE: |  1 { T,  C,  -  = T      }
+// CHECKFULLCASE-NEXT: |  2 { F,  C,  -  = T      }
+// CHECKFULLCASE: |  C1-Pair: not covered
+// CHECKFULLCASE-NEXT: |  C2-Pair: constant folded
+// CHECKFULLCASE-NEXT: |  C3-Pair: not covered
+// CHECKFULLCASE: |  MC/DC Coverage for Decision: 0.00%
+// CHECKFULLCASE: |  1 { T,  -,  C  = T      }
+// CHECKFULLCASE-NEXT: |  2 { F,  T,  C  = T      }
+// CHECKFULLCASE: |  C1-Pair: not covered
+// CHECKFULLCASE-NEXT: |  C2-Pair: not covered
+// CHECKFULLCASE-NEXT: |  C3-Pair: constant folded
+// CHECKFULLCASE: |  MC/DC Coverage for Decision: 0.00%
+// CHECKFULLCASE: |  1 { T,  C,  -  = T      }
+// CHECKFULLCASE-NEXT: |  2 { F,  C,  T  = T      }
+// CHECKFULLCASE: |  C1-Pair: not covered
+// CHECKFULLCASE-NEXT: |  C2-Pair: constant folded
+// 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-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: _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: _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: _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: _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: _Z5casembb {{.*}} 2       2   0.00%
+// REPORT-NEXT: _Z5casenbb {{.*}} 2       2   0.00%
+// REPORT: TOTAL {{.*}} 40      28  30.00%
+
+Instructions for regenerating the test:
+
+# cd %S/Inputs
+cp mcdc-const.cpp /tmp
+cp mcdc-const-folding.cpp /tmp
+
+clang -fcoverage-mcdc -fprofile-instr-generate -fcoverage-compilation-dir=. \
+    -fcoverage-mapping /tmp/mcdc-const.cpp -o /tmp/mcdc-const.o
+
+clang -fcoverage-mcdc -fprofile-instr-generate -fcoverage-compilation-dir=. \
+    -fcoverage-mapping /tmp/mcdc-const-folding.cpp -o /tmp/mcdc-const-folding.o
+
+mv /tmp/mcdc-const.o %S/Inputs
+mv /tmp/mcdc-const-folding.o %S/Inputs

diff  --git a/llvm/test/tools/llvm-cov/mcdc-export-json.test b/llvm/test/tools/llvm-cov/mcdc-export-json.test
new file mode 100644
index 00000000000000..c2cebf52db8cc5
--- /dev/null
+++ b/llvm/test/tools/llvm-cov/mcdc-export-json.test
@@ -0,0 +1,18 @@
+// RUN: llvm-profdata merge %S/Inputs/mcdc-general.proftext -o %t.profdata
+// RUN: llvm-cov export --format=text %S/Inputs/mcdc-general.o -instr-profile %t.profdata | FileCheck %s
+
+// CHECK: 12,7,12,27,0,5,[true,true,true,true]
+// CHECK: 15,7,15,13,0,5,[true,true]
+// CHECK: 15,19,15,25,0,5,[true,false]
+// CHECK: 18,7,19,15,0,5,[true,true,false,true]
+// CHECK: "mcdc":{"count":12,"covered":10,"notcovered":2,"percent":83.333333333333343}
+
+Instructions for regenerating the test:
+
+# cd %S/Inputs
+cp mcdc-general.cpp /tmp
+
+clang -fcoverage-mcdc -fprofile-instr-generate -fcoverage-compilation-dir=. \
+    -fcoverage-mapping /tmp/mcdc-general.cpp -o /tmp/mcdc-const.o
+
+mv /tmp/mcdc-general.o %S/Inputs

diff  --git a/llvm/test/tools/llvm-cov/mcdc-general-none.test b/llvm/test/tools/llvm-cov/mcdc-general-none.test
new file mode 100644
index 00000000000000..bcf8f3cbd05d45
--- /dev/null
+++ b/llvm/test/tools/llvm-cov/mcdc-general-none.test
@@ -0,0 +1,82 @@
+// Test visualization of general MC/DC constructs with 0 executed test vectors.
+
+// RUN: llvm-profdata merge %S/Inputs/mcdc-general-none.proftext -o %t.profdata
+// RUN: llvm-cov show --show-mcdc %S/Inputs/mcdc-general.o -instr-profile %t.profdata -path-equivalence=/tmp,%S/Inputs %S/Inputs/mcdc-general.cpp | FileCheck %s
+// RUN: llvm-cov report --show-mcdc-summary %S/Inputs/mcdc-general.o -instr-profile %t.profdata -show-functions -path-equivalence=/tmp,%S/Inputs %S/Inputs/mcdc-general.cpp | FileCheck %s -check-prefix=REPORT
+
+// CHECK: test(bool
+
+// CHECK:  ------------------
+// CHECK-NEXT:  |---> MC/DC Decision Region (12:7) to (12:27)
+// CHECK-NEXT:  |
+// CHECK-NEXT:  |  Number of Conditions: 4
+// CHECK-NEXT:  |     Condition C1 --> (12:8)
+// CHECK-NEXT:  |     Condition C2 --> (12:13)
+// CHECK-NEXT:  |     Condition C3 --> (12:20)
+// CHECK-NEXT:  |     Condition C4 --> (12:25)
+// CHECK-NEXT:  |
+// CHECK-NEXT:  |  Executed MC/DC Test Vectors:
+// CHECK-NEXT:  |
+// CHECK-NEXT:  |  None.
+// CHECK-NEXT:  |
+// CHECK-NEXT:  |  C1-Pair: not covered
+// CHECK-NEXT:  |  C2-Pair: not covered
+// CHECK-NEXT:  |  C3-Pair: not covered
+// CHECK-NEXT:  |  C4-Pair: not covered
+// CHECK-NEXT:  |  MC/DC Coverage for Decision: 0.00%
+// CHECK-NEXT:  |
+// CHECK-NEXT:  ------------------
+
+
+// Turn off MC/DC visualization.
+// RUN: llvm-cov show %S/Inputs/mcdc-general.o -instr-profile %t.profdata -path-equivalence=/tmp,%S/Inputs %S/Inputs/mcdc-general.cpp | FileCheck %s -check-prefix=NOMCDC
+// NOMCDC-NOT: MC/DC Decision Region
+
+// REPORT: Name                        Regions    Miss   Cover     Lines    Miss   Cover  Branches    Miss   Cover    MC/DC Conditions    Miss   Cover
+// REPORT-NEXT: -------------------------------------------------------------------------------------------------------------------------------------------
+// REPORT-NEXT: _Z4testbbbb                      25       0 100.00%         9       0 100.00%        24       2  91.67%                  12      12   0.00%
+// REPORT-NEXT: main                              1       0 100.00%        11       0 100.00%         0       0   0.00%                   0       0   0.00%
+// REPORT-NEXT: ---
+// REPORT-NEXT: TOTAL                            26       0 100.00%        20       0 100.00%        24       2  91.67%                  12      12   0.00%
+
+// Turn off MC/DC summary.
+// RUN: llvm-cov report %S/Inputs/mcdc-general.o -instr-profile %t.profdata -show-functions -path-equivalence=/tmp,%S/Inputs %S/Inputs/mcdc-general.cpp | FileCheck %s -check-prefix=REPORT_NOMCDC
+// REPORT_NOMCDC-NOT: TOTAL{{.*}}12                   12    0.00%
+
+
+// Test file-level report.
+// RUN: llvm-cov report --show-mcdc-summary %S/Inputs/mcdc-general.o -instr-profile %t.profdata -path-equivalence=/tmp,%S/Inputs %S/Inputs/mcdc-general.cpp | FileCheck %s -check-prefix=FILEREPORT
+// FILEREPORT: TOTAL{{.*}}12                   12    0.00%
+
+
+// Test html output.
+// RUN: llvm-cov show --show-mcdc-summary --show-mcdc %S/Inputs/mcdc-general.o -instr-profile %t.profdata -path-equivalence=/tmp,%S/Inputs %S/Inputs/mcdc-general.cpp -format html -o %t.html.dir
+// RUN: FileCheck -check-prefix=HTML -input-file=%t.html.dir/coverage/tmp/mcdc-general.cpp.html %s
+// HTML-COUNT-4: MC/DC Decision Region (<span class='line-number'><a name='L{{[0-9]+}}' href='#L{{[0-9]+}}'><span>
+
+// RUN: FileCheck -check-prefix HTML-INDEX -input-file %t.html.dir/index.html %s
+// HTML-INDEX-LABEL: <table>
+// HTML-INDEX: <td class='column-entry-bold'>Filename</td>
+// HTML-INDEX: <td class='column-entry-bold'>Function Coverage</td>
+// HTML-INDEX: <td class='column-entry-bold'>Line Coverage</td>
+// HTML-INDEX: <td class='column-entry-bold'>Region Coverage</td>
+// HTML-INDEX: <td class='column-entry-bold'>Branch Coverage</td>
+// HTML-INDEX: <td class='column-entry-bold'>MC/DC</td>
+// HTML-INDEX: <a href='coverage{{.*}}mcdc-general.cpp.html'{{.*}}mcdc-general.cpp</a>
+// HTML-INDEX: <td class='column-entry-green'>
+// HTML-INDEX: 100.00% (2/2)
+// HTML-INDEX: 100.00% (20/20)
+// HTML-INDEX: 100.00% (26/26)
+// HTML-INDEX: 91.67% (22/24)
+// HTML-INDEX: 0.00% (0/12)
+// HTML-INDEX: Totals
+
+Instructions for regenerating the test:
+
+# cd %S/Inputs
+cp mcdc-general.cpp /tmp
+
+clang -fcoverage-mcdc -fprofile-instr-generate -fcoverage-compilation-dir=. \
+    -fcoverage-mapping /tmp/mcdc-general.cpp -o /tmp/mcdc-general.o
+
+mv /tmp/mcdc-general.o %S/Inputs

diff  --git a/llvm/test/tools/llvm-cov/mcdc-general.test b/llvm/test/tools/llvm-cov/mcdc-general.test
new file mode 100644
index 00000000000000..588aed09c16a5e
--- /dev/null
+++ b/llvm/test/tools/llvm-cov/mcdc-general.test
@@ -0,0 +1,148 @@
+// Test visualization of general MC/DC constructs.
+
+// RUN: llvm-profdata merge %S/Inputs/mcdc-general.proftext -o %t.profdata
+// RUN: llvm-cov show --show-mcdc %S/Inputs/mcdc-general.o -instr-profile %t.profdata -path-equivalence=/tmp,%S/Inputs %S/Inputs/mcdc-general.cpp | FileCheck %s
+// RUN: llvm-cov report --show-mcdc-summary %S/Inputs/mcdc-general.o -instr-profile %t.profdata -show-functions -path-equivalence=/tmp,%S/Inputs %S/Inputs/mcdc-general.cpp | FileCheck %s -check-prefix=REPORT
+
+// CHECK: test(bool
+
+// CHECK:  ------------------
+// CHECK-NEXT:  |---> MC/DC Decision Region (12:7) to (12:27)
+// CHECK-NEXT:  |
+// CHECK-NEXT:  |  Number of Conditions: 4
+// CHECK-NEXT:  |     Condition C1 --> (12:8)
+// CHECK-NEXT:  |     Condition C2 --> (12:13)
+// CHECK-NEXT:  |     Condition C3 --> (12:20)
+// CHECK-NEXT:  |     Condition C4 --> (12:25)
+// CHECK-NEXT:  |
+// CHECK-NEXT:  |  Executed MC/DC Test Vectors:
+// CHECK-NEXT:  |
+// CHECK-NEXT:  |     C1, C2, C3, C4    Result
+// CHECK-NEXT:  |  1 { F,  -,  F,  -  = F      }
+// CHECK-NEXT:  |  2 { T,  F,  F,  -  = F      }
+// CHECK-NEXT:  |  3 { F,  -,  T,  F  = F      }
+// CHECK-NEXT:  |  4 { T,  F,  T,  F  = F      }
+// CHECK-NEXT:  |  5 { T,  T,  -,  -  = T      }
+// CHECK-NEXT:  |  6 { T,  F,  T,  T  = T      }
+// CHECK-NEXT:  |
+// CHECK-NEXT:  |  C1-Pair: covered: (1,5)
+// CHECK-NEXT:  |  C2-Pair: covered: (2,5)
+// CHECK-NEXT:  |  C3-Pair: covered: (2,6)
+// CHECK-NEXT:  |  C4-Pair: covered: (4,6)
+// CHECK-NEXT:  |  MC/DC Coverage for Decision: 100.00%
+// CHECK-NEXT:  |
+// CHECK-NEXT:  ------------------
+
+// CHECK:  ------------------
+// CHECK-NEXT:  |---> MC/DC Decision Region (15:7) to (15:13)
+// CHECK-NEXT:  |
+// CHECK-NEXT:  |  Number of Conditions: 2
+// CHECK-NEXT:  |     Condition C1 --> (15:7)
+// CHECK-NEXT:  |     Condition C2 --> (15:12)
+// CHECK-NEXT:  |
+// CHECK-NEXT:  |  Executed MC/DC Test Vectors:
+// CHECK-NEXT:  |
+// CHECK-NEXT:  |     C1, C2    Result
+// CHECK-NEXT:  |  1 { F,  -  = F      }
+// CHECK-NEXT:  |  2 { T,  F  = F      }
+// CHECK-NEXT:  |  3 { T,  T  = T      }
+// CHECK-NEXT:  |
+// CHECK-NEXT:  |  C1-Pair: covered: (1,3)
+// CHECK-NEXT:  |  C2-Pair: covered: (2,3)
+// CHECK-NEXT:  |  MC/DC Coverage for Decision: 100.00%
+// CHECK-NEXT:  |
+// CHECK-NEXT:  |---> MC/DC Decision Region (15:19) to (15:25)
+// CHECK-NEXT:  |
+// CHECK-NEXT:  |  Number of Conditions: 2
+// CHECK-NEXT:  |     Condition C1 --> (15:19)
+// CHECK-NEXT:  |     Condition C2 --> (15:24)
+// CHECK-NEXT:  |
+// CHECK-NEXT:  |  Executed MC/DC Test Vectors:
+// CHECK-NEXT:  |
+// CHECK-NEXT:  |     C1, C2    Result
+// CHECK-NEXT:  |  1 { F,  -  = F      }
+// CHECK-NEXT:  |  2 { T,  T  = T      }
+// CHECK-NEXT:  |
+// CHECK-NEXT:  |  C1-Pair: covered: (1,2)
+// CHECK-NEXT:  |  C2-Pair: not covered
+// CHECK-NEXT:  |  MC/DC Coverage for Decision: 50.00%
+// CHECK-NEXT:  |
+// CHECK-NEXT:  ------------------
+
+// CHECK:  ------------------
+// CHECK-NEXT:  |---> MC/DC Decision Region (18:7) to (19:15)
+// CHECK-NEXT:  |
+// CHECK-NEXT:  |  Number of Conditions: 4
+// CHECK-NEXT:  |     Condition C1 --> (18:8)
+// CHECK-NEXT:  |     Condition C2 --> (18:13)
+// CHECK-NEXT:  |     Condition C3 --> (19:8)
+// CHECK-NEXT:  |     Condition C4 --> (19:13)
+// CHECK-NEXT:  |
+// CHECK-NEXT:  |  Executed MC/DC Test Vectors:
+// CHECK-NEXT:  |
+// CHECK-NEXT:  |     C1, C2, C3, C4    Result
+// CHECK-NEXT:  |  1 { F,  -,  -,  -  = F      }
+// CHECK-NEXT:  |  2 { T,  F,  -,  -  = F      }
+// CHECK-NEXT:  |  3 { T,  T,  T,  F  = F      }
+// CHECK-NEXT:  |  4 { T,  T,  T,  T  = T      }
+// CHECK-NEXT:  |
+// CHECK-NEXT:  |  C1-Pair: covered: (1,4)
+// CHECK-NEXT:  |  C2-Pair: covered: (2,4)
+// CHECK-NEXT:  |  C3-Pair: not covered
+// CHECK-NEXT:  |  C4-Pair: covered: (3,4)
+// CHECK-NEXT:  |  MC/DC Coverage for Decision: 75.00%
+// CHECK-NEXT:  |
+// CHECK-NEXT:  ------------------
+
+// Turn off MC/DC visualization.
+// RUN: llvm-cov show %S/Inputs/mcdc-general.o -instr-profile %t.profdata -path-equivalence=/tmp,%S/Inputs %S/Inputs/mcdc-general.cpp | FileCheck %s -check-prefix=NOMCDC
+// NOMCDC-NOT: MC/DC Decision Region
+
+// REPORT: Name                        Regions    Miss   Cover     Lines    Miss   Cover  Branches    Miss   Cover    MC/DC Conditions    Miss   Cover
+// REPORT-NEXT: -------------------------------------------------------------------------------------------------------------------------------------------
+// REPORT-NEXT: _Z4testbbbb                      25       0 100.00%         9       0 100.00%        24       2  91.67%                  12       2  83.33%
+// REPORT-NEXT: main                              1       0 100.00%        11       0 100.00%         0       0   0.00%                   0       0   0.00%
+// REPORT-NEXT: ---
+// REPORT-NEXT: TOTAL                            26       0 100.00%        20       0 100.00%        24       2  91.67%                  12       2  83.33%
+
+// Turn off MC/DC summary.
+// RUN: llvm-cov report %S/Inputs/mcdc-general.o -instr-profile %t.profdata -show-functions -path-equivalence=/tmp,%S/Inputs %S/Inputs/mcdc-general.cpp | FileCheck %s -check-prefix=REPORT_NOMCDC
+// REPORT_NOMCDC-NOT: TOTAL{{.*}}12                    2    83.33%
+
+
+// Test file-level report.
+// RUN: llvm-cov report --show-mcdc-summary %S/Inputs/mcdc-general.o -instr-profile %t.profdata -path-equivalence=/tmp,%S/Inputs %S/Inputs/mcdc-general.cpp | FileCheck %s -check-prefix=FILEREPORT
+// FILEREPORT: TOTAL{{.*}}12                    2    83.33%
+
+
+// Test html output.
+// RUN: llvm-cov show --show-mcdc-summary --show-mcdc %S/Inputs/mcdc-general.o -instr-profile %t.profdata -path-equivalence=/tmp,%S/Inputs %S/Inputs/mcdc-general.cpp -format html -o %t.html.dir
+// RUN: FileCheck -check-prefix=HTML -input-file=%t.html.dir/coverage/tmp/mcdc-general.cpp.html %s
+// HTML-COUNT-4: MC/DC Decision Region (<span class='line-number'><a name='L{{[0-9]+}}' href='#L{{[0-9]+}}'><span>
+
+// RUN: FileCheck -check-prefix HTML-INDEX -input-file %t.html.dir/index.html %s
+// HTML-INDEX-LABEL: <table>
+// HTML-INDEX: <td class='column-entry-bold'>Filename</td>
+// HTML-INDEX: <td class='column-entry-bold'>Function Coverage</td>
+// HTML-INDEX: <td class='column-entry-bold'>Line Coverage</td>
+// HTML-INDEX: <td class='column-entry-bold'>Region Coverage</td>
+// HTML-INDEX: <td class='column-entry-bold'>Branch Coverage</td>
+// HTML-INDEX: <td class='column-entry-bold'>MC/DC</td>
+// HTML-INDEX: <a href='coverage{{.*}}mcdc-general.cpp.html'{{.*}}mcdc-general.cpp</a>
+// HTML-INDEX: <td class='column-entry-green'>
+// HTML-INDEX: 100.00% (2/2)
+// HTML-INDEX: 100.00% (20/20)
+// HTML-INDEX: 100.00% (26/26)
+// HTML-INDEX: 91.67% (22/24)
+// HTML-INDEX: 83.33% (10/12)
+// HTML-INDEX: Totals
+
+Instructions for regenerating the test:
+
+# cd %S/Inputs
+cp mcdc-general.cpp /tmp
+
+clang -fcoverage-mcdc -fprofile-instr-generate -fcoverage-compilation-dir=. \
+    -fcoverage-mapping /tmp/mcdc-general.cpp -o /tmp/mcdc-general.o
+
+mv /tmp/mcdc-general.o %S/Inputs

diff  --git a/llvm/tools/llvm-cov/CodeCoverage.cpp b/llvm/tools/llvm-cov/CodeCoverage.cpp
index c1e3e2c4d1e294..6405bb15e70c4d 100644
--- a/llvm/tools/llvm-cov/CodeCoverage.cpp
+++ b/llvm/tools/llvm-cov/CodeCoverage.cpp
@@ -105,6 +105,11 @@ class CodeCoverageTool {
                             const MemoryBuffer &File,
                             CoverageData &CoverageInfo);
 
+  /// Create source views for the MCDC records.
+  void attachMCDCSubViews(SourceCoverageView &View, StringRef SourceName,
+                          ArrayRef<MCDCRecord> MCDCRecords,
+                          const MemoryBuffer &File, CoverageData &CoverageInfo);
+
   /// Create the source view of a particular function.
   std::unique_ptr<SourceCoverageView>
   createFunctionView(const FunctionRecord &Function,
@@ -352,6 +357,37 @@ void CodeCoverageTool::attachBranchSubViews(SourceCoverageView &View,
   }
 }
 
+void CodeCoverageTool::attachMCDCSubViews(SourceCoverageView &View,
+                                          StringRef SourceName,
+                                          ArrayRef<MCDCRecord> MCDCRecords,
+                                          const MemoryBuffer &File,
+                                          CoverageData &CoverageInfo) {
+  if (!ViewOpts.ShowMCDC)
+    return;
+
+  const auto *NextRecord = MCDCRecords.begin();
+  const auto *EndRecord = MCDCRecords.end();
+
+  // Group and process MCDC records that have the same line number into the
+  // same subview.
+  while (NextRecord != EndRecord) {
+    std::vector<MCDCRecord> ViewMCDCRecords;
+    unsigned CurrentLine = NextRecord->getDecisionRegion().LineEnd;
+
+    while (NextRecord != EndRecord &&
+           CurrentLine == NextRecord->getDecisionRegion().LineEnd) {
+      ViewMCDCRecords.push_back(*NextRecord++);
+    }
+
+    if (ViewMCDCRecords.empty())
+      continue;
+
+    auto SubView = SourceCoverageView::create(SourceName, File, ViewOpts,
+                                              std::move(CoverageInfo));
+    View.addMCDCRecord(CurrentLine, ViewMCDCRecords, std::move(SubView));
+  }
+}
+
 std::unique_ptr<SourceCoverageView>
 CodeCoverageTool::createFunctionView(const FunctionRecord &Function,
                                      const CoverageMapping &Coverage) {
@@ -364,12 +400,15 @@ CodeCoverageTool::createFunctionView(const FunctionRecord &Function,
 
   auto Branches = FunctionCoverage.getBranches();
   auto Expansions = FunctionCoverage.getExpansions();
+  auto MCDCRecords = FunctionCoverage.getMCDCRecords();
   auto View = SourceCoverageView::create(DC.demangle(Function.Name),
                                          SourceBuffer.get(), ViewOpts,
                                          std::move(FunctionCoverage));
   attachExpansionSubViews(*View, Expansions, Coverage);
   attachBranchSubViews(*View, DC.demangle(Function.Name), Branches,
                        SourceBuffer.get(), FunctionCoverage);
+  attachMCDCSubViews(*View, DC.demangle(Function.Name), MCDCRecords,
+                     SourceBuffer.get(), FunctionCoverage);
 
   return View;
 }
@@ -386,11 +425,14 @@ CodeCoverageTool::createSourceFileView(StringRef SourceFile,
 
   auto Branches = FileCoverage.getBranches();
   auto Expansions = FileCoverage.getExpansions();
+  auto MCDCRecords = FileCoverage.getMCDCRecords();
   auto View = SourceCoverageView::create(SourceFile, SourceBuffer.get(),
                                          ViewOpts, std::move(FileCoverage));
   attachExpansionSubViews(*View, Expansions, Coverage);
   attachBranchSubViews(*View, SourceFile, Branches, SourceBuffer.get(),
                        FileCoverage);
+  attachMCDCSubViews(*View, SourceFile, MCDCRecords, SourceBuffer.get(),
+                     FileCoverage);
   if (!ViewOpts.ShowFunctionInstantiations)
     return View;
 
@@ -408,11 +450,14 @@ CodeCoverageTool::createSourceFileView(StringRef SourceFile,
         auto SubViewCoverage = Coverage.getCoverageForFunction(*Function);
         auto SubViewExpansions = SubViewCoverage.getExpansions();
         auto SubViewBranches = SubViewCoverage.getBranches();
+        auto SubViewMCDCRecords = SubViewCoverage.getMCDCRecords();
         SubView = SourceCoverageView::create(
             Funcname, SourceBuffer.get(), ViewOpts, std::move(SubViewCoverage));
         attachExpansionSubViews(*SubView, SubViewExpansions, Coverage);
         attachBranchSubViews(*SubView, SourceFile, SubViewBranches,
                              SourceBuffer.get(), SubViewCoverage);
+        attachMCDCSubViews(*SubView, SourceFile, SubViewMCDCRecords,
+                           SourceBuffer.get(), SubViewCoverage);
       }
 
       unsigned FileID = Function->CountedRegions.front().FileID;
@@ -752,6 +797,10 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
       cl::desc("Show branch condition statistics in summary table"),
       cl::init(true));
 
+  cl::opt<bool> MCDCSummary("show-mcdc-summary", cl::Optional,
+                            cl::desc("Show MCDC statistics in summary table"),
+                            cl::init(false));
+
   cl::opt<bool> InstantiationSummary(
       "show-instantiation-summary", cl::Optional,
       cl::desc("Show instantiation statistics in summary table"));
@@ -923,6 +972,7 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
       ::exit(0);
     }
 
+    ViewOpts.ShowMCDCSummary = MCDCSummary;
     ViewOpts.ShowBranchSummary = BranchSummary;
     ViewOpts.ShowRegionSummary = RegionSummary;
     ViewOpts.ShowInstantiationSummary = InstantiationSummary;
@@ -968,6 +1018,11 @@ int CodeCoverageTool::doShow(int argc, const char **argv,
                             "percent", "Show True/False percent")),
       cl::init(CoverageViewOptions::BranchOutputType::Off));
 
+  cl::opt<bool> ShowMCDC(
+      "show-mcdc", cl::Optional,
+      cl::desc("Show the MCDC Coverage for each applicable boolean expression"),
+      cl::cat(ViewCategory));
+
   cl::opt<bool> ShowBestLineRegionsCounts(
       "show-line-counts-or-regions", cl::Optional,
       cl::desc("Show the execution counts for each line, or the execution "
@@ -1063,6 +1118,7 @@ int CodeCoverageTool::doShow(int argc, const char **argv,
   ViewOpts.ShowExpandedRegions = ShowExpansions;
   ViewOpts.ShowBranchCounts =
       ShowBranches == CoverageViewOptions::BranchOutputType::Count;
+  ViewOpts.ShowMCDC = ShowMCDC;
   ViewOpts.ShowBranchPercents =
       ShowBranches == CoverageViewOptions::BranchOutputType::Percent;
   ViewOpts.ShowFunctionInstantiations = ShowInstantiations;

diff  --git a/llvm/tools/llvm-cov/CoverageExporterJson.cpp b/llvm/tools/llvm-cov/CoverageExporterJson.cpp
index 9e43377e38a8a6..a424bbe06e0ecd 100644
--- a/llvm/tools/llvm-cov/CoverageExporterJson.cpp
+++ b/llvm/tools/llvm-cov/CoverageExporterJson.cpp
@@ -20,6 +20,8 @@
 //       -- File: dict => Coverage for a single file
 //         -- Branches: array => List of Branches in the file
 //           -- Branch: dict => Describes a branch of the file with counters
+//         -- MCDC Records: array => List of MCDC records in the file
+//           -- MCDC Values: array => List of T/F covered condition values
 //         -- Segments: array => List of Segments contained in the file
 //           -- Segment: dict => Describes a segment of the file with a counter
 //         -- Expansions: array => List of expansion records
@@ -34,6 +36,7 @@
 //           -- FunctionCoverage: dict => Object summarizing function coverage
 //           -- RegionCoverage: dict => Object summarizing region coverage
 //           -- BranchCoverage: dict => Object summarizing branch coverage
+//           -- MCDCCoverage: dict => Object summarizing MC/DC coverage
 //     -- Functions: array => List of objects describing coverage for functions
 //       -- Function: dict => Coverage info for a single function
 //         -- Filenames: array => List of filenames that the function relates to
@@ -43,6 +46,7 @@
 //     -- InstantiationCoverage: dict => Object summarizing inst. coverage
 //     -- RegionCoverage: dict => Object summarizing region coverage
 //     -- BranchCoverage: dict => Object summarizing branch coverage
+//     -- MCDCCoverage: dict => Object summarizing MC/DC coverage
 //
 //===----------------------------------------------------------------------===//
 
@@ -97,6 +101,20 @@ json::Array renderBranch(const coverage::CountedRegion &Region) {
        Region.ExpandedFileID, int64_t(Region.Kind)});
 }
 
+json::Array gatherConditions(const coverage::MCDCRecord &Record) {
+  json::Array Conditions;
+  for (unsigned c = 0; c < Record.getNumConditions(); c++)
+    Conditions.push_back(Record.isConditionIndependencePairCovered(c));
+  return Conditions;
+}
+
+json::Array renderMCDCRecord(const coverage::MCDCRecord &Record) {
+  const llvm::coverage::CounterMappingRegion &CMR = Record.getDecisionRegion();
+  return json::Array({CMR.LineStart, CMR.ColumnStart, CMR.LineEnd,
+                      CMR.ColumnEnd, CMR.ExpandedFileID, int64_t(CMR.Kind),
+                      gatherConditions(Record)});
+}
+
 json::Array renderRegions(ArrayRef<coverage::CountedRegion> Regions) {
   json::Array RegionArray;
   for (const auto &Region : Regions)
@@ -112,6 +130,13 @@ json::Array renderBranchRegions(ArrayRef<coverage::CountedRegion> Regions) {
   return RegionArray;
 }
 
+json::Array renderMCDCRecords(ArrayRef<coverage::MCDCRecord> Records) {
+  json::Array RecordArray;
+  for (auto &Record : Records)
+    RecordArray.push_back(renderMCDCRecord(Record));
+  return RecordArray;
+}
+
 std::vector<llvm::coverage::CountedRegion>
 collectNestedBranches(const coverage::CoverageMapping &Coverage,
                       ArrayRef<llvm::coverage::ExpansionRecord> Expansions) {
@@ -178,7 +203,14 @@ json::Object renderSummary(const FileCoverageSummary &Summary) {
              {"covered", int64_t(Summary.BranchCoverage.getCovered())},
              {"notcovered", int64_t(Summary.BranchCoverage.getNumBranches() -
                                     Summary.BranchCoverage.getCovered())},
-             {"percent", Summary.BranchCoverage.getPercentCovered()}})}});
+             {"percent", Summary.BranchCoverage.getPercentCovered()}})},
+       {"mcdc",
+        json::Object(
+            {{"count", int64_t(Summary.MCDCCoverage.getNumPairs())},
+             {"covered", int64_t(Summary.MCDCCoverage.getCoveredPairs())},
+             {"notcovered", int64_t(Summary.MCDCCoverage.getNumPairs() -
+                                    Summary.MCDCCoverage.getCoveredPairs())},
+             {"percent", Summary.MCDCCoverage.getPercentCovered()}})}});
 }
 
 json::Array renderFileExpansions(const coverage::CoverageMapping &Coverage,
@@ -206,6 +238,14 @@ json::Array renderFileBranches(const coverage::CoverageData &FileCoverage,
   return BranchArray;
 }
 
+json::Array renderFileMCDC(const coverage::CoverageData &FileCoverage,
+                           const FileCoverageSummary &FileReport) {
+  json::Array MCDCRecordArray;
+  for (const auto &Record : FileCoverage.getMCDCRecords())
+    MCDCRecordArray.push_back(renderMCDCRecord(Record));
+  return MCDCRecordArray;
+}
+
 json::Object renderFile(const coverage::CoverageMapping &Coverage,
                         const std::string &Filename,
                         const FileCoverageSummary &FileReport,
@@ -216,6 +256,7 @@ json::Object renderFile(const coverage::CoverageMapping &Coverage,
     auto FileCoverage = Coverage.getCoverageForFile(Filename);
     File["segments"] = renderFileSegments(FileCoverage, FileReport);
     File["branches"] = renderFileBranches(FileCoverage, FileReport);
+    File["mcdc_records"] = renderFileMCDC(FileCoverage, FileReport);
     if (!Options.SkipExpansions) {
       File["expansions"] =
           renderFileExpansions(Coverage, FileCoverage, FileReport);
@@ -264,6 +305,7 @@ json::Array renderFunctions(
                       {"count", clamp_uint64_to_int64(F.ExecutionCount)},
                       {"regions", renderRegions(F.CountedRegions)},
                       {"branches", renderBranchRegions(F.CountedBranchRegions)},
+                      {"mcdc_records", renderMCDCRecords(F.MCDCRecords)},
                       {"filenames", json::Array(F.Filenames)}}));
   return FunctionArray;
 }

diff  --git a/llvm/tools/llvm-cov/CoverageReport.cpp b/llvm/tools/llvm-cov/CoverageReport.cpp
index eae2653b85c80b..8cc073e4def8fc 100644
--- a/llvm/tools/llvm-cov/CoverageReport.cpp
+++ b/llvm/tools/llvm-cov/CoverageReport.cpp
@@ -86,9 +86,9 @@ Column column(StringRef Str, unsigned Width, const T &Value) {
 }
 
 // Specify the default column widths.
-size_t FileReportColumns[] = {25, 12, 18, 10, 12, 18, 10, 16,
-                              16, 10, 12, 18, 10, 12, 18, 10};
-size_t FunctionReportColumns[] = {25, 10, 8, 8, 10, 8, 8, 10, 8, 8};
+size_t FileReportColumns[] = {25, 12, 18, 10, 12, 18, 10, 16, 16, 10,
+                              12, 18, 10, 12, 18, 10, 20, 21, 10};
+size_t FunctionReportColumns[] = {25, 10, 8, 8, 10, 8, 8, 10, 8, 8, 20, 8, 8};
 
 /// Adjust column widths to fit long file paths and function names.
 void adjustColumnWidths(ArrayRef<StringRef> Files,
@@ -291,6 +291,22 @@ void CoverageReport::render(const FileCoverageSummary &File,
       OS << column("-", FileReportColumns[15], Column::RightAlignment);
   }
 
+  if (Options.ShowMCDCSummary) {
+    OS << format("%*u", FileReportColumns[16],
+                 (unsigned)File.MCDCCoverage.getNumPairs());
+    Options.colored_ostream(OS, LineCoverageColor)
+        << format("%*u", FileReportColumns[17],
+                  (unsigned)(File.MCDCCoverage.getNumPairs() -
+                             File.MCDCCoverage.getCoveredPairs()));
+    if (File.MCDCCoverage.getNumPairs())
+      Options.colored_ostream(OS, LineCoverageColor)
+          << format("%*.2f", FileReportColumns[18] - 1,
+                    File.MCDCCoverage.getPercentCovered())
+          << '%';
+    else
+      OS << column("-", FileReportColumns[18], Column::RightAlignment);
+  }
+
   OS << "\n";
 }
 
@@ -338,6 +354,19 @@ void CoverageReport::render(const FunctionCoverageSummary &Function,
                   Function.BranchCoverage.getPercentCovered())
         << '%';
   }
+  if (Options.ShowMCDCSummary) {
+    OS << format("%*u", FunctionReportColumns[10],
+                 (unsigned)Function.MCDCCoverage.getNumPairs());
+    Options.colored_ostream(OS, LineCoverageColor)
+        << format("%*u", FunctionReportColumns[11],
+                  (unsigned)(Function.MCDCCoverage.getNumPairs() -
+                             Function.MCDCCoverage.getCoveredPairs()));
+    Options.colored_ostream(
+        OS, determineCoveragePercentageColor(Function.MCDCCoverage))
+        << format("%*.2f", FunctionReportColumns[12] - 1,
+                  Function.MCDCCoverage.getPercentCovered())
+        << '%';
+  }
   OS << "\n";
 }
 
@@ -370,6 +399,11 @@ void CoverageReport::renderFunctionReports(ArrayRef<std::string> Files,
       OS << column("Branches", FunctionReportColumns[7], Column::RightAlignment)
          << column("Miss", FunctionReportColumns[8], Column::RightAlignment)
          << column("Cover", FunctionReportColumns[9], Column::RightAlignment);
+    if (Options.ShowMCDCSummary)
+      OS << column("MC/DC Conditions", FunctionReportColumns[10],
+                   Column::RightAlignment)
+         << column("Miss", FunctionReportColumns[11], Column::RightAlignment)
+         << column("Cover", FunctionReportColumns[12], Column::RightAlignment);
     OS << "\n";
     renderDivider(FunctionReportColumns, OS);
     OS << "\n";
@@ -380,6 +414,7 @@ void CoverageReport::renderFunctionReports(ArrayRef<std::string> Files,
       Totals.RegionCoverage += Function.RegionCoverage;
       Totals.LineCoverage += Function.LineCoverage;
       Totals.BranchCoverage += Function.BranchCoverage;
+      Totals.MCDCCoverage += Function.MCDCCoverage;
       render(Function, DC, OS);
     }
     if (Totals.ExecutionCount) {
@@ -502,6 +537,12 @@ void CoverageReport::renderFileReports(
        << column("Missed Branches", FileReportColumns[14],
                  Column::RightAlignment)
        << column("Cover", FileReportColumns[15], Column::RightAlignment);
+  if (Options.ShowMCDCSummary)
+    OS << column("MC/DC Conditions", FileReportColumns[16],
+                 Column::RightAlignment)
+       << column("Missed Conditions", FileReportColumns[17],
+                 Column::RightAlignment)
+       << column("Cover", FileReportColumns[18], Column::RightAlignment);
   OS << "\n";
   renderDivider(FileReportColumns, OS);
   OS << "\n";

diff  --git a/llvm/tools/llvm-cov/CoverageSummaryInfo.cpp b/llvm/tools/llvm-cov/CoverageSummaryInfo.cpp
index 10e059adeb7d86..4f150020ee3815 100644
--- a/llvm/tools/llvm-cov/CoverageSummaryInfo.cpp
+++ b/llvm/tools/llvm-cov/CoverageSummaryInfo.cpp
@@ -44,6 +44,21 @@ static void sumBranchExpansions(size_t &NumBranches, size_t &CoveredBranches,
   }
 }
 
+static std::pair<size_t, size_t>
+sumMCDCPairs(const ArrayRef<MCDCRecord> &Records) {
+  size_t NumPairs = 0, CoveredPairs = 0;
+  for (const auto &Record : Records) {
+    const auto NumConditions = Record.getNumConditions();
+    for (unsigned C = 0; C < NumConditions; C++) {
+      if (!Record.isCondFolded(C))
+        ++NumPairs;
+      if (Record.isConditionIndependencePairCovered(C))
+        ++CoveredPairs;
+    }
+  }
+  return {NumPairs, CoveredPairs};
+}
+
 FunctionCoverageSummary
 FunctionCoverageSummary::get(const CoverageMapping &CM,
                              const coverage::FunctionRecord &Function) {
@@ -73,11 +88,15 @@ FunctionCoverageSummary::get(const CoverageMapping &CM,
   sumBranches(NumBranches, CoveredBranches, CD.getBranches());
   sumBranchExpansions(NumBranches, CoveredBranches, CM, CD.getExpansions());
 
+  size_t NumPairs = 0, CoveredPairs = 0;
+  std::tie(NumPairs, CoveredPairs) = sumMCDCPairs(CD.getMCDCRecords());
+
   return FunctionCoverageSummary(
       Function.Name, Function.ExecutionCount,
       RegionCoverageInfo(CoveredRegions, NumCodeRegions),
       LineCoverageInfo(CoveredLines, NumLines),
-      BranchCoverageInfo(CoveredBranches, NumBranches));
+      BranchCoverageInfo(CoveredBranches, NumBranches),
+      MCDCCoverageInfo(CoveredPairs, NumPairs));
 }
 
 FunctionCoverageSummary
@@ -97,10 +116,12 @@ FunctionCoverageSummary::get(const InstantiationGroup &Group,
   Summary.RegionCoverage = Summaries[0].RegionCoverage;
   Summary.LineCoverage = Summaries[0].LineCoverage;
   Summary.BranchCoverage = Summaries[0].BranchCoverage;
+  Summary.MCDCCoverage = Summaries[0].MCDCCoverage;
   for (const auto &FCS : Summaries.drop_front()) {
     Summary.RegionCoverage.merge(FCS.RegionCoverage);
     Summary.LineCoverage.merge(FCS.LineCoverage);
     Summary.BranchCoverage.merge(FCS.BranchCoverage);
+    Summary.MCDCCoverage.merge(FCS.MCDCCoverage);
   }
   return Summary;
 }

diff  --git a/llvm/tools/llvm-cov/CoverageSummaryInfo.h b/llvm/tools/llvm-cov/CoverageSummaryInfo.h
index 46510dc9cf3faf..64c2c8406cf3eb 100644
--- a/llvm/tools/llvm-cov/CoverageSummaryInfo.h
+++ b/llvm/tools/llvm-cov/CoverageSummaryInfo.h
@@ -142,6 +142,47 @@ class BranchCoverageInfo {
   }
 };
 
+/// Provides information about MC/DC coverage for a function/file.
+class MCDCCoverageInfo {
+  /// The number of Independence Pairs that were covered.
+  size_t CoveredPairs;
+
+  /// The total number of Independence Pairs in a function/file.
+  size_t NumPairs;
+
+public:
+  MCDCCoverageInfo() : CoveredPairs(0), NumPairs(0) {}
+
+  MCDCCoverageInfo(size_t CoveredPairs, size_t NumPairs)
+      : CoveredPairs(CoveredPairs), NumPairs(NumPairs) {
+    assert(CoveredPairs <= NumPairs && "Covered pairs over-counted");
+  }
+
+  MCDCCoverageInfo &operator+=(const MCDCCoverageInfo &RHS) {
+    CoveredPairs += RHS.CoveredPairs;
+    NumPairs += RHS.NumPairs;
+    return *this;
+  }
+
+  void merge(const MCDCCoverageInfo &RHS) {
+    CoveredPairs = std::max(CoveredPairs, RHS.CoveredPairs);
+    NumPairs = std::max(NumPairs, RHS.NumPairs);
+  }
+
+  size_t getCoveredPairs() const { return CoveredPairs; }
+
+  size_t getNumPairs() const { return NumPairs; }
+
+  bool isFullyCovered() const { return CoveredPairs == NumPairs; }
+
+  double getPercentCovered() const {
+    assert(CoveredPairs <= NumPairs && "Covered pairs over-counted");
+    if (NumPairs == 0)
+      return 0.0;
+    return double(CoveredPairs) / double(NumPairs) * 100.0;
+  }
+};
+
 /// Provides information about function coverage for a file.
 class FunctionCoverageInfo {
   /// The number of functions that were executed.
@@ -189,6 +230,7 @@ struct FunctionCoverageSummary {
   RegionCoverageInfo RegionCoverage;
   LineCoverageInfo LineCoverage;
   BranchCoverageInfo BranchCoverage;
+  MCDCCoverageInfo MCDCCoverage;
 
   FunctionCoverageSummary(const std::string &Name)
       : Name(Name), ExecutionCount(0) {}
@@ -196,10 +238,11 @@ struct FunctionCoverageSummary {
   FunctionCoverageSummary(const std::string &Name, uint64_t ExecutionCount,
                           const RegionCoverageInfo &RegionCoverage,
                           const LineCoverageInfo &LineCoverage,
-                          const BranchCoverageInfo &BranchCoverage)
+                          const BranchCoverageInfo &BranchCoverage,
+                          const MCDCCoverageInfo &MCDCCoverage)
       : Name(Name), ExecutionCount(ExecutionCount),
         RegionCoverage(RegionCoverage), LineCoverage(LineCoverage),
-        BranchCoverage(BranchCoverage) {}
+        BranchCoverage(BranchCoverage), MCDCCoverage(MCDCCoverage) {}
 
   /// Compute the code coverage summary for the given function coverage
   /// mapping record.
@@ -219,6 +262,7 @@ struct FileCoverageSummary {
   RegionCoverageInfo RegionCoverage;
   LineCoverageInfo LineCoverage;
   BranchCoverageInfo BranchCoverage;
+  MCDCCoverageInfo MCDCCoverage;
   FunctionCoverageInfo FunctionCoverage;
   FunctionCoverageInfo InstantiationCoverage;
 
@@ -230,6 +274,7 @@ struct FileCoverageSummary {
     LineCoverage += RHS.LineCoverage;
     FunctionCoverage += RHS.FunctionCoverage;
     BranchCoverage += RHS.BranchCoverage;
+    MCDCCoverage += RHS.MCDCCoverage;
     InstantiationCoverage += RHS.InstantiationCoverage;
     return *this;
   }
@@ -238,6 +283,7 @@ struct FileCoverageSummary {
     RegionCoverage += Function.RegionCoverage;
     LineCoverage += Function.LineCoverage;
     BranchCoverage += Function.BranchCoverage;
+    MCDCCoverage += Function.MCDCCoverage;
     FunctionCoverage.addFunction(/*Covered=*/Function.ExecutionCount > 0);
   }
 

diff  --git a/llvm/tools/llvm-cov/CoverageViewOptions.h b/llvm/tools/llvm-cov/CoverageViewOptions.h
index eb852859e9cbe4..6925cffd8246d2 100644
--- a/llvm/tools/llvm-cov/CoverageViewOptions.h
+++ b/llvm/tools/llvm-cov/CoverageViewOptions.h
@@ -30,12 +30,14 @@ struct CoverageViewOptions {
   bool ShowLineNumbers;
   bool ShowLineStats;
   bool ShowRegionMarkers;
+  bool ShowMCDC;
   bool ShowBranchCounts;
   bool ShowBranchPercents;
   bool ShowExpandedRegions;
   bool ShowFunctionInstantiations;
   bool ShowFullFilenames;
   bool ShowBranchSummary;
+  bool ShowMCDCSummary;
   bool ShowRegionSummary;
   bool ShowInstantiationSummary;
   bool ShowDirectoryCoverage;

diff  --git a/llvm/tools/llvm-cov/SourceCoverageView.cpp b/llvm/tools/llvm-cov/SourceCoverageView.cpp
index 7480b7f628a0b9..c910edd1db7827 100644
--- a/llvm/tools/llvm-cov/SourceCoverageView.cpp
+++ b/llvm/tools/llvm-cov/SourceCoverageView.cpp
@@ -178,6 +178,12 @@ void SourceCoverageView::addBranch(unsigned Line,
   BranchSubViews.emplace_back(Line, Regions, std::move(View));
 }
 
+void SourceCoverageView::addMCDCRecord(
+    unsigned Line, ArrayRef<MCDCRecord> Records,
+    std::unique_ptr<SourceCoverageView> View) {
+  MCDCSubViews.emplace_back(Line, Records, std::move(View));
+}
+
 void SourceCoverageView::addInstantiation(
     StringRef FunctionName, unsigned Line,
     std::unique_ptr<SourceCoverageView> View) {
@@ -203,12 +209,15 @@ void SourceCoverageView::print(raw_ostream &OS, bool WholeFile,
   llvm::stable_sort(ExpansionSubViews);
   llvm::stable_sort(InstantiationSubViews);
   llvm::stable_sort(BranchSubViews);
+  llvm::stable_sort(MCDCSubViews);
   auto NextESV = ExpansionSubViews.begin();
   auto EndESV = ExpansionSubViews.end();
   auto NextISV = InstantiationSubViews.begin();
   auto EndISV = InstantiationSubViews.end();
   auto NextBRV = BranchSubViews.begin();
   auto EndBRV = BranchSubViews.end();
+  auto NextMSV = MCDCSubViews.begin();
+  auto EndMSV = MCDCSubViews.end();
 
   // Get the coverage information for the file.
   auto StartSegment = CoverageInfo.begin();
@@ -276,6 +285,11 @@ void SourceCoverageView::print(raw_ostream &OS, bool WholeFile,
       renderBranchView(OS, *NextBRV, ViewDepth + 1);
       RenderedSubView = true;
     }
+    for (; NextMSV != EndMSV && NextMSV->Line == LI.line_number(); ++NextMSV) {
+      renderViewDivider(OS, ViewDepth + 1);
+      renderMCDCView(OS, *NextMSV, ViewDepth + 1);
+      RenderedSubView = true;
+    }
     if (RenderedSubView)
       renderViewDivider(OS, ViewDepth + 1);
     renderLineSuffix(OS, ViewDepth);

diff  --git a/llvm/tools/llvm-cov/SourceCoverageView.h b/llvm/tools/llvm-cov/SourceCoverageView.h
index c07595f980727a..2e3fa8e627d83c 100644
--- a/llvm/tools/llvm-cov/SourceCoverageView.h
+++ b/llvm/tools/llvm-cov/SourceCoverageView.h
@@ -84,6 +84,23 @@ struct BranchView {
   }
 };
 
+/// A view that represents one or more MCDC regions on a given source line.
+struct MCDCView {
+  std::vector<MCDCRecord> Records;
+  std::unique_ptr<SourceCoverageView> View;
+  unsigned Line;
+
+  MCDCView(unsigned Line, ArrayRef<MCDCRecord> Records,
+           std::unique_ptr<SourceCoverageView> View)
+      : Records(Records), View(std::move(View)), Line(Line) {}
+
+  unsigned getLine() const { return Line; }
+
+  friend bool operator<(const MCDCView &LHS, const MCDCView &RHS) {
+    return LHS.Line < RHS.Line;
+  }
+};
+
 /// A file manager that handles format-aware file creation.
 class CoveragePrinter {
 public:
@@ -160,6 +177,9 @@ class SourceCoverageView {
   /// A container for all branches in the source on display.
   std::vector<BranchView> BranchSubViews;
 
+  /// A container for all MCDC records in the source on display.
+  std::vector<MCDCView> MCDCSubViews;
+
   /// A container for all instantiations (e.g template functions) in the source
   /// on display.
   std::vector<InstantiationView> InstantiationSubViews;
@@ -233,6 +253,10 @@ class SourceCoverageView {
   virtual void renderBranchView(raw_ostream &OS, BranchView &BRV,
                                 unsigned ViewDepth) = 0;
 
+  /// Render an MCDC view.
+  virtual void renderMCDCView(raw_ostream &OS, MCDCView &BRV,
+                              unsigned ViewDepth) = 0;
+
   /// Render \p Title, a project title if one is available, and the
   /// created time.
   virtual void renderTitle(raw_ostream &OS, StringRef CellText) = 0;
@@ -283,6 +307,10 @@ class SourceCoverageView {
   void addBranch(unsigned Line, ArrayRef<CountedRegion> Regions,
                  std::unique_ptr<SourceCoverageView> View);
 
+  /// Add an MCDC subview to this view.
+  void addMCDCRecord(unsigned Line, ArrayRef<MCDCRecord> Records,
+                     std::unique_ptr<SourceCoverageView> View);
+
   /// Print the code coverage information for a specific portion of a
   /// source file to the output stream.
   void print(raw_ostream &OS, bool WholeFile, bool ShowSourceName,

diff  --git a/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp b/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp
index d0a2e44be2521d..b43e9e64231e0c 100644
--- a/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp
+++ b/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp
@@ -335,6 +335,10 @@ void emitTableRow(raw_ostream &OS, const CoverageViewOptions &Opts,
     AddCoverageTripleToColumn(FCS.BranchCoverage.getCovered(),
                               FCS.BranchCoverage.getNumBranches(),
                               FCS.BranchCoverage.getPercentCovered());
+  if (Opts.ShowMCDCSummary)
+    AddCoverageTripleToColumn(FCS.MCDCCoverage.getCoveredPairs(),
+                              FCS.MCDCCoverage.getNumPairs(),
+                              FCS.MCDCCoverage.getPercentCovered());
 
   if (IsTotals)
     OS << tag("tr", join(Columns.begin(), Columns.end(), ""), "light-row-bold");
@@ -385,6 +389,8 @@ static void emitColumnLabelsForIndex(raw_ostream &OS,
     Columns.emplace_back(tag("td", "Region Coverage", "column-entry-bold"));
   if (Opts.ShowBranchSummary)
     Columns.emplace_back(tag("td", "Branch Coverage", "column-entry-bold"));
+  if (Opts.ShowMCDCSummary)
+    Columns.emplace_back(tag("td", "MC/DC", "column-entry-bold"));
   OS << tag("tr", join(Columns.begin(), Columns.end(), ""));
 }
 
@@ -955,6 +961,52 @@ void SourceCoverageViewHTML::renderBranchView(raw_ostream &OS, BranchView &BRV,
   OS << EndExpansionDiv;
 }
 
+void SourceCoverageViewHTML::renderMCDCView(raw_ostream &OS, MCDCView &MRV,
+                                            unsigned ViewDepth) {
+  for (auto &Record : MRV.Records) {
+    OS << BeginExpansionDiv;
+    OS << BeginPre;
+    OS << "  MC/DC Decision Region (";
+
+    // Display Line + Column information.
+    const CounterMappingRegion &DecisionRegion = Record.getDecisionRegion();
+    std::string LineNoStr = Twine(DecisionRegion.LineStart).str();
+    std::string ColNoStr = Twine(DecisionRegion.ColumnStart).str();
+    std::string TargetName = "L" + LineNoStr;
+    OS << tag("span",
+              a("#" + TargetName, tag("span", LineNoStr + ":" + ColNoStr),
+                TargetName),
+              "line-number") +
+              ") to (";
+    LineNoStr = utostr(uint64_t(DecisionRegion.LineEnd));
+    ColNoStr = utostr(uint64_t(DecisionRegion.ColumnEnd));
+    OS << tag("span",
+              a("#" + TargetName, tag("span", LineNoStr + ":" + ColNoStr),
+                TargetName),
+              "line-number") +
+              ")\n\n";
+
+    // Display MC/DC Information.
+    OS << "  Number of Conditions: " << Record.getNumConditions() << "\n";
+    for (unsigned i = 0; i < Record.getNumConditions(); i++) {
+      OS << "     " << Record.getConditionHeaderString(i);
+    }
+    OS << "\n";
+    OS << "  Executed MC/DC Test Vectors:\n\n     ";
+    OS << Record.getTestVectorHeaderString();
+    for (unsigned i = 0; i < Record.getNumTestVectors(); i++)
+      OS << Record.getTestVectorString(i);
+    OS << "\n";
+    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";
+    OS << EndPre;
+    OS << EndExpansionDiv;
+  }
+  return;
+}
+
 void SourceCoverageViewHTML::renderInstantiationView(raw_ostream &OS,
                                                      InstantiationView &ISV,
                                                      unsigned ViewDepth) {

diff  --git a/llvm/tools/llvm-cov/SourceCoverageViewHTML.h b/llvm/tools/llvm-cov/SourceCoverageViewHTML.h
index c846379889cd3a..7b97f05b946bd2 100644
--- a/llvm/tools/llvm-cov/SourceCoverageViewHTML.h
+++ b/llvm/tools/llvm-cov/SourceCoverageViewHTML.h
@@ -91,6 +91,9 @@ class SourceCoverageViewHTML : public SourceCoverageView {
   void renderBranchView(raw_ostream &OS, BranchView &BRV,
                         unsigned ViewDepth) override;
 
+  void renderMCDCView(raw_ostream &OS, MCDCView &BRV,
+                      unsigned ViewDepth) override;
+
   void renderInstantiationView(raw_ostream &OS, InstantiationView &ISV,
                                unsigned ViewDepth) override;
 

diff  --git a/llvm/tools/llvm-cov/SourceCoverageViewText.cpp b/llvm/tools/llvm-cov/SourceCoverageViewText.cpp
index 44694a0426c81a..73b7ffe16a9637 100644
--- a/llvm/tools/llvm-cov/SourceCoverageViewText.cpp
+++ b/llvm/tools/llvm-cov/SourceCoverageViewText.cpp
@@ -337,6 +337,57 @@ void SourceCoverageViewText::renderBranchView(raw_ostream &OS, BranchView &BRV,
   }
 }
 
+void SourceCoverageViewText::renderMCDCView(raw_ostream &OS, MCDCView &MRV,
+                                            unsigned ViewDepth) {
+  for (auto &Record : MRV.Records) {
+    renderLinePrefix(OS, ViewDepth);
+    OS << "---> MC/DC Decision Region (";
+    // Display Line + Column information.
+    const CounterMappingRegion &DecisionRegion = Record.getDecisionRegion();
+    OS << DecisionRegion.LineStart << ":";
+    OS << DecisionRegion.ColumnStart << ") to (";
+    OS << DecisionRegion.LineEnd << ":";
+    OS << DecisionRegion.ColumnEnd << ")\n";
+    renderLinePrefix(OS, ViewDepth);
+    OS << "\n";
+
+    // Display MC/DC Information.
+    renderLinePrefix(OS, ViewDepth);
+    OS << "  Number of Conditions: " << Record.getNumConditions() << "\n";
+    for (unsigned i = 0; i < Record.getNumConditions(); i++) {
+      renderLinePrefix(OS, ViewDepth);
+      OS << "     " << Record.getConditionHeaderString(i);
+    }
+    renderLinePrefix(OS, ViewDepth);
+    OS << "\n";
+    renderLinePrefix(OS, ViewDepth);
+    OS << "  Executed MC/DC Test Vectors:\n";
+    renderLinePrefix(OS, ViewDepth);
+    OS << "\n";
+    renderLinePrefix(OS, ViewDepth);
+    OS << "     ";
+    OS << Record.getTestVectorHeaderString();
+    for (unsigned i = 0; i < Record.getNumTestVectors(); i++) {
+      renderLinePrefix(OS, ViewDepth);
+      OS << Record.getTestVectorString(i);
+    }
+    renderLinePrefix(OS, ViewDepth);
+    OS << "\n";
+    for (unsigned i = 0; i < Record.getNumConditions(); i++) {
+      renderLinePrefix(OS, ViewDepth);
+      OS << Record.getConditionCoverageString(i);
+    }
+    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()) << "%\n";
+    renderLinePrefix(OS, ViewDepth);
+    OS << "\n";
+  }
+}
+
 void SourceCoverageViewText::renderInstantiationView(raw_ostream &OS,
                                                      InstantiationView &ISV,
                                                      unsigned ViewDepth) {

diff  --git a/llvm/tools/llvm-cov/SourceCoverageViewText.h b/llvm/tools/llvm-cov/SourceCoverageViewText.h
index ade47ed3b5f586..7cb47fcbf42bd6 100644
--- a/llvm/tools/llvm-cov/SourceCoverageViewText.h
+++ b/llvm/tools/llvm-cov/SourceCoverageViewText.h
@@ -77,6 +77,9 @@ class SourceCoverageViewText : public SourceCoverageView {
   void renderBranchView(raw_ostream &OS, BranchView &BRV,
                         unsigned ViewDepth) override;
 
+  void renderMCDCView(raw_ostream &OS, MCDCView &BRV,
+                      unsigned ViewDepth) override;
+
   void renderInstantiationView(raw_ostream &OS, InstantiationView &ISV,
                                unsigned ViewDepth) override;
 

diff  --git a/llvm/unittests/ProfileData/CoverageMappingTest.cpp b/llvm/unittests/ProfileData/CoverageMappingTest.cpp
index 873bc05b2ecc3f..1cf497cbdc2e61 100644
--- a/llvm/unittests/ProfileData/CoverageMappingTest.cpp
+++ b/llvm/unittests/ProfileData/CoverageMappingTest.cpp
@@ -187,6 +187,25 @@ struct CoverageMappingTest : ::testing::TestWithParam<std::tuple<bool, bool>> {
                 : CounterMappingRegion::makeRegion(C, FileID, LS, CS, LE, CE));
   }
 
+  void addMCDCDecisionCMR(unsigned Mask, unsigned NC, StringRef File,
+                          unsigned LS, unsigned CS, unsigned LE, unsigned CE) {
+    auto &Regions = InputFunctions.back().Regions;
+    unsigned FileID = getFileIndexForFunction(File);
+    Regions.push_back(CounterMappingRegion::makeDecisionRegion(
+        CounterMappingRegion::MCDCParameters{Mask, NC}, FileID, LS, CS, LE,
+        CE));
+  }
+
+  void addMCDCBranchCMR(Counter C1, Counter C2, unsigned ID, unsigned TrueID,
+                        unsigned FalseID, StringRef File, unsigned LS,
+                        unsigned CS, unsigned LE, unsigned CE) {
+    auto &Regions = InputFunctions.back().Regions;
+    unsigned FileID = getFileIndexForFunction(File);
+    Regions.push_back(CounterMappingRegion::makeBranchRegion(
+        C1, C2, CounterMappingRegion::MCDCParameters{0, 0, ID, TrueID, FalseID},
+        FileID, LS, CS, LE, CE));
+  }
+
   void addExpansionCMR(StringRef File, StringRef ExpandedFile, unsigned LS,
                        unsigned CS, unsigned LE, unsigned CE) {
     InputFunctions.back().Regions.push_back(CounterMappingRegion::makeExpansion(
@@ -828,6 +847,33 @@ TEST_P(CoverageMappingTest, non_code_region_counters) {
   ASSERT_EQ(1U, Names.size());
 }
 
+// Test that MCDC bitmasks not associated with any code regions are allowed.
+TEST_P(CoverageMappingTest, non_code_region_bitmask) {
+  // No records in profdata
+
+  startFunction("func", 0x1234);
+  addCMR(Counter::getCounter(0), "file", 1, 1, 5, 5);
+  addCMR(Counter::getCounter(1), "file", 1, 1, 5, 5);
+  addCMR(Counter::getCounter(2), "file", 1, 1, 5, 5);
+  addCMR(Counter::getCounter(3), "file", 1, 1, 5, 5);
+
+  addMCDCDecisionCMR(0, 2, "file", 7, 1, 7, 6);
+  addMCDCBranchCMR(Counter::getCounter(0), Counter::getCounter(1), 1, 2, 0,
+                   "file", 7, 2, 7, 3);
+  addMCDCBranchCMR(Counter::getCounter(2), Counter::getCounter(3), 2, 0, 0,
+                   "file", 7, 4, 7, 5);
+
+  EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded());
+
+  std::vector<std::string> Names;
+  for (const auto &Func : LoadedCoverage->getCoveredFunctions()) {
+    Names.push_back(Func.Name);
+    ASSERT_EQ(2U, Func.CountedBranchRegions.size());
+    ASSERT_EQ(1U, Func.MCDCRecords.size());
+  }
+  ASSERT_EQ(1U, Names.size());
+}
+
 TEST_P(CoverageMappingTest, strip_filename_prefix) {
   ProfileWriter.addRecord({"file1:func", 0x1234, {0}}, Err);
 


        


More information about the cfe-commits mailing list