[llvm] [Coverage] Rework Decision/Expansion/Branch (PR #78969)

NAKAMURA Takumi via llvm-commits llvm-commits at lists.llvm.org
Sun Jan 28 07:07:43 PST 2024


https://github.com/chapuni updated https://github.com/llvm/llvm-project/pull/78969

>From 5d9de08db9025b67aecfcce65cee0d866759ce06 Mon Sep 17 00:00:00 2001
From: NAKAMURA Takumi <geek4civic at gmail.com>
Date: Sun, 21 Jan 2024 20:02:13 +0900
Subject: [PATCH 01/11] [Coverage] Rework Decision/Expansion/Branch (#77871)

---
 .../ProfileData/Coverage/CoverageMapping.cpp  | 112 +++++++++++++++---
 1 file changed, 97 insertions(+), 15 deletions(-)

diff --git a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
index 9c95cd5d76d215..d8e1af84e898aa 100644
--- a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
+++ b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
@@ -14,6 +14,7 @@
 #include "llvm/ProfileData/Coverage/CoverageMapping.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallBitVector.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringExtras.h"
@@ -582,6 +583,72 @@ static unsigned getMaxBitmapSize(const CounterMappingContext &Ctx,
   return MaxBitmapID + (SizeInBits / CHAR_BIT);
 }
 
+struct DecisionRow {
+  const CounterMappingRegion *DecisionRegion;
+  LineColPair DecisionStartLoc;
+  LineColPair DecisionEndLoc;
+
+  SmallVector<const CounterMappingRegion *, 6> Branches;
+  DenseSet<CounterMappingRegion::MCDCConditionID> IDs;
+  SmallVector<const CounterMappingRegion *> Expansions;
+
+  DecisionRow(const CounterMappingRegion &Decision)
+      : DecisionRegion(&Decision), DecisionStartLoc(Decision.startLoc()),
+        DecisionEndLoc(Decision.endLoc()) {}
+
+  bool insert(const CounterMappingRegion &Branch) {
+    auto ID = Branch.MCDCParams.ID;
+    if (ID == 1)
+      Branches.insert(Branches.begin(), &Branch);
+    else
+      Branches.push_back(&Branch);
+    IDs.insert(ID);
+    return (Branches.size() == DecisionRegion->MCDCParams.NumConditions);
+  }
+
+  enum class UpdateResult {
+    NotFound = 0,
+    Updated,
+    Committed,
+  };
+
+  UpdateResult updateBranch(const CounterMappingRegion &Branch) {
+    if (IDs.contains(Branch.MCDCParams.ID))
+      return UpdateResult::NotFound;
+
+    if (Branch.FileID == DecisionRegion->FileID &&
+        Branch.startLoc() >= DecisionStartLoc &&
+        Branch.endLoc() <= DecisionEndLoc)
+      return (insert(Branch) ? UpdateResult::Committed : UpdateResult::Updated);
+
+    for (const auto *R : Expansions) {
+      if (Branch.FileID == R->ExpandedFileID)
+        return (insert(Branch) ? UpdateResult::Committed
+                               : UpdateResult::Updated);
+    }
+
+    return UpdateResult::NotFound;
+  }
+
+  bool updateExpansion(const CounterMappingRegion &Expansion) {
+    if (Expansion.FileID == DecisionRegion->FileID &&
+        Expansion.startLoc() >= DecisionStartLoc &&
+        Expansion.endLoc() <= DecisionEndLoc) {
+      Expansions.push_back(&Expansion);
+      return true;
+    }
+
+    for (const auto *R : Expansions) {
+      if (Expansion.FileID == R->ExpandedFileID) {
+        Expansions.push_back(&Expansion);
+        return true;
+      }
+    }
+
+    return false;
+  }
+};
+
 Error CoverageMapping::loadFunctionRecord(
     const CoverageMappingRecord &Record,
     IndexedInstrProfReader &ProfileReader) {
@@ -638,18 +705,11 @@ Error CoverageMapping::loadFunctionRecord(
       Record.MappingRegions[0].Count.isZero() && Counts[0] > 0)
     return Error::success();
 
-  unsigned NumConds = 0;
-  const CounterMappingRegion *MCDCDecision;
-  std::vector<const CounterMappingRegion *> MCDCBranches;
-
+  SmallVector<DecisionRow> Decisions;
   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;
+      Decisions.emplace_back(Region);
       continue;
     }
     Expected<int64_t> ExecutionCount = Ctx.evaluate(Region.Count);
@@ -664,23 +724,39 @@ Error CoverageMapping::loadFunctionRecord(
     }
     Function.pushRegion(Region, *ExecutionCount, *AltExecutionCount);
 
+    if (Region.Kind == CounterMappingRegion::ExpansionRegion) {
+      for (auto &Decision : reverse(Decisions)) {
+        if (Decision.updateExpansion(Region))
+          break;
+      }
+      continue;
+    }
+
+    if (Region.Kind != CounterMappingRegion::MCDCBranchRegion)
+      continue;
+
     // 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);
+    for (int I = Decisions.size() - 1; I >= 0; --I) {
+      auto &Decision = Decisions[I];
 
       // 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) {
+      switch (Decision.updateBranch(Region)) {
+      case DecisionRow::UpdateResult::NotFound:
+        continue;
+      case DecisionRow::UpdateResult::Updated:
+        goto branch_found;
+      case DecisionRow::UpdateResult::Committed:
         // 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);
+            Ctx.evaluateBitmap(Decision.DecisionRegion);
         if (auto E = ExecutedTestVectorBitmap.takeError()) {
           consumeError(std::move(E));
           return Error::success();
@@ -690,7 +766,8 @@ Error CoverageMapping::loadFunctionRecord(
         // 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);
+            *Decision.DecisionRegion, *ExecutedTestVectorBitmap,
+            Decision.Branches);
         if (auto E = Record.takeError()) {
           consumeError(std::move(E));
           return Error::success();
@@ -698,9 +775,14 @@ Error CoverageMapping::loadFunctionRecord(
 
         // Save the MC/DC Record so that it can be visualized later.
         Function.pushMCDCRecord(*Record);
-        MCDCBranches.clear();
+
+        Decisions.erase(Decisions.begin() + I);
+        goto branch_found;
       }
     }
+    llvm_unreachable("Branch not found in Decisions");
+
+  branch_found:;
   }
 
   // Don't create records for (filenames, function) pairs we've already seen.

>From 1564424ec4018fc2a26a3263cbabba2a4a6cbec7 Mon Sep 17 00:00:00 2001
From: NAKAMURA Takumi <geek4civic at gmail.com>
Date: Tue, 23 Jan 2024 14:48:36 +0900
Subject: [PATCH 02/11] Confirm that all Decisions have not been resolved.

---
 llvm/lib/ProfileData/Coverage/CoverageMapping.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
index d8e1af84e898aa..b8bff6df4ab0e9 100644
--- a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
+++ b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
@@ -785,6 +785,8 @@ Error CoverageMapping::loadFunctionRecord(
   branch_found:;
   }
 
+  assert(Decisions.empty() && "All Decisions have not been resolved");
+
   // Don't create records for (filenames, function) pairs we've already seen.
   auto FilenamesHash = hash_combine_range(Record.Filenames.begin(),
                                           Record.Filenames.end());

>From f74dec7611aa997bb7973589aef06ef17d0dad11 Mon Sep 17 00:00:00 2001
From: NAKAMURA Takumi <geek4civic at gmail.com>
Date: Tue, 23 Jan 2024 14:35:09 +0900
Subject: [PATCH 03/11] DecisionRow: Rework to reflect reviews

---
 .../ProfileData/Coverage/CoverageMapping.cpp  | 63 +++++++++----------
 1 file changed, 28 insertions(+), 35 deletions(-)

diff --git a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
index b8bff6df4ab0e9..5aa25c8b8b11b4 100644
--- a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
+++ b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
@@ -596,56 +596,49 @@ struct DecisionRow {
       : DecisionRegion(&Decision), DecisionStartLoc(Decision.startLoc()),
         DecisionEndLoc(Decision.endLoc()) {}
 
-  bool insert(const CounterMappingRegion &Branch) {
-    auto ID = Branch.MCDCParams.ID;
-    if (ID == 1)
-      Branches.insert(Branches.begin(), &Branch);
-    else
-      Branches.push_back(&Branch);
-    IDs.insert(ID);
-    return (Branches.size() == DecisionRegion->MCDCParams.NumConditions);
+  bool inDecisionRegion(const CounterMappingRegion &R) {
+    return (R.FileID == DecisionRegion->FileID &&
+            R.startLoc() >= DecisionStartLoc && R.endLoc() <= DecisionEndLoc);
+  }
+
+  bool inExpansions(const CounterMappingRegion &R) {
+    return any_of(Expansions, [&](const auto &Expansion) {
+      return (Expansion->ExpandedFileID == R.FileID);
+    });
   }
 
   enum class UpdateResult {
     NotFound = 0,
     Updated,
-    Committed,
+    Completed,
   };
 
   UpdateResult updateBranch(const CounterMappingRegion &Branch) {
-    if (IDs.contains(Branch.MCDCParams.ID))
-      return UpdateResult::NotFound;
+    auto ID = Branch.MCDCParams.ID;
 
-    if (Branch.FileID == DecisionRegion->FileID &&
-        Branch.startLoc() >= DecisionStartLoc &&
-        Branch.endLoc() <= DecisionEndLoc)
-      return (insert(Branch) ? UpdateResult::Committed : UpdateResult::Updated);
+    if (!IDs.contains(ID) &&
+        (inDecisionRegion(Branch) || inExpansions(Branch))) {
+      assert(Branches.size() < DecisionRegion->MCDCParams.NumConditions);
 
-    for (const auto *R : Expansions) {
-      if (Branch.FileID == R->ExpandedFileID)
-        return (insert(Branch) ? UpdateResult::Committed
-                               : UpdateResult::Updated);
-    }
+      if (ID == 1)
+        Branches.insert(Branches.begin(), &Branch);
+      else
+        Branches.push_back(&Branch);
 
-    return UpdateResult::NotFound;
+      IDs.insert(ID);
+      return (Branches.size() == DecisionRegion->MCDCParams.NumConditions
+                  ? UpdateResult::Completed
+                  : UpdateResult::Updated);
+    } else
+      return UpdateResult::NotFound;
   }
 
   bool updateExpansion(const CounterMappingRegion &Expansion) {
-    if (Expansion.FileID == DecisionRegion->FileID &&
-        Expansion.startLoc() >= DecisionStartLoc &&
-        Expansion.endLoc() <= DecisionEndLoc) {
+    if (inDecisionRegion(Expansion) || inExpansions(Expansion)) {
       Expansions.push_back(&Expansion);
       return true;
-    }
-
-    for (const auto *R : Expansions) {
-      if (Expansion.FileID == R->ExpandedFileID) {
-        Expansions.push_back(&Expansion);
-        return true;
-      }
-    }
-
-    return false;
+    } else
+      return false;
   }
 };
 
@@ -749,7 +742,7 @@ Error CoverageMapping::loadFunctionRecord(
         continue;
       case DecisionRow::UpdateResult::Updated:
         goto branch_found;
-      case DecisionRow::UpdateResult::Committed:
+      case DecisionRow::UpdateResult::Completed:
         // 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

>From 425185ce38676d6b5ccd0e18c70038b28771291b Mon Sep 17 00:00:00 2001
From: NAKAMURA Takumi <geek4civic at gmail.com>
Date: Tue, 23 Jan 2024 21:46:06 +0900
Subject: [PATCH 04/11] Add comments to the upper half.

---
 .../ProfileData/Coverage/CoverageMapping.cpp  | 23 +++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
index 5aa25c8b8b11b4..61984adae3f208 100644
--- a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
+++ b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
@@ -583,24 +583,37 @@ static unsigned getMaxBitmapSize(const CounterMappingContext &Ctx,
   return MaxBitmapID + (SizeInBits / CHAR_BIT);
 }
 
+/// This holds the DecisionRegion and MCDCBranch(es) under it.
+/// Also traverses Expansion(s).
 struct DecisionRow {
+  /// The subject
   const CounterMappingRegion *DecisionRegion;
+
+  /// They are reflected from `DecisionRegion` for convenience.
   LineColPair DecisionStartLoc;
   LineColPair DecisionEndLoc;
 
+  /// This is passed to `MCDCRecordProcessor`, so this should be compatible to
+  /// `ArrayRef<const CounterMappingRegion *>`.
   SmallVector<const CounterMappingRegion *, 6> Branches;
+
+  /// Each `ID` in `Branches` should be unique.
   DenseSet<CounterMappingRegion::MCDCConditionID> IDs;
+
+  /// Relevant `Expansion`(s) should be caught to find expanded Branches.
   SmallVector<const CounterMappingRegion *> Expansions;
 
   DecisionRow(const CounterMappingRegion &Decision)
       : DecisionRegion(&Decision), DecisionStartLoc(Decision.startLoc()),
         DecisionEndLoc(Decision.endLoc()) {}
 
+  /// Determine whether `R` is included in `DecisionRegion`.
   bool inDecisionRegion(const CounterMappingRegion &R) {
     return (R.FileID == DecisionRegion->FileID &&
             R.startLoc() >= DecisionStartLoc && R.endLoc() <= DecisionEndLoc);
   }
 
+  /// Determin whether `R` is pointed by any of Expansions.
   bool inExpansions(const CounterMappingRegion &R) {
     return any_of(Expansions, [&](const auto &Expansion) {
       return (Expansion->ExpandedFileID == R.FileID);
@@ -613,19 +626,26 @@ struct DecisionRow {
     Completed,
   };
 
+  /// Add `Branch` into the Decision
   UpdateResult updateBranch(const CounterMappingRegion &Branch) {
     auto ID = Branch.MCDCParams.ID;
+    assert(ID > 0 && "MCDCBranch.ID should begin with 1");
 
     if (!IDs.contains(ID) &&
         (inDecisionRegion(Branch) || inExpansions(Branch))) {
       assert(Branches.size() < DecisionRegion->MCDCParams.NumConditions);
 
+      // Put `ID=1` in front of `Branches` for convenience
+      // even if `Branches` is not topological.
       if (ID == 1)
         Branches.insert(Branches.begin(), &Branch);
       else
         Branches.push_back(&Branch);
 
+      // Mark `ID` as `assigned`.
       IDs.insert(ID);
+
+      // `Completed` when `Branches` is full
       return (Branches.size() == DecisionRegion->MCDCParams.NumConditions
                   ? UpdateResult::Completed
                   : UpdateResult::Updated);
@@ -633,6 +653,8 @@ struct DecisionRow {
       return UpdateResult::NotFound;
   }
 
+  /// Record `Expansion` if it is dominated to the Decision.
+  /// Each `Expansion` may nest.
   bool updateExpansion(const CounterMappingRegion &Expansion) {
     if (inDecisionRegion(Expansion) || inExpansions(Expansion)) {
       Expansions.push_back(&Expansion);
@@ -702,6 +724,7 @@ Error CoverageMapping::loadFunctionRecord(
   FunctionRecord Function(OrigFuncName, Record.Filenames);
   for (const auto &Region : Record.MappingRegions) {
     if (Region.Kind == CounterMappingRegion::MCDCDecisionRegion) {
+      // Start recording `Region` as the `Decision`
       Decisions.emplace_back(Region);
       continue;
     }

>From 2fda236c5fe68fd16b0f35ff0e6e1719d8157219 Mon Sep 17 00:00:00 2001
From: NAKAMURA Takumi <geek4civic at gmail.com>
Date: Thu, 25 Jan 2024 20:01:20 +0900
Subject: [PATCH 05/11] Fill up comments and revise

* Introduce `MCDCDecisionRecorder`.
* Sink `DecisionRecord` (was `DecisionRow`) into `MCDCDecisionRecorder` and make it private.
* Rename identifiers.
* Replace `Expansions` with `ExpandedFileIDs`
* Seek `Decisions` in ascent order.
---
 .../ProfileData/Coverage/CoverageMapping.cpp  | 310 ++++++++++--------
 1 file changed, 182 insertions(+), 128 deletions(-)

diff --git a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
index 61984adae3f208..da28affa788bb7 100644
--- a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
+++ b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
@@ -583,84 +583,169 @@ static unsigned getMaxBitmapSize(const CounterMappingContext &Ctx,
   return MaxBitmapID + (SizeInBits / CHAR_BIT);
 }
 
-/// This holds the DecisionRegion and MCDCBranch(es) under it.
-/// Also traverses Expansion(s).
-struct DecisionRow {
-  /// The subject
-  const CounterMappingRegion *DecisionRegion;
-
-  /// They are reflected from `DecisionRegion` for convenience.
-  LineColPair DecisionStartLoc;
-  LineColPair DecisionEndLoc;
-
-  /// This is passed to `MCDCRecordProcessor`, so this should be compatible to
-  /// `ArrayRef<const CounterMappingRegion *>`.
-  SmallVector<const CounterMappingRegion *, 6> Branches;
-
-  /// Each `ID` in `Branches` should be unique.
-  DenseSet<CounterMappingRegion::MCDCConditionID> IDs;
-
-  /// Relevant `Expansion`(s) should be caught to find expanded Branches.
-  SmallVector<const CounterMappingRegion *> Expansions;
-
-  DecisionRow(const CounterMappingRegion &Decision)
-      : DecisionRegion(&Decision), DecisionStartLoc(Decision.startLoc()),
-        DecisionEndLoc(Decision.endLoc()) {}
-
-  /// Determine whether `R` is included in `DecisionRegion`.
-  bool inDecisionRegion(const CounterMappingRegion &R) {
-    return (R.FileID == DecisionRegion->FileID &&
-            R.startLoc() >= DecisionStartLoc && R.endLoc() <= DecisionEndLoc);
-  }
+/// Collect Decisions, Branchs, and Expansions and associate them.
+class MCDCDecisionRecorder {
 
-  /// Determin whether `R` is pointed by any of Expansions.
-  bool inExpansions(const CounterMappingRegion &R) {
-    return any_of(Expansions, [&](const auto &Expansion) {
-      return (Expansion->ExpandedFileID == R.FileID);
-    });
-  }
+private:
+  /// This holds the DecisionRegion and MCDCBranches under it.
+  /// Also traverses Expansion(s).
+  /// The Decision has the number of MCDCBranches and
+  struct DecisionRecord {
+    const CounterMappingRegion *DecisionRegion;
+
+    /// They are reflected from DecisionRegion for convenience.
+    LineColPair DecisionStartLoc;
+    LineColPair DecisionEndLoc;
+
+    /// This is passed to `MCDCRecordProcessor`, so this should be compatible
+    /// to`ArrayRef<const CounterMappingRegion *>`.
+    SmallVector<const CounterMappingRegion *> MCDCBranches;
+
+    /// IDs that are stored in MCDCBranches
+    /// Complete when all IDs (1 to NumConditions) are met.
+    DenseSet<CounterMappingRegion::MCDCConditionID> ConditionIDs;
+
+    /// Set of IDs of Expansion(s) that are relevant to DecisionRegion
+    /// and its children (via expansions).
+    /// FileID  pointed by ExpandedFileID is dedicated to the expansion, so
+    /// the location in the expansion doesn't matter.
+    DenseSet<unsigned> ExpandedFileIDs;
+
+    DecisionRecord(const CounterMappingRegion &Decision)
+        : DecisionRegion(&Decision), DecisionStartLoc(Decision.startLoc()),
+          DecisionEndLoc(Decision.endLoc()) {
+      assert(Decision.Kind == CounterMappingRegion::MCDCDecisionRegion);
+    }
 
-  enum class UpdateResult {
-    NotFound = 0,
-    Updated,
-    Completed,
-  };
+    /// Determine whether DecisionRecord dominates `R`.
+    bool dominates(const CounterMappingRegion &R) {
+      // Determine whether `R` is included in `DecisionRegion`.
+      if (R.FileID == DecisionRegion->FileID &&
+          R.startLoc() >= DecisionStartLoc && R.endLoc() <= DecisionEndLoc)
+        return true;
+
+      // Determine whether `R` is pointed by any of Expansions.
+      return ExpandedFileIDs.contains(R.FileID);
+    }
+
+    enum Result {
+      NotProcessed = 0, /// Irrelevant to this Decision
+      Processed,        /// Added to this Decision
+      Completed,        /// Added and filled this Decision
+    };
 
-  /// Add `Branch` into the Decision
-  UpdateResult updateBranch(const CounterMappingRegion &Branch) {
-    auto ID = Branch.MCDCParams.ID;
-    assert(ID > 0 && "MCDCBranch.ID should begin with 1");
+    /// Add Branch into the Decision
+    /// \param Branch expects MCDCBranchRegion
+    /// \returns NotProcessed/Processed/Completed
+    Result addBranch(const CounterMappingRegion &Branch) {
+      assert(Branch.Kind == CounterMappingRegion::MCDCBranchRegion);
 
-    if (!IDs.contains(ID) &&
-        (inDecisionRegion(Branch) || inExpansions(Branch))) {
-      assert(Branches.size() < DecisionRegion->MCDCParams.NumConditions);
+      auto ConditionID = Branch.MCDCParams.ID;
+      assert(ConditionID > 0 && "ConditionID should begin with 1");
 
-      // Put `ID=1` in front of `Branches` for convenience
-      // even if `Branches` is not topological.
-      if (ID == 1)
-        Branches.insert(Branches.begin(), &Branch);
+      if (ConditionIDs.contains(ConditionID) ||
+          ConditionID > DecisionRegion->MCDCParams.NumConditions)
+        return NotProcessed;
+
+      if (!this->dominates(Branch))
+        return NotProcessed;
+
+      assert(MCDCBranches.size() < DecisionRegion->MCDCParams.NumConditions);
+
+      // Put `ID=1` in front of `MCDCBranches` for convenience
+      // even if `MCDCBranches` is not topological.
+      if (ConditionID == 1)
+        MCDCBranches.insert(MCDCBranches.begin(), &Branch);
       else
-        Branches.push_back(&Branch);
+        MCDCBranches.push_back(&Branch);
 
       // Mark `ID` as `assigned`.
-      IDs.insert(ID);
-
-      // `Completed` when `Branches` is full
-      return (Branches.size() == DecisionRegion->MCDCParams.NumConditions
-                  ? UpdateResult::Completed
-                  : UpdateResult::Updated);
-    } else
-      return UpdateResult::NotFound;
-  }
+      ConditionIDs.insert(ConditionID);
+
+      // `Completed` when `MCDCBranches` is full
+      return (MCDCBranches.size() == DecisionRegion->MCDCParams.NumConditions
+                  ? Completed
+                  : Processed);
+    }
+
+    /// Record Expansion if it is relevant to this Decision.
+    /// Each `Expansion` may nest.
+    /// \returns true if recorded.
+    bool recordExpansion(const CounterMappingRegion &Expansion) {
+      if (!this->dominates(Expansion))
+        return false;
 
-  /// Record `Expansion` if it is dominated to the Decision.
-  /// Each `Expansion` may nest.
-  bool updateExpansion(const CounterMappingRegion &Expansion) {
-    if (inDecisionRegion(Expansion) || inExpansions(Expansion)) {
-      Expansions.push_back(&Expansion);
+      ExpandedFileIDs.insert(Expansion.ExpandedFileID);
       return true;
-    } else
+    }
+  };
+
+private:
+  /// Decisions in progress
+  /// DecisionRecord is added for each MCDCDecisionRegion.
+  /// DecisionRecord is removed when Decision is completed.
+  SmallVector<DecisionRecord> Decisions;
+
+public:
+  ~MCDCDecisionRecorder() {
+    assert(Decisions.empty() && "All Decisions have not been resolved");
+  }
+
+  /// Register Region and start recording if it is MCDCDecisionRegion.
+  /// \param Region to be inspected
+  /// \returns true if recording started.
+  bool registerDecision(const CounterMappingRegion &Region) {
+    if (Region.Kind != CounterMappingRegion::MCDCDecisionRegion)
       return false;
+
+    // Start recording Region to create DecisionRecord
+    Decisions.emplace_back(Region);
+    return true;
+  }
+
+  using DecisionAndBranches =
+      std::pair<const CounterMappingRegion *,             /// Decision
+                SmallVector<const CounterMappingRegion *> /// Branches
+                >;
+
+  /// If Region is ExpansionRegion, record it.
+  /// If Region is MCDCBranchRegion, add it to DecisionRecord.
+  /// \param Region to be inspected
+  /// \returns DecisionsAndBranches if DecisionRecord completed.
+  ///     Or returns nullopt.
+  std::optional<DecisionAndBranches>
+  processRegion(const CounterMappingRegion &Region) {
+
+    // Record ExpansionRegion.
+    if (Region.Kind == CounterMappingRegion::ExpansionRegion) {
+      for (auto &Decision : reverse(Decisions)) {
+        if (Decision.recordExpansion(Region))
+          break;
+      }
+      return std::nullopt; // It doesn't complete.
+    }
+
+    // Do nothing unless MCDCBranchRegion.
+    if (Region.Kind != CounterMappingRegion::MCDCBranchRegion)
+      return std::nullopt;
+
+    // Seek each Decision and apply Region to it.
+    for (auto DecisionIter = Decisions.begin(), DecisionEnd = Decisions.end();
+         DecisionIter != DecisionEnd; ++DecisionIter)
+      switch (DecisionIter->addBranch(Region)) {
+      case DecisionRecord::NotProcessed:
+        continue;
+      case DecisionRecord::Processed:
+        return std::nullopt;
+      case DecisionRecord::Completed:
+        DecisionAndBranches Result =
+            std::make_pair(DecisionIter->DecisionRegion,
+                           std::move(DecisionIter->MCDCBranches));
+        Decisions.erase(DecisionIter); // No longer used.
+        return Result;
+      }
+
+    llvm_unreachable("Branch not found in Decisions");
   }
 };
 
@@ -720,14 +805,13 @@ Error CoverageMapping::loadFunctionRecord(
       Record.MappingRegions[0].Count.isZero() && Counts[0] > 0)
     return Error::success();
 
-  SmallVector<DecisionRow> Decisions;
+  MCDCDecisionRecorder MCDCDecisions;
   FunctionRecord Function(OrigFuncName, Record.Filenames);
   for (const auto &Region : Record.MappingRegions) {
-    if (Region.Kind == CounterMappingRegion::MCDCDecisionRegion) {
-      // Start recording `Region` as the `Decision`
-      Decisions.emplace_back(Region);
+    // MCDCDecisionRegion should be handled first since it overlaps with
+    // others inside.
+    if (MCDCDecisions.registerDecision(Region))
       continue;
-    }
     Expected<int64_t> ExecutionCount = Ctx.evaluate(Region.Count);
     if (auto E = ExecutionCount.takeError()) {
       consumeError(std::move(E));
@@ -740,69 +824,39 @@ Error CoverageMapping::loadFunctionRecord(
     }
     Function.pushRegion(Region, *ExecutionCount, *AltExecutionCount);
 
-    if (Region.Kind == CounterMappingRegion::ExpansionRegion) {
-      for (auto &Decision : reverse(Decisions)) {
-        if (Decision.updateExpansion(Region))
-          break;
-      }
+    auto Result = MCDCDecisions.processRegion(Region);
+    if (!Result) // Any Decision doesn't complete.
       continue;
-    }
 
-    if (Region.Kind != CounterMappingRegion::MCDCBranchRegion)
-      continue;
-
-    // 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).
-    for (int I = Decisions.size() - 1; I >= 0; --I) {
-      auto &Decision = Decisions[I];
-
-      // 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.
-      switch (Decision.updateBranch(Region)) {
-      case DecisionRow::UpdateResult::NotFound:
-        continue;
-      case DecisionRow::UpdateResult::Updated:
-        goto branch_found;
-      case DecisionRow::UpdateResult::Completed:
-        // 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(Decision.DecisionRegion);
-        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(
-            *Decision.DecisionRegion, *ExecutedTestVectorBitmap,
-            Decision.Branches);
-        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);
+    auto MCDCDecision = Result->first;
+    auto &MCDCBranches = Result->second;
+
+    // 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();
+    }
 
-        Decisions.erase(Decisions.begin() + I);
-        goto branch_found;
-      }
+    // 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();
     }
-    llvm_unreachable("Branch not found in Decisions");
 
-  branch_found:;
+    // Save the MC/DC Record so that it can be visualized later.
+    Function.pushMCDCRecord(*Record);
   }
 
-  assert(Decisions.empty() && "All Decisions have not been resolved");
-
   // Don't create records for (filenames, function) pairs we've already seen.
   auto FilenamesHash = hash_combine_range(Record.Filenames.begin(),
                                           Record.Filenames.end());

>From 133bd019a7a945d64207fa4ebe00d048743e2083 Mon Sep 17 00:00:00 2001
From: NAKAMURA Takumi <geek4civic at gmail.com>
Date: Fri, 26 Jan 2024 09:02:03 +0900
Subject: [PATCH 06/11] Update comments

---
 llvm/lib/ProfileData/Coverage/CoverageMapping.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
index da28affa788bb7..ef4d8d5349cb00 100644
--- a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
+++ b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
@@ -589,7 +589,8 @@ class MCDCDecisionRecorder {
 private:
   /// This holds the DecisionRegion and MCDCBranches under it.
   /// Also traverses Expansion(s).
-  /// The Decision has the number of MCDCBranches and
+  /// The Decision has the number of MCDCBranches and will complete
+  /// when it is filled with unique ConditionID of MCDCBranches.
   struct DecisionRecord {
     const CounterMappingRegion *DecisionRegion;
 

>From 2b6edff7f3e68a64e02ee5972b7f9533aa150443 Mon Sep 17 00:00:00 2001
From: NAKAMURA Takumi <geek4civic at gmail.com>
Date: Fri, 26 Jan 2024 09:02:27 +0900
Subject: [PATCH 07/11] Make the class local

---
 llvm/lib/ProfileData/Coverage/CoverageMapping.cpp | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
index ef4d8d5349cb00..60ec2fad9f08ed 100644
--- a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
+++ b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
@@ -583,6 +583,8 @@ static unsigned getMaxBitmapSize(const CounterMappingContext &Ctx,
   return MaxBitmapID + (SizeInBits / CHAR_BIT);
 }
 
+namespace {
+
 /// Collect Decisions, Branchs, and Expansions and associate them.
 class MCDCDecisionRecorder {
 
@@ -750,6 +752,8 @@ class MCDCDecisionRecorder {
   }
 };
 
+} // namespace
+
 Error CoverageMapping::loadFunctionRecord(
     const CoverageMappingRecord &Record,
     IndexedInstrProfReader &ProfileReader) {

>From 9e0f60f3b0a9750e280723ab9de94215093dbb17 Mon Sep 17 00:00:00 2001
From: Alan Phipps <a-phipps at ti.com>
Date: Thu, 18 Jan 2024 10:29:50 -0600
Subject: [PATCH 08/11] Import Alan's testcase from #78819

---
 llvm/test/tools/llvm-cov/Inputs/mcdc-macro.c  |  20 ++++
 llvm/test/tools/llvm-cov/Inputs/mcdc-macro.o  | Bin 0 -> 81624 bytes
 .../tools/llvm-cov/Inputs/mcdc-macro.proftext |  62 ++++++++++++
 llvm/test/tools/llvm-cov/mcdc-macro.test      |  92 ++++++++++++++++++
 4 files changed, 174 insertions(+)
 create mode 100644 llvm/test/tools/llvm-cov/Inputs/mcdc-macro.c
 create mode 100755 llvm/test/tools/llvm-cov/Inputs/mcdc-macro.o
 create mode 100644 llvm/test/tools/llvm-cov/Inputs/mcdc-macro.proftext
 create mode 100644 llvm/test/tools/llvm-cov/mcdc-macro.test

diff --git a/llvm/test/tools/llvm-cov/Inputs/mcdc-macro.c b/llvm/test/tools/llvm-cov/Inputs/mcdc-macro.c
new file mode 100644
index 00000000000000..bd2b979bd257f7
--- /dev/null
+++ b/llvm/test/tools/llvm-cov/Inputs/mcdc-macro.c
@@ -0,0 +1,20 @@
+#define C c
+#define D 1
+#define E (C != a) && (C > a)
+#define F E
+
+void __attribute__((noinline)) func1(void) { return; }
+
+void __attribute__((noinline)) func(int a, int b, int c) {
+  if (a && D && E || b)
+    func1();
+  if (b && D)
+    func1();
+  if (a && (b && C) || (D && F))
+    func1();
+}
+
+int main() {
+  func(2, 3, 3);
+  return 0;
+}
diff --git a/llvm/test/tools/llvm-cov/Inputs/mcdc-macro.o b/llvm/test/tools/llvm-cov/Inputs/mcdc-macro.o
new file mode 100755
index 0000000000000000000000000000000000000000..f10c849b844bd2b7aa325531d8f2f978b62097c2
GIT binary patch
literal 81624
zcmeFa3wTpi)<1mGCJ?YR0YR&R1TEUitq}?>P-!5A6HK9UlToxSZ3 at -iCQYCyYBjC)
z7&6uwN1YjGWM*_;-x;03Q4w|2luHYEtEh<gqKGFJwBDc~lK*e*vrlpont3nZ_dU<|
zd|ytVW}W at pYp=cb+G}6WPR?yb&iN at iouFQ+!W9CAwv14aLLRX)H_B5e<O#!sLg5l&
zyf6sydi<+=@$^6nB5|qIs04*nP)e6UHv`bo3M{Bp&7CNvOLXlvDKRP)%u1Nj8R(XW
z<LSLuD|{+d<0JVsDDgrZ-7-$WRjN%#wt4LLO8jF_#L@>{N?2&)l<P(2qI5kxT at Oze
zp`;MSbE?{tY(oEe+ at A5&f;hS?>{Afp)2-y`;;G87Qq}fLKlfDrZ}KbQ=`1%Zip0}X
zTo0A1`CWx{WS9T$rB?G2o?oJRSjqiUrQEE-l$z?*)2B_Tshm_(UFU6@)RZ at U()4MQ
z8|x=u&Qu`1C=c?f1&fvoDa}HVFg3jzPhpCui;BjxV{`(-6i?-95pKt)AP_bY|8Kfp
zuf419{*8nUbZrC3n`lh*oCNsE1o-a~;I&Z9g1Go=GVp=&CA|m2zf6Ez6W~<|@INQO
zvlHk;bu*AYFDJl1O at L=4kYAgC|E2`^MG53wm4JU`0$fUjL%s!Z at mC{&f$Z=`0(?XQ
zJ<m<Re_;arz6ALA1o)E)@Vo^0?FsOw65wSC at M#IkyFUT{Sqb<b1a3i^`0K9#2D1M*
z3Gm&(hsO&+7~+MC2EgwDHjsRww6vnBytJyiuDquDCYR9YaaXK$3su!MuDW`mw!EgM
zzCx&Kbh&O6sw(O{HNqN~r=hx1sP)#>mNy_Z`oD_OMo+ohQ(9YIUB?8}HV9P>?&>;E
zl~7gR;HpE?n)*hU;H at jKWm$OYYlK=?ZKKPBEGpMkBd*Hhs;LpG+%DW!)p#4%lBBwd
zh7Cla43SDN1XnVNP{UQXPH3#-`80YeU2ZoCr6i<e`5IRvFA*yeGGi$`ZZOunp*m<?
zeJKi9?y0ValIz{o9+%MIu0S!XJZ^6tB3wd+VuM;LZtabg)s&J<hPb9`k3i+8?tZ$k
z!0DJfuk`ZC)@hO3snOfZC+7;Kj%CHA(9^Z1y3yluFDsr`Q(xy=R=&E%1)*zd>+85B
zOBL1u8EI at t#eWLwnm+&iAMxn_4Ubj^xs498ZEzxWT)gkz`!Ve3g)2GsGz+H+D|py@
ze)SO2sGJ-RmpXq@=MC*=EBF>ZKZ)Sn8MnpZC7eGPhhMr*k+VAvpTY6&INZ+h19AAz
zI4)90f=ks$4VT2>h15~tS{aX at trWN+4)4BR;ctq=H!oH2<~UqXhJ2wd4$mo9_}k;~
z5{^F at hj(*4_zxu=%@t^VpkBK*xH`9>xNZ&3=Dj?@0S!)lx_ZeP{8AMJ?bF~i#;aGq
z2ER-NK{K`}fx#e(C6%y6gYzjKi^$U8>RgW^ax^%VS-tW!IOU;Ug&JI~dx{e^I2<~1
zm1yvx5fuKQ!Ozm*Wg0w9gRj-#!!&q<22a=EO&Z*!!J9R>S%bG}@ZlP~U4v`;{|7ZV
zZ*nZvW(_`4Bj+&<K1zdc(ctH3 at NF7ALxTr3_<0(9w+267gLiB23pDrv4X&>JD7r_3
zkJ0eU8l2`L>eZ*g$EqM`zXrExaDlfw@~Lqe+^E5|<F8qRU##KJ(BPRG+ at is04y0aL
z8vGI!1kKUlSsFY~gJ)~-|LyH}rVCGsz9UA_Z`kuNnoDcYlM?P0eLIain8<MMr+~xb
z58=}^wh(s|UQ2PIo-nTQ?^BqXdZ?R)_feR-qEL{9_fnXed}s>`Z>KOd_0VP(exAbA
zMTOc~_ at 5M}CLd~M;lEOtswLFG!hfPLHStgx3;&kF)U-n-Ec{CfQ<DxAvhdv$rluUq
zVc|O{OiegsVd0x7OiedrW??slsmX=}7QTVP)Ko)#rvZ##O<^;Idsz4y3J<4nHw!PJ
zFg3|gkcAggn3`f}3k%PsFg3x at W)_}FVQPAzb{4*z!qntK%`AKwg{i9!HL&nS6sD#Z
zDr4dEDNIc+RKmg|C`?T)RLH_ZDNIc)l*7U)6sD#Xvas;UKOjs^Dr9EiZz)VoDI~D)
zmlPgL;l7`!{GU?TLg5}3exJhBWJ296ypO`vR6;=(-b-O>BB3oTyq&_-G(wwMcqVo_
zs at h>!MoZ+&VB0RkBYgB&xua^bX>1GF75^iDwH|dyZ(c7-pNhUieI?6^tU>D at F;H+X
zoG3i%yN3kf`)C^A9mr4&7 at jAR-|ra(SKkjNa`hqM9 at E%nlD&gdlEe6O?<SIcT9o?4
zj<4p39sQ}Iu2X#Tv}Ys)Ji!GR!#!1|vG!<s(yzJT^DsnuCoC6z1$To}5T(yOX`;Vi
zKguW%hZW^Yd~_OK1fT9Y<Q>VM(udX~lIyb^Ns-?$QpOk5iIO1&x0FP&35)j~DU at zG
zC3*X$<==x at MlPKBBiwcs^-&3Zx1178zYh9~``WfQf2uQW-PiG{Fyu|CxbHEYsg*Ur
z9#Zv;^!}ng-)^&1)VD<!;oPclc1Cl0fo}bVhoZ>6xb+u(Kdb0ee732v*;XsUs-sD-
zSA at -pBGaP69*!+TO02Naim<e3QXOu$=-P_D4qc_h9iYlnjQJXFeYf<JVcLqm(%9zB
zPq|p6GqqB4ySwNs(Ld}FmbL>GG3+<EMeN;0Un8Bgx8uuHX`i%T42&uv9^JR1w7kc6
zaPU8M`#u%@#XUoZw+)ZjkJ>Y=HJ?di)jA8eaynXEq`F9}4j8{}Z?w9)Q4QAMu?`gH
z6JBX>J`#0hR8%2uS*4}$N{ce{d at qSIUw~V(&L?*leG*rWHf#=wzU_LYEZxzvbPrUP
z?Tnk1<yqXybJ4mZW%(~%?-xob9*qk5EpB<$XzHco)42Mkc{Ymgi6*RO3BNzX+2g!!
zw32XMG+`cY$rNvdK2_pO(KtPBBQ~`LpVYBBc%9e5;|cf`=Z~#K!w^j+sse6Q1nmDU
zS)AGM6}t5s9*-h_#4T at 655rqui?ND6;;yPl#b;lG&o(d_%x7J=Z7X{H?xNRG%)5);
zh^z7Yf5U3rpwxIUTI0ciYFuXAOxureE0_Ej4rAs}c%7!t=KYhotLPMpNF_QMP4zWy
zU#6+T^E5w at _REtY!5V}NGi0>We8aY4X^HfmLpqI=<_91^GTi<nc#pqCQx3VdA6ZI<
zbp(xR!sp90GAnUcQCwiyH5gk($>@|CjH1*f`!O_i6&fiu&2p}_U9Zu$svX%lP9h3}
zgKh^YZ at b#I!nSOw{1zp6P?Wwv{g_2xPoJ3GB}%8P$7BhzL}>?xm>*YA$`4E}cZ1m>
z?G&X?<loIuq^);Iown?c9UX@);>(D}+FB5ty5nL;*dkv-sv^s&4yjArMzN%yL)Yo(
zIBjuCDB{OX>09}Y<KghUPW=qQDHZ(r1d>bry%#~_Dfc4}f1Vh~y_qx!jJhAh8kFn7
z at 05DPfUD<P+bY}jwo+}?uyJcSE8lXIumt1N6%blvJtnq>O)VH^gyuPdspZ$WvHK_O
zL!8||{tXb(KV~+Fe^Mtt<?3VMFbY at Pdeoa<)OyG}1Sat6TaS85F_`L!V*%sn+D7vU
zhpwyG^pBs4QSv~+>3+m!E9rS`Ct|-Z)}4kB&oDCJP(+L2-MuN+pe>L#sP$-3z%wYr
zZnA%4$I#5GaFy*kUbyyXc`&P8>GT)eh%Ab!JjY;C8!ONeR$)K-SoVU`KfWG3-bucK
zZ$SlbW-$se)>k02&^gi%^0}fQv<6Kr&mtk%3lRb3vq9vVAIJginPvbv1YFAj<2J}b
z&WAj3y)R at t!EhgYW>z?ePrX=S&xAwTGc9yyrn?O5(IV^7-ieNipG4g!qCfZD<CLE#
zGm9#JA0*5R=qHO0cIs27*i&mV_2FIZV#RAV(^kF7*FWBKadE{Dq$cfY+QPf$w&|ya
zclCZ$g)1_L0<+zu6T=<SYrg)qp38jw9?w0#{s!+no@*fYD&#8qmt|(irO1*pGDFP$
zk$4A>x5#sNyfM5h)QGy3WYT5u>{_qECLQS=g%5q_>|^frw(!n*h`AIoU&-fvk4C=l
zc&-FFHfcZnbjCSov**EFVxT57N0h#X3lF&(L8+ri!gwc7M-pFu#&a3qF*$ep`or7U
zt$nwmm9yd-QTmFk^f)++bYIz}&x at oVpw@@*IqL_#+3<w&W2nWWo*c9%`I+y~GAjDu
zUry;XrSl-&+)n*e<aq#2wUXKkcPgj!V{b^EZ!MMgo<d23Vnx5)fHc%<E!6s at E~VGG
z8N(hx^ae+lpaiC(9oOza6W#PnGAcFE0|W;~eFuV)y$VIZ+kw!vG!I;gz=KfKv^4`B
zbr#yOEPbnFlhK?fc+RGbsJDC`l`LEvqP?J&4=^VUu=2e`y^3|;wINzySi?*u3exm0
zuDzpuWcUq6<dxrNx~mj2rFW>O4Z{=Doc`R)e}JB?q+AZPaGLD>#jVFYXFH|iFqy1}
z-sEfJkd6#m2_>n=^BaaD9hk^4ChBm{CoB02KKq`D^Q8L<KEbWvF~|a9Ojid@!wu_@
z2BcIWqC%_+IbR{E3ORzb;Cx3RFDc|d6w<Dc4It<lQPm?zvzwr4mtieHvY1oyWSXT_
zXn&HS1Q<FjQ`<v`5d)LHFQMM=XcrnLIH at Cq=VVgziMAgqb0gRT^K|z7&!ioufQu4W
z>|macR_Of*xGAt0Xn02i5B}cQKYaZfH1$?Y4drl;ykj2gQ(D)c#^kNA9!&K$FnKRN
zdN&uf9yPUKu%?FG`^%!%W2Tl47V7M6L0zw14$g)?SXMGT`5jp{Xlh*x#|-HqPdWf?
zMh><Hn@`qy_0oI2ldVDNJy!n#cV}Vq>0_olW`P-NfPlU^pr7jtc85DCjfqlx;M)ib
zm|FjWGWhy+zHUJ>e27fQFn|Q<mC#RUR&ojDU$Ofo8gil2d89{R)QDxwv3-cNy-4g(
z?Vi`bANm7A;2Q$ImzohF*TZ{l%ty(r{|O%W&+2=?#ahaENDAGAM5qbWi(Q(u2ubZy
z?v)^Z)FJhaW@(I_Wn9pQHYBh5IvgfHts?)c68)pjhB&aLg<p3DhBd-$aOkd0<Oq9w
zr&HGtlD-lBX}d-LOsC&4=UY_TE508Ie&6X&>qc`8K`J^3G}Un^k(`E|0eSFTDwTIs
z??mL{oh~28_JRDZjX=+N=shx`y+`OqB#_4Z5t3sgs8d*vSuYjUKWf)EYR)F~KY?N6
zp&|6@=pBr}i?9PU84pdM#&1zVxs`HkW;uF)(fcT5P2jQuxkJF-J0DO+1S(t#XeuB>
z1bPUNQ#uKO4iZ=iS-ls4<FkOGQy{mPm7F*(1qYh21xlhvh*;hkC>RAEYSL+lq9#2F
zHd31O?|`78wkV`lAuALzM<H1X8Lp6%SfwJtX9{^$A<uw_U50DGLKo&ezXK97!c~-Y
zH}#imNdW6FKPZ7&es&bvq9!h){&GxAe<}ZT5 at i(&S at lip%|_3e4v)MP0$9)4J2=v3
z%KM0QjKVskx!_aeD|k|_-2r>dm at WvNhHfA_q^`zh)wQ9Eu?|E3-CXb-V@$OMca(rN
zjj=w;S+4-=%1}RAcw{^gr61*I=P=iSa12N<>5&od-4{WJXx~HgVa=43`lq0L<s|sm
z4*BURtW4GNjZBueOumTbO#@C?4^RvY<ic!IzF;yn@*&>asIUAOS=*#e>P%?*=lfCT
zMK2=HKpr&N(#|8{D%<?3tITlXE->l_5U}?j!Dx-7uR_D#AQ<n7Fs at V>n-#`4z-W63
z9Z|UVKCrC?o70~ow;?<DdkvT{kUUR&dr}AFZjP{rRwE5kOlK)9^19w|*q<jqOf?_K
zy at w_I0^W at Y1k$ZVI=1<T`cdYG%a&sFv%q!6jH0ZDUHCPKr1Kko_(~AYG7P4me at s6D
z-s~bPjP(H)dgzEC{3dw3d>9gG&?iG3g?gb=6p0z at IasO4asjK0Y?{43o`rTbmMZz%
zG+GmUOyC3AlpRanvYm<b&|L5n`8399Om0jq>w!A`=!P+M^;{<gFdIWW4ZOdD3o({u
zjHNO&1Jg^2SZ$(QO|A3<pJZ)^O{87&-=GX8R8prr902D%hFjaqU=(^s6Yu>9qnuI~
zmNQ>|$wv0p?-7eQIN3h=FNkNEenOz%aHm9ZhT|wZ8o;l~B%g&8r<z2qE(@*BdhB`H
zg_VaQEyfS9s#BzG$N{N4SZc%b*xRMqFAT6M(~RoM&LgQPJCwFdC*?ioAn7h~)|j<O
zpsWcFBePIIQBXbsael)rM4n|RCYJSb)ofM+byT*$UW!syLg^u-G#gmj7gG+S?k!>N
z>OgvKmyby^zu}WMvVNXC7TJJ^C;@8zQm~_uU@%bJYB(##C-Tq0^&&a3B%_PBL{?gC
zyj|)y{P75B_7pxnqvY9`JxV*6>1Fdo6zl+-9 at Zj9MK{AjrrQn>*y0Ju?%f;h0@@?(
zpDo$IaV2~S?SC3g5+8K1<>@)JJauGuIi*w3SDBU`QYMim>W|@uoisDsB2yY$mwxc%
zdUs`&t;;@G?{fQsg8+8w2Mcl-ve7q~T0bLG2Xb?rtVPOSKr8b77Oa%nIv4vdcY#AP
zeBMip1wNQce)9{)<vGJ6`jM|>*b7$GBmP2~NQQp^f*Yb#yDaFDSw~0<Whm>E4$7O6
ztwY*H%c;*H+j09(6lrRG09vSB)h4tzyVQ at a>Ogc5rm^-cQjpdD+Y3m?QKvx2e#qMP
zGF4yi3t&5RCsloptVhvNVi`;^ebDGA`zEkI19nAf%_8EYdBO?k#w89?8W|WjF}W7`
z)x*rCexL#eazAI{(R{f}y at aG8D|Er&)`C)@XE}?8y2$daJ at n&=nDBCJkWgPH{Xl(8
zT|W7}r;;X*YCmvUrqVCa$t_Wx+lm3+<7@(omhG8Gb1C#T$2?b3Z-ezHdYfbNB+Mo$
zI#WbvWG3VVQW2YBsFX49hERo*-ot0;Hw^hio{ikLQO6RJ2=N=PZ6#L2t6+j-+yE=d
z+dfa=7 at xp6n9q__zM2XELF)TiYQsILB#W(<SD`~hA7N(Q(|?R0{1eQ at 8suJ<;uHB|
zXim-lZx>TFD-HAd>&O#y&<Isbk$)iOK<;{_mZj~ey;*=*0eW8n?>ndkrGDQ454>i$
z$h>A0xI%wN)yP*uJ{TVdBNysG99rb3hgeOY1$!Zj!AJ*3lHOPPj(1=J#Y{AkY3i4@
zW0s0+9fl4RWx)Oj<WU_=qY?UXvYnKQkrg3U_ at QdF=ugEyiRkOETUU(H8arFIQY!Co
zR)!4uDn+52zz>DKzY^Wz7({>TqXJ~emn+fND$!%n)L|h}+TNQ1rq>u#9?~tMh`^}Z
zKwzz5P=}A;n^=Fx^mkwzT>4HfL?kt5DEu*-&^$+uVUvPI);;n(Wap3uLEW$iV;lC;
zK9)^`S+_kI4LG9xQm)>wK4a$m7nMJdJ3Xr2#SZ@!l=vcw at f$9VX!;0B2u(?0Xj*~%
zc&ps3v3JaV5r(aw<h>VApSDZh$7rW~5|r)J$aEN)rc6dgcgki^4C7AeYibymW7tAE
zu}hC8KoN(rks(inkDx`G;DA}$4hdr&_Jt~`O>M*w?w^)P-04u#iV@}*+Yr^7Yb1)#
zY~>WCUDmMtHfi-BwMvU4`zMF)s8fg0C)j|-^7GFTh1Q%QKYTIWp_Ne37^_0)4i;(n
z?snqIkf(mi?7IpYz`liKJ68Ry?PydFoexc-28^*E%CX)^eT<nb(7Mmm@*=aucTzVS
z;o%HyMcA7Y>E9OmJrj%z{mUl65Z}lzLH5E~_6cTF>qew>gu9%xFcI)<D3*S5bbM}D
z=$}0SD-iFVMgGbO=0&D|R8BC?^=+JxA$anX^#E<S?RNN18Er2bA)(kb_XryRS!dyt
zzNB1MBA4RqBhGx;)7^V6TBTDu%=}Y+2}XcJdhhHVq9~eTJ<3LP&5BO5T}<;yY!NRl
zk)NTtz<`tO8)@<Y4;V=kzCgi=`DoG4_9z$@cdq_x(J2KA4gwdY(~25jP&~RP+8g&3
zlpR7ryl<mcp0Tr9k=H;?k{P5?{C#3p<ZylXmNkY2v%G35>-}3UqwX+ZSpFH2VT*|5
zcbHme?^@2l)*G~wEsUbgcgmmVk~4S8AJIrZV7Y!pC8jw*H$<RS_zllN=vjtW at M&tj
zm+C!`+lNw7ueJ3cXZ|yo$q>-{Q|OHyBl&s6NgcFJ-62~{EnP^7fOVgE{2LK2)Y?g+
zq%k)_E{U3jO(UA-R*|GGLkVEIXyn3#@e_FhCRl#MB&N$8rps7xhAzVgB_Dkul9Afy
zaZS344p4eA;5|TEZN!QT9$;$0h=UH at bpQ@!YGG5&z;bvoM#aPOxp+*$n08W1|1I4F
z{t4xQ7N*v9=+Q$P at L{K7D5Yqbi7<Mn)>|n7^`pf_q|m46nf81F{=1nZgDVn$gz7pl
z>L(C1LCwl5^c(QQAlH1Vl;|?P6ZbWG8tVTbfhI5&AG7wniFrrvMyv{%qw^)va+1+y
z7y%h{k>T!Oz1b)7-AJg-kA{%fZ&8G%D?(=|LNARIDkwr<Jg5lmeNy9(c)Wo5$rVV?
zCZC_kjY>s$vK)aGI?P)gzD~2lcet-O8_kjCt<<Og{Ue&1hvmcMnE{u~Mg{h~T%8}q
z_CIp*SSk!UAbB2E4dm2UGRmAfc)8-#GA;TL)yF7{a<Ql``6oc+l%KPxU9dc at Oi)S7
zAO^iV?q+hqe*z1w&Tw>wWofi!A8~XX%EA+#3mqL_j4alD;mH2T(eYEN_=52GU)kj_
z{o^Bf14`~p#m*I;au78-b$z0acAd4ojCvlyce<f*5SuFOh?bR%>5zVK`q!A9{@c*)
z&X6yLR7ZZNsr5}*-|25O$~F~wnQGX7V}|_oNW}ohLE#P5&}*!S%im>crM*TlWywRC
z2|DEwpq%M0g~_`C40-Yp&M-X6a1P7|Dc+S}5aq{sHd~_E7&yZW(RcG&oi_&xVp?${
z8EcR;wb6`!Q5Gm0(?52RJ^WYZi8 at SXf(_`V*PX+LLRbjT>(qUDY4;BKLgautxOL^i
zK-$F%10yfS<LU+&e~=?E4MT2c1Lhw7w3*_pv^?*>oB{nzhhH>`vz&QG?-Mq^n1j0<
z at 1Jp(hr2xQZ$w{Lp~Dv}wC-`__qliW2CRE}rRZQ6**~Dgmw5Y;HnJHulvg at 1Z`eoe
z2R%z$vmRFV7om>qnT5DnNjG`L{#(q&epiOWe_fV*P7Wp9RqXfXIsCVXBIW=V`F04v
z*qMfoiMlgW+w~Ac(*yIBfwU>bXd<j&BT=yJ4X{(M7)ZNJl$@EOL-)EUVT*t^x3e7i
z`%QOTOgi{($q?`?GL<b5zZ*eP51D at PF2Xb)FfR&>oa}%o)7G~{zZ1`)bZ<KSm3Y{s
zJ4jM3PF)R3raSJ`{Ul<~FHg+=2xgwtK!w(YoD()?7G4<`IfZG1+#41K?wi!iEV3+f
zEh=MDBjfy%R4YM$4pJscn=%z1XC|BgQ=i_eV)oY=#?%n~G|~6%2tvm~I6|QqwnKo=
zp4oulu0m0&$y`gp90cu|C56TLCr!Q+80T#M8*)Ti0*#@fVx|S-yD`7`PMokRv(VHw
z2(VN4qZ9k|zLVg0LbE2PzsZbelTQEI4Ay}-bi3sG$w&`}6m>g@`L5T9873-2JBQmo
zq7((07Y3;zH{fx>SoXLe>M{<${$qzu|A~l)0r?+!#>;;@A2OeVW#}@Ct-C!R(xZb5
zZ5I7lg~|JY6HnioZ2q}<@F&Ieew5YJLJisBUkC;FirMd*+6thLn7S-e6m{FhS<5oB
zOn1>Z at 9Q_Z$2fGaIszm0&ipS;cYcX<s1Ufu+a}*f2vBWc#=T$keUa+9kh~!Wts@;s
zk!fpMPLa`>D~RDvG5m)2D=~Y!=sS68eUZbrD^*N&W|nB5hnTi5v;Jf=F3bIi?oQL)
zX}Sy39Ud@@w9<`$wx}2L`@G%Q>wfYBXibwtI&Wq(J-cvPmF=vB)8lI1)I!J4XjN))
z&O#G2`RI`fbx7p9;eE_T%tvhgbTk0+eM{(7Jbm-^TN?G&eZ9-2?Y>WY#K5#{Yjizr
zGTX1{Gnvz_u$j!u3Ix-NZrs=l%%&B1{xKW~J`qGmU=j0ALf`!Drj`ZaaQLwd%uS#X
zY(#SVX<nTp>fVT}Z>L{Bqj-p&mDniee+&DO1Op_5+K^oS8 at B1Fm7^A5g`=ilG~otg
zqJt`zE=<<3xFt6edq=lpQRmPNl0TmSZru*~BTyU*fHyYIlglz-kk)?B2d1s5Hp`&i
z_f)yPmtxF5e;tyrsm32?<1R4jp*d_Jbj`bX9>%*FG_f at C_MOy^MLSl_1h*>oc{&`A
zj$+7N at L~2K&E~sRk1pmvk)GDI`|$Mo5 at wLdhK2kin^bndqmAM1tiEQI=o at cBlf!f^
z4f_hSGvyh?gBy&z{snqWZ1*Tne0qnqhj%%`ol=J*|MkYF=bN@}oM9}&m>a at _@G0n3
z)7tHro_F-<9sb$)cj_VYW9dt|Y8-lj at Gcwt_hS+asp#R-Y|?AJe?McK6{4-Rs=ale
zXHDctgi>6a^tR}~&4 at N{pvUuLsDP33Idz!O+-gMIuoUMX at m#dfKW)My|3<br**L){
zVp{qkHlpQ``!VzJ-)iiAJz{7)Az<fCp2Gx`8hT*Z%XDWd+Wov>!6dq9)PGP|DtN3P
zBw+X>NSEPZeA0yjXq#~>jrtRVAL6j4^4NO3#CMxf at C*_CbHby<14!mCa1enJF9?sC
zPPbi#sUUQ98La$nC7wk$6BJ!b<k_Tm9nvw(Jp79@{0l}yWzWL7fsuLnd)Ljycx`6Y
z3C~i_)}VIG2xO2D(LW2zVYapjpNnLJx1l+NdvEEQhanG-=dR4K2AvX?z-sCungLcu
z1=#aXG at c(LMe1NOTK9No^iGvJMa&^+iI*jxbs6P{wn-}jbo$sQ`Bc9DZJHYcwClc@
zaI;yuc{E$r=22bHE=jaLmF=`Fl+XGBmC(kH9xeX2g_{#wxEEOAjJB@%taR)=0T$5L
z!=!kA6Ll`%kt{Z9(+t8wQvkEP=qxA?%NgZmxT9T&jSSf(w=lXx-o)@u`94Ow<k^hw
zl<gGH&T(T*WnCl|yP_0wNC%3gZ?J)9DX}dBDYRWJQ=Om{tc-XfP6Z7xpT7M_1_qO2
z=?y1l1IA)$mqU8pfwq8g6v|?Hg9+q*dDf+*@-DVAqR9hRLKzIQXNtwr_f)NH;qjGx
zFJN?ZY^(h%c`9crq^Z9D>C6)3CBM#Cb~*k3$}B^eN6~3=c?XNyDF*DBE1k4M>BODI
z8F12*ks8cSXcvaYG3gkdvapr@!4lgN`FE6$Z>L#KIhz!1$=r<6%j0?AF$A!ia_3OQ
zJxH-z$Tk>lF(-RGa~lgF8;S|?Kp_QoBjA)yP=AK(TQa*@xYvn9)PjoX&@N415Y at w?
z9?6tJMZZ4NncYWl9}A+YdAOgk{VS7BDTrMytQFx_a1Qz0n?V32po{ML;b`;<N_og<
z%Bc0MZD1UjBT-raO)Lp^d&_7<DS`1>Gqi<AW<E!L3Kt(K?Zy~Db**s-+GKASA_$BL
zYhBO#{+7%JRwqwqnz at b{pp*|yA5j6DGA#%}y(|I=$9<0GJ`eX4RHmEYFGL7DVg$#4
z0JsEUO1m<GW41^UWeWdV5W0wm6_npMY4}ksm})BgZ5kXS#HP%40F?MaH9~qQbF;$x
z7zkYyy+w1sO=U${QLmdag9`s{j$8M%q7VBK-S>MUq;&%(5eIlQc}9<tLIzQD=+oTy
zb5`=4mE<CHL=Ym3AjBpH6w6Z`8WGmip{X^G<`(tiz(o_^%fT=DAIhX}wbR4PmT>@R
z*M#kQ;N0;(!fD8H;!YiI@(9K7<CMndev7OPQ}Ia`#+qrkl}1fxa1MuQ#+Fr_f7;~x
z9hj8;H`WgvlZ#k>-c?xDV_tsRE&C0pzhQZLR^deIVY;XKA>k-u8z at o$YsqW}1 at s^Y
zT^O()BW at JF8NuivGL*SyXbxt4U54L-g)U#g9dw7Mg#Ln`<1RdE>6=W(G9t>LXQSIL
z!xbQObs1*!JL{g%H`qOj4hC57p-*k14%$c^4}6CjH4ST20d!*_wL!B;6m{o0gyt!Q
zL3fLQ$f9slqTZ}jqMey#K-aPWTBw<A`i9T7?m2$oygg`?zMW0cBdD4sCszIr>6Gc&
zTc at Ew<85I~r2-{*<TV#7L!3*MJ`=OC!}cMn0*`ZNy at ruC1%sLN`!ck5%(riuZv9}O
zgbwMjBOBc_`t0LKm_iAa2My1xMM^R73<<z)H#r_{q1}x|CQb{G0v4(0F}vh_+;2D+
z{gi*$FnrSRc~zjqi21=n)cLpcP+cnCB6byRQ_5cy>?+#LY~nB2NwER%mPq}YS}%e4
zF2fE)&}9w!hds-fmOqA*^-(chMM0_zF3NXe at cPlHe9xKk_G8)CJB8*7xm7IomIvjJ
z#vxmO(PkXbhg2i(CKw!u^aYKQ!KrXPMg_#|qIe4%*~R?brdE0qwMECC!noRc&$WJ_
z9Ai3G)S}x)+L~Hw!66m3%riY}_fCT)KD0~EG{JsS>qdO~4PWDMfq&TN_>8mDUV;+Z
z=^=`ZwbN<pCJetr1YKmOdl=KNDZBr|R5<Zd;D{ILnGyOcWHL+5W|6SeQx;^*EEQ1X
zD#jX(cxJ4zxItkzdZ#gmv`JkH{SW;>9^*HpBF;bT#8IWJ%W2Z++uqOp;(dY>miKXr
zjV*6FEAQVBK^K+xVcgq%UHzhTUBoZg6w;-77)>B2qO8}&i^CRgo2m5$IAqm4ACwoo
zi$jyqm$JgoVcaPE*0EIhTQ<`X4;*U5^x{)_?p~Tx-m;mdlm(-}DHU9RPZG#QnOY8^
zbD}48PppI;ddHwY at q6zJcRKwHP<#V>cD{ds&yJ^q-uuXS8E}_(r5)INTG);W7gOcw
zBV-4tGJ&gti54Ck%m3O#M|8cL?GtkE1q%+x-48-a6iZ*(vwsx*ffD5Hxy$#X*~SjX
z+f8$SL|#zD8JJW{%+i-dQa@#!B}%Qt4cpl|4(a`dg^2bKE5s)`=^|8|1+|8=Wy3NF
zOIT~idP!SNYxb_{GMoUY*4R<pBi~|rY#nXOMN}qlw9XEie5Yv1?JsyVULY at _sU-vY
zvUW-5=3*ZDv+oaJ-E?m?`+8Oj%N$|b4Y6V4<+0%*ap8N{#>U&?<dw(8Ump|p8#YwM
zrr#5n{<%Gx_}KM<-Sq6FUv8!63K-Pyfs;c0g$QF8rGf~yeO|(d6=wY@&gx?PF{A4t
zMoiHy=$~j|RfAo&v-x at stLftWuS_ksq7gZ6Je}v8JyY-f62lt4p0;YH1v9gmSz_xR
z>}uz~=N%_XcnDrI6N8`Ot!F4dY=!mW%Xax6xyOeN!k`q3uF>#}8XH0q#$fVwAf6V?
zGqvkw>pq+A9Vv+Yyi{>qpTAfy`Z{p;ji~Dob+2JxFU>B6Am8M>1IhhG_zI9Q)w}0;
zsytu6-rI5eEot<7I@>Tf2;Qgc{#*3)fS_nF*=D)fZra*I3q1=v)_qVe=nsb at Q`igV
z1x8-#+ivuo3`fo88G}qhYhc7Erdi)(E|$g~^@uR4ZnqOl11zs#=FmUknbu&4eiw_K
zS?M*?`VNmQi}SxWwOk0;abx(uwlAjT%)X6JV}n4q)2aImUvJF+5cVbeQ7qYaxf+YD
z*;g2Y$@fRZ!@RjM`w`YXy4ND+wPCKRF)(cj`gW#4dgEFfRw6b!=bP$%mF-57ad+Bo
z-<YW}@6$zo4^G)i$K}rr7}K`0FJSmOjJ^|LkC`MyP1f7?B21{UA@?_KKTjGSPcyT9
z0JCjltiG5h;^-UI at B3hj!5LVu>m3cu);UP<=WeYekN!md`wq-tItNXG7h}_OkgtD=
z=VP4u;9mp5H+kCqhP%NVUC&AdH^qdPvweG14m&VITLvkzaVM>Vnnuu}n0aEwJCxHj
z<b*u}?2UmJk7%RoV}9P|$12R-(1_S+S4<7%xk!O_lu?BP06Hvm4_l8yf1KxpYof2C
zlR at F_%H!p8<XyP)lS6wxrZeOCA}B8P{1}d at Xx8J2^4kXyYJMpL=XXyezvcha{A3>$
zXGeS?uhV483rX%5oifLi*A&VBosR!udF9jr^Zy8P;tKt5^1lb%XuRHY<mLa}{Q3S5
zdd`>TqTZh0it0zreU6gAMXD8xV7Gu*yIH<c(a1uUGU@~;e8`x{9^s&(&v1Dc?xOYY
z8I1?R8?bQ&$GZ|av0V~!YPM~Jz1&4*)YZ~sWAS3@=tkWQ0=0gD%zHJvS5 at tNzpey3
zb@(a=4i;PjvGROmM~~eD1s79r27->RK`5VK>mv8W at cw}(x5#>^d6wXrL}Psh0(eHC
z9^x!QMy)}+X>m`HbSbqD4~dWlDJu(lcFCU1#qOoC_N;t;4*I--sf at CJSkxN4HOCQ{
zufq&yG^@U-|AaeGcrhHb=XczaX4-1 at ot)z7ZQbWRf=t<z2j at YN^}Obdv%^z7Mto&w
z7J7p&dYpr9>f at JD&OiyioeSn;8m&V2Ara~G(iD_7v>$xRdQ}M at j)Y54jWo2t$msgm
zmlI*<Dx{{q6n)LPz7s>dBiI2>8eQcpd6YGX2-AJRpJ}QC?VmBeGz0=MR*VI2w5ZB?
zDSW??@2WqESh1qd5tuz44_?IMAB*FTi{n1VchJ`jf1Y$w%5!~41S8wOgw3hXi;ATE
z<iW5WP73MPkMJArD8)BbM4dEj18ziV at Q!wI+wJbEdUr!j7+snj-<O1c3!yvlsjlDq
zj~&Py9O>^c_OP^m`i46A(LPF?fm|2lIeb?L8-{in&ci)Pk-LvEFO%tfxx+tQ>^Ph%
zcATCgW*<O(;3S#+kC(7Xv6pl0Bg&%ig+oOCk|K+F<Z{s7!LT1502Ye*J50W<WB~t_
zY;y9Y>Dk6<re|-SF5=*%?xg2^vX}0-cf9r3vF$n!<}7JAJF>^K9roz5(drOM9eCKb
z$NP2f6Vki4e{mX9CQQuz`frL+r<e8StNlA3RHcadr#;W|IFIr`FS6h7z;{PUUa`N>
zOi90X`t9G~++@(Ri`nOl_}(Y*44O5M>qBFa5M|QVm*4Q^!PxQYeF|gl+Ctd~fdwXC
zPk(egP{JLD^}0S(NTGly8@?d1h`N3Nf`74|N`98_gt2i59>@fHYw?^x+KVSvRi>}^
zScCZ7hyNfSpQ}uDs|FkKSvfeZ%2f7Vm8l&6r{BW;LEPj2^iE9HFdf|P1$cUUm8oI(
zSGd`Yn~vqU>8LW at 8Wi*QdN9Q-#3aFUt`X1g at vJBRP473@hv?U;G!!Zw>%9U0O;MeW
zMRkG~)~(86`sDl at eLC5rwZ!^#YV_G?Hsaf)Tpt+D*J1J<?AKw&xtpH91Qr-IEmnEn
z#SW|BPzBov2`@FHcE at 5t%YzwMYMN}mFZ%J^$rhM6*tg5rYhb6%z`zchDM>C=l5j+u
zO1I0d`-zSA)PmW!a at G2dng8P{;3d+zG?h0>i}k41Yo)i)h1vtv$p7<xC%&3##6A%Q
zM6>m%(tfcyNKe3Q;T0BSPg{vbd)vk=oHfKL<FBOmr4XWW=!7&qHN$*9yi3f-H$1(a
zaMA(JpTz2ea}M-G%ScN2ujsAA0R~KMuv=scFDENeo@!WIV4JQBMl<%}JT$c))lNLs
z7&_CfSPn>rZ<h&$BjX~K?rXag2AYRqp-8#{rzNoOx!h=z=Ib4S>x~pvbEoZpqvtjp
zw7{q~pS4o3rP%{zu+|}L4vfxp=<qPE7xQB>8P+CM?N~fmkJ+U!!%HpFu6b=Wlkvz1
zdt8UD`>+lPFU{eP7j-)w;ayH<sMj3QM>hZbG#Vw($D{T}i*=6`kE8XOp?gp^Ja7p;
z3aWvm4mccZebn+;>x<0qs%&uhbIe34&l#Dx1#XAOzv+E5VGBfa>mVIG7sk$qFirNP
zoAF~5;SOwEdHXs(<^6 at O+o20OC4DBDkohRDjT^9WKsH1tn%SkJj$N|pRU={vA?+Z;
zss0LUvBWST`5YbuL5aC-HRN$*N4B2_Q<B%k+OpS+lVxzvIKPW_j;ucwO)qx&`sPH(
zBY5tcviRI`eu?8c#Oyc3ZBzLr;-L(nj;~Y1ap)7#I1mWoCoDR|=S}<)$L$rj>G*~7
z9oup8Fps)ejBlbZJfsm{4(;EuO^4aNczc+3;n!b;egZS76QUJn>{!PbpxPVCPUc%Z
z7;}~JQ2`*tZ4v{E!=m*KjMVvWm>%HqC76RfX~AIm)K(0CaG}fS`!eG-Q?2)(#T3`@
zD!%^QX?PPyC3FQex;C16&-EL&0%bur#@$B|2%~zmKatZe75tK=e1KA-f0O#Eq-6`H
zMeP?@P^j(iME?SdHmuGc(acRP*CS)me?7+B4?I7|SGDnQ`#To#I<ghJFjK`ugvOL;
zr8($;#yX=Te~zhTC?(I|?=a2n5>0dWpYCw@*JBVq=6&C^bx;1V&<c!(cim5-=v0>P
zgwAwl4~4>eLPV*rI}IDWkCHSObb<(86#MT;M#VNgJ0Ej^Tc?Y<E at b)=wRib?#i*v1
zM-ajm06T2o=P*6FILp+EO-Sl_r1z;vn$omhj$*JSycJq4C9Uqf0TA}sn+ujw?%qme
z8ka$NoTRM|bgkGzw>dEx4lgj`Sk!l-FCV+Uv`;~Y0?%^zZZjkNXM{2FdjbUKX&lme
zV{e)$<tfM6Br0`u;*5FzOUuFM3ucJ33 at _kz-8{eH-S_wu?seSY%Q&~a3cf3rk<n;9
z;G<)dfl=czwNR&o?2&e;k*WDc at 4}Z!>ps!cQBX$G0}Dv{b+|R%nTJxcld2A17y7*u
z)*$$v%*QOY1<#7g5PQt91lj6LElUy5C>yfF(vkT+dp1E1b2c at Z);*|ty5XL)2DV=0
z*MEmelBwktvP4%Wq~SLJtikqTsS8sn#a}Lfzu?>*`3wI0PH!*vFJO}__{+ZhQO}{!
zrn`Pm0{kULF?+A+&L1csl^8?6MN;N3SZ8z^{&0Y~%&$P0%iN5VXSmGwipxyE&I;P0
zspV>jD8?ydsS{>^%it)TsTGeg;4>$f+c^9y$!*l4wC17=irYZB(WKm+6 at ZxAj3y1d
zuBh8Mq}Sx&Q}p-*J7nZMrk2A at _NAh3vBrTO2dH(RUn2|$dKko^+g&UzFoyn)d-#wD
z_X+)p?(u!Rr67vm_;9<<<~Kb2F87=JaYKG{6Zi%#A$IbcD{(dfesdJW8jL*m4HZB|
zJlOA at 6ZM+QNVeiNm*UoR=NuFgKT>g~%XrT{X1Ex+={)ICi5I}Wkr37&)V~@+zs3pb
zm=5-0WTEL`4WUMG#PzT+Txb>GXm3OFH^YCTP|^O)e>HZo3m)AMrKQ}hAf{&*Ph-c;
zZY5ihbCNMdX_SOWYw#s<jNWW at JhR>2Pk`Mt54-fk)`6wiKcuKlucK8}{%+4Cr{7S*
z(6pd!8#qy0d+59pCf00y6b=?+sR7XYeeXZnXkdDF{dDT}^6-UE<o*EC;PK;N2%(Fn
z!Kqcg6Zi>_+Yz)L3%y1Jy(&-V*)0ar_Tmxj!FLFMp6NSL)%W%=g-!ELLa*ImpvGit
zn{8y{{WqqTdGKo5KSva%2XDL#W at sGrbXn1SG2UUjlCNZb=zR*S1+VWQe$ysuVK~jt
zkrrp1JM}r_Y85<W&(TqEStN+B>G=&skzgtulFs~n<NcR1CgNmO2CYwrjbl7RI`vlN
zjNx7{)^p56rk3}xI|$=^`!?wb<9y>g2;-dAvjctC`VOD6?u(t at plok~nGR at m>dyr&
z*sccc=aPm6|BQx7#ezRY!=xjA-YS2;cN;C5&ZqUJKlgUVunzLhfr8sH9C<?+-ue8%
zZzxAR9sSVnmcGHsP3`gl=jE_=z@`gW at CbD*j8C&-w(~MV!Kjn-VLkg9&2V))d^@ms
zc!35lB>Va;eCrYMg8Vr?Sjt_14 at BUP0Y|3Cx*fV+JoKOuB)UH-Vm$!g8;NOBVA#V*
zh&fh=k$&VrzZ?55rk2|v!Y*}5Ctz6{HXvcyW4%SbFHSiFb91c6OlCR(S=cSYEBJkn
z=Dsg_56Oi%C)uf|1Jh#uaqmvD;l)_>(+`Gq9E4oh6EaN0^_O#AGx at _2?R*3K6|my7
zc9EtiH2?B1$iSi=zrBFrjGD`Dps(nkVS0Ai3Z)sh0jTnwG&T;R{g>W4X}|Aq+}c(%
zAHjNZw2qdyI(_2FH7##Dy>BO+T%+ahr{yj5Gi{x1)%DZaSe)(rc1OFf;JSTK2)`e9
zk)Z^4*d*5!ud2P%kPlQB>7P0c*~A|SO9dAq99`eXuD4|Tk|26TELP-s>~4pABPe}C
zp^=4m$v1NjqkKDy>6Eh>-6dNY?JCaxn$A6=s&>o~eV^g`)nAd!kTd(BXnMY9w&VEQ
zivpG7jZV|tgYq!M;qeXOreAdg(j1Prj>!G^T74XN$T<OvEjlFh3L;TxQWD|g@(V01
zWA-m^!7U!h$T(m%p0s=rUwClna2)!pV%_0l-B*z>KhXEqVppG$^+nVkIJ@);JB0SC
zD7}Lj9~})ANsU5Bb{M149Eb0VpzK7R_^z;+9b$zLkH1qKsGO)2E(dXO<v1c=a3*-j
znT?ZNU&wv<mOm>R9&}*K^B at bNWXI+GxaB3glD;taAniZuzQe;YJil at 3dd1YyIMjr;
z$qL4{M){8k(Jx8{=Huri<w5X)LiP)j6!!Jd0-;bSk?*G87N81vj(P>9?=;Qp_!*+%
zf3*L`_Y2F3QF_mr{SB|j{R;!DrW!HTeorn!Tuf2Lw|bRdHd6KsBl}qzdBgvWydUr_
zws?7R&zaz7noN}TjGv9j*6d%=&uMDIk>il4U3R|kT}(gds9GL9YRhqS!3O9LUC4 at 6
zMJhSg)k?@A714yAeLo+O4lHJ)GY!7Kh514D+hnJ~A|~H7|2UEXY!t(2^-1S0=`oOz
zeVO*U{osWwM%pL%^Jtk`h*RQlx4 at _m-XaHM&&qM06o<u;92sNV*g~4p=^v^$bmw#$
z^$A5 at UPK=Iy<PvP*Bor9|NCEQ_+}WL-9%TugVH9iqy>@|NLnCifuseJ7D!s)|3M4T
zdtyBQ2#4Q{`~Op1I6nTqxcDsyV~fGLqS#X6uCKythAc(&I-Z%9`Q->yT0HfZ at r^?T
z_R<}^V#k6v`qaBOOpZ;msNUn684*xf?kTs_)q5=T0w28Ohh&iCnUFM at M7dq%l^fL9
zSV?DO&Rzn<l3OVKP+>m33JLEmVmZ(oiw07DiMP&E4W9D4N=tPeURC6+b=7&;dxb3P
zT<%7^U&vBh-B?@hsaOlSuBHZ8g$Jb_UpdjT+Uv34g+;g at I-u;RVKlm3G`{g7MZXwz
zt}b`iRoAVVX>r1KZm6=3zum}EUuD6|nrhb7mNqDMFRk>}Hdrdk>nt at e57`VaX<EJ7
zh4(hCu at HNM+qJH`-rHES!9uLimZ!8ZgEQ+|imE+pk&(C2Wf?!d78ypEn9b_lmWs9I
zb!%L)@M;&%7Py#L<c2z`ufyx}qD-|e_Zm{8x{+BOFJ~IKlyN0O?chyTHLlY7)i=Pt
zSyhz<<14e3it{!!)Vs;#P_d#OwN>Xu*({A4>MAU?^_4Em`nB*JO~#egjr3xs_)3Xb
z*iK$oTfV`v+694bx3__$S9+QKR7<JyxGTZM$<XzyJ$O}?;$(RJ)Qw2xBD`?}UJF%g
zsj055_QZMozw^_J##eG*7+>Qp9bd_eRa&xS at qCA~sFXfpkrhQt<}O~UfRRR|G^wTV
zLsfUyI2+16Ye_ZoH_N)}a-~Vd)P|CBG2Gl;4LjD?L>+~t)+9YsQl+b^+*{+BOqsgN
z*9+ro6!jx|MeM!2j;ezsP-DS6$Sm+7W<%y_$O;}`NBUWoH<I(fF)duYg;?S&nn3W{
z`bG~E_&=7uthmJPSdzd~W3?yM6m|>VNmlKyucPk5vaZ}+O@*9jal5MUny|VG#a{<5
z6B##AnZ}Q=ur#8NsjjL<>!e1G7RFpvMJs_+xTrO-PBNxOBc`DyttR+yiBav1mWUR%
zMyROAd(+S-QtK85Dyo3jtg)WcR^kwr*WFlGzrGIdLL+q|9=v2(sbi62S?S_M^WvPA
zw+iZ}<GO;1ddPLTOIKHyH&Qp^t%GV%BF?+GfO*jlfhW{-3`mUufVu*uuZEjO+KAFY
z(Yxj*;r)4QAb+xDp4W}4^U%1lxC*b%^H>TjXqXc%?3HvkQOJufZ~cVTvA)7}DdEFn
zG4BJ>6SI~$zR^;}hDfC^an!9tj?t#Z`eZZ?>Tz5``D%fFtIy?*%_35<@Q6!27J8GO
zB78l%aitq9Up>IE!c~hpG0P=l1<HFOOss97^zOa?>yq5A_0 at Hi1IUS#<lpI}q-(7A
zy5Z?D-5To6Q;!a}7V`|H4VA7aDV at 7~{`{gPrAr;x4D4tBF0Y9edP87L6(z`+W-4eG
zXuj2Os<;+>rlm<L*Ig;kGFL5zvho at gZM0O@YsWYhj|^tz9t&?}lPwNRPB7d<Tk;c&
z#+T7XBHbp<6DCon+^gzgB=&Y%H=K6Cyu~ZfHjCyvR%-7{mo8i4C{acl*s|ERvUJh%
z;?fm1=hD&=X4PeJRvn-YJ??T0hjp&?=nHGSu0|^HWQzsuvk_wp%`>QHuWz6iHd-dt
zlAe}H>l!BUR@*qK!R4ORh}Sq4%&xnF#){}Xk|rEXXv98hA`1*e6 at mt9p}uIcWvGA&
z9ZjSx^_ZNY1>yylp6Uv4v0kN7FbP$7J)~7fpE`Q)rS5{%S at bSqdUz=OIvmc?3qsig
z;qXe(2GDNMcF+va<_E*!2GGr*?VyFf4~Mr94l0B8ftqoUzUL3&@NCd64~N4opyoe?
z!*7E&fPN3!4LTa9b2tA-INS`H^H?~%8MF*^8)!S|0nqOM42SVEokHQ`*t7&~2dxBc
z{(CrlAB9051KkYzFVKc9;cy>l&XeJ=1)G#Tpe3LdJoJ5)C?3_0#8K5PpoO4?IEm5(
zx*2pcXfw|3%b at 1xk>60*<K=MpLC`+X9?&wJOSPOO2tm+kpaKpJJ3;$E>HF&?+r#0<
zDGZvHhWOp#FujSZ8MFm7_!{g5+6}rBv<LJ6XdmbiP~ml~F at _<3(2<}P&@9j#(Al7c
zpe3Lspp~FypiQ6+pqq%k33;GfK!tSV2Rah88#D{lybtL?n?ajF`#`sVHek!Qhr*y{
z6VhYzcs6L^JNRN1XgBENpgo{L&_2+&DIS}@M?lL!>BXB{K=VMmL7kvIpk<(apdL`+
z-Eg=K)C~F%XxV{qcsFPR=!c-qpnah2p!(sc7tjpQEua%Y3*SS%f%bqd1MLH)H<LBI
zkNN`@K0y6}Hh+Zr838$<B4{7zD$tyd;lH3Ipe>+VKpz4Pg3|loyFqt?_JAG$?E^gm
zYB`AVoQ-s!!49CCL4%-MK;H&6<0mC$&>YZHgo9>|MEUU3n0cTLpiWSW410hUf_gwp
zK-)meKpz5a0HxoQYQ~S!^;0~4g065B;z5^zHh|Kb)Y?ItL3=<S11-ekf^N_f&_keQ
zp#7jh&}rwOT%aPTfCnIDpe3M96vl;Y*rmhoKI at vyx(m_<8{6 at SmT>xchlXE;!<P_;
zeK2Z95Hj&U4gb5_Aq$6Kgmm-#^o)h3v(_7%g*oTXx@>CZML=2lRdj>&EFs<FBAFii
z8}CEi5 at 1U=-<dLR*r27z7(t4^5C0T at Kl+qNeCoY=;-L7a@!$4fI6M<H67N;gj|B8K
z{vZ5hIE<=e at g?a-ruPv9+mWu4 at VV(`TY82q-Qr9AMM}ylx;4*DH)bJfB+7pPd<Thd
z5!Y{lZ_p;gFZB1MrtC&eJ6?VLjZQnF3&H!)Z^Gf%iI<7Un44~yo1Qf{J;#=wXG<^i
z4c;^;z*63slCq7U92N~GutEu=6G`c7IGMjize_efQ;t2oU8fb4Ix}S;Nj9)4Haq}2
z?V)h^I<A*5r7_(IxofyB`VsFz{I!wzx^!bvx_K%4TnH}s5&kXE=W+1u<#oZ7j+W1!
zUJ|3$%G80>DoQU|I1p}2FQNKuK^_@@42J`_r?UG}QW}`DE7FZrpN}BE4e?YrLUFno
zF!O_d0pAS#BEng^VwP+X;)(Y|#BV`-0m at 48%+I3rcnj*$mNv*N6e8M)F>oiwr5lkZ
z#{Q at -DkQJ`J5y3uq)^SGexntLB;?CZ$mn|nho*3^=(C#XGZ8_0LuBTo;qVt)T^2 at d
zmwKsARfoD#;X&{p`b#*B;gVg1Jr1l#gXI7V0{alyE-n}K1E03T?exD$g|-*#(k<|F
z#nIr6q|5h6)ASg=R!Wi)qokn(_oSqtH8Tj#VnH<V(-%gJ7>ggp{TclBSpFukECYWV
z_ at _s8(zXeEdResGXcN~D>>6b$O#?*~rI%F?gd<;Sk2GI#{xux_4)-zjsI<p8e@%IR
zAW`Hy88E#5Z&9CQzBAXCdMmG`lm)l}-!%N&!Mm1tx$iLED^-_CeT!<g2#%I=W2A~%
zf3y+lf=`6Q%OQhwW$EVolwM|`BA?3i2>ANJhm(r`oiYUnD$^<?Jpws2XYx_KpdH1v
z<@mnp#6WU(y!sk?zIoKG-I=0xY!F9%m=klhAofWQp$-PH{etvhY#+8;KTr-xi;K$r
z5ag9%u6B?B*e6ck_`2LZkhIxIdI)li&xFHuBxk-x4%#-=Qb6(i6`Dqkk${zywZ%-#
zDf5v2DoT%w`lLKyEx_^#fNty2=a4Uy0Dm0#VZxQR1>Z-1L0)gzq~!EgdKOw2D-H@}
z%miy2(p7H7JcXx2o25ofW7AdY*hFz#!1oaN=#<@nHi>OpOVZa~lipxUZ<?Flth1-L
z>E>dDa13N9h+Bz&Go1QE$WMi at 3?P3&dbd8N at xGXTKtDGfU$sC)YNl==$u>l>{;>wK
z+h4%k1)>Ez6iT_7jWg8V+7S0W;%4!DSzFMIEvc6nSc9N`@M-WGJHlZQb`iE40eu|y
zAu+^ZeZWp({;o}{$DF at E4$A=ceH=CsSYI4Q>xd(9*fL;p97b>OJrsv+1lFU$M5x;i
z>_cGmJC6g{Kd$dg?HO<yfTZM8A3|39j&S%2+><Xx=R50Emty{{$J*vG at WHj%MfS`9
z_J{@}c at u$c29`%$@Oj8vkiI$9hjFHc3B+86#*1a(+XlWhoKG2FB4bj at HJTxb_*=l=
z|3Bb=8vJ?qPV at Q%@^6Tfe+c|-|4sfZ$Q+3^+yU at EP4X4Hsco@MH5 at ui*4_)jTeB-1
zzJv2BeH7X|^(|_9cgMDO>L;3zZYR=h`giG?V$)H*Y(cuwo#8Nw#4f^i0?W`~)IS{n
zHWJu8;zC`epl*n_54aOJEtlvbUG!;a`@rTB5aTCGwW5nEO1EHQ!1`dN<&dn|NY{XL
zV<{aj!b*T`1cq$bMOY=UCSY2fn}B(MY1`o at E>BHMb+H*(4ft#{X2s~1nyb at jMESf5
z-uA9oKkfmxDGvJ{SX&%s#Jaf!7^2uk@<sz|1_lzrauC=EtX+fIf%OCXg9cj(tYvp}
z{zi7L0Y-ZP8oyP>2IecYw!mztT2tq!Ga3gULAu>YCy{(s2biK+B5REJaj;>aPFO_J
zBama>6Au48QQxBJZ!vXTnW%3e+vH)t!2U{X{W*aZ0=tIv#6^5%z%~J!#SjJa0NV!a
z0*;ZtwgKA-Oq~m*U?D>3Hv><5H5>+E7hz8$U<9W1o!!9nz-aiOi}*gIL>i37&pu$M
zkk3T20sLv8_L_R5PE!f+e$)@<#W7=c5ul~9kU;WFupja&_H(Ah$VXkp%r#P9L=LPD
z`DB~K6`cX08__aRnYKXwLvP at 8ChqAXY$vb-z_j)_0PJmG*N`5#h>!MaEZFlQd(%aI
zIz>zbM)P*wr(<eDU3!XJ?b9i37Cu_Q_bBn<B5XE3EZ8$r+bdxu_!tfBAH;-<uu6Pv
z1E%InSQ9>W0 at Lce30N==+YIbqz|^sc%KbDjk~cWf`Xa`UQ@$FgJ&>GJkkgF4u5R29
z(5~X=!_N;S2O<PJI#c62v3<u%V0sNkWv`)57Z~$<wBw&)_Cs~h!qd;keQY0hYov!G
zzQ at 6rgY?&)!3S65oipk4Hu%<pPqi6gv at cev!DtLW1*{C%&$v9LEyVk3%7R!gRO&Pj
zn=36y3zuRSmCp&R8CVt}_H;`H%13x5 at CSj53SO8pPifm5fz#e1Nb*WrAZdZ51(Fs>
zS|DkGqy>@|NLnCif&U*_pdHW6>1yYTff63Rbe)3F;MC6P&p4$eAYE*K2vps_VGB=$
zvE{+87Pc8F2<P$eV^1hI>K+8Gk?1;KA;LBuKbnWtb?OhN!}SC;;TalT>VCrkzSzQ}
z26k=c@#>z}Ke?jnS~-LB;W2&8#gk#7ufTly9?4H#L#u1Mb9rjoccV+ivv|g;-dS7_
zwuITG=8vBnVC4bGSWojXor9qZzlFf!RXg|b at ZCKA1P`nBQ&RoE=d*{`W4uPwxx(`~
zUBPJ;r|USqlhgY-{R^jCIql?hKc|N|J;v!Etg-1jhto?qozCfePFHYR#pyau at 8tA;
zPXEH`R!%!P-OuS^PLFXqh_|P6IK70^>735zbOonXoUY?k(H>v-d{Vh9e2UYv6^}Ry
zLU*b^bziT<Z(gcUfo<qv-=bUzmvGw6sp^lM`~Nd!W>e31TW@&$L)9*YXh?MBC<Nan
ziM_~%+Uu6 at e^t8hnk?6ov+v!RKV+l9$kX+qfzqYrrTcR at p!Rns-wAv%z4F%|sQEW3
z$-*xtNFSeny!>Q1X at R5#k`_o>AZdZ51(Fs>S|DkGqy>@|NLnCifuseJ7D!qkX at R5#
zk`_o>AZdZ51(Fs>S|DkGqy>@|NLnCifuseJ7D!qkX at R5#k`_o>AZdZ51(Fs>S|DkG
zqy>@|NLnCifuseJ7D!qkX at R5#k`_o>AZdZ51(Fs>S|DkGqy>@|NLnCifuseJ7D!qk
zX at R5#k`_o>AZdZ51(Fs>S|DkGqy>@|NLnCifuseJ7D!qkX at R5#k`_o>AZdZ51(Fs>
zS|DkGqy>@|NLnCifusfg_ZC<%Z{AEx*7DWfI*-?4y?pZ2$vKm(UKX_8dimv(bEai;
zSg5EeuUlhT=W;hz*VkFBd6RP{=d7vrShCi7JPnOAr%XX$t#|e0iu&3qH8ty$k4X*g
z`Wsvop2 at _S@2Z$~d1dajsn*=fUAa}c)2-Iat=7uQ8Pld$S#zgWOwY})nlZzbm;HZ3
zUQ;WprkCeV%c-oYthDBpmrq4bIhW^@&zL at AYJMd$EYDkAIn7$0t=LX|p98(GLVc&g
zc;7{LO_}79bUHz&8#GavfjITO4X at yZ3hKKXRK7Hw<{GLSH)NP at lCGe@cHWrbqijW2
zEq4 at W>Wni7r|5Kp1)~nHav4S+BO)K?X5n=+gN1W*#-->CDY!GH;e9v5Mhe4)5kv3>
zh;wrW4Wrk*;Ladq=vtebn~esYK|sE$z$`tb8<sO^s1BS!N0WqXAzk6lj^Q3jIa{&}
zgNWo5r0Y^yrUghcvH)+^Nzbv3NQFS$WeOvNv-4pY%S60l=lp!UM`!Fb;X+|-t}sp!
zolBxq$EBPFbp(9m;N3Y$i#y|asb>jzoyb|jh3q3|%sG1G1Z*}bH(WSZxG-nb`Kdw*
z-Pw`Jc`1`f4n$CKahGD#QMl+rJrLZb=2Lt=-4YXC%7Z(dQ%|4g5vy)EeGa~gawfJ5
zbCzE;2=DSia2aLlV3`(QLa+BhrmHAZ-Bm25gFXfI{Td1VVR#zKpj_&^Iq2O$DR{5Z
z at F||!hAFibl@*g}%PZXVlPfsm0EFJlp%=b6<9#2LuTDr6R`M4HS$Nuu67bXOlv0HB
z$orMl*SpM4z`ua=_o$2r)8)WF$uW~a%|-7%CR}}QlN#m5jZPRV6rzIZQr|OjE4v>R
zdCwAy#0R}!nCTfOpI$$s!`o!l_cp2g-MAUZJ|88(zXeY63nwcQzEg<s6Da9f!u1St
zJRJ%XzxraR*ExPM$GiCpqgL|DzMSKIV-&`{dWFvCc;OT!9N==UQRS#Omvakn(#MQO
zNSE>{2+wi;E!m0y_5D-b9PgtS1>?Gr%XuF-$+2)bTpgj0;~5-R-xGC;%PG5Dk;7l#
zBT&b}>Scn0zn~D|GLDx_Q~;_!x(YciOj5$rA(yTdzz3?YngsZr3GhE8z=xT5J at Rr1
z7M88>4ES07PE!=&u+RaV%C&i(!Z(W>^j*&1!1>h*_$)!bI<&)q%5_cx{8HdLI8`No
zuaa6X3li|_(Rq;m4Y`W`Dt}o5{&h_LSi!^jx%q{AINlV8|B>Sx<K#ch at biTRt|z)v
zcI^aCc2nQ?q`qhTb1tWu%dv3&uQ-2BxdN!~F-t>1NKOgIn>c?K$D3CxfcoCFSsZWY
z_&+#*F~_^P9Mum?fe%zK>wuFzABijPZ5-bmXP*ZW$Vt?H{*%c$TX_6luAqhtq)!n1
zWCx*2VPx<4#_d6lH*j2izuRe!_lOF=+I}<Oza(eNr3(H9ulQHdE(mW}qu^?{K8^DS
zd3%3Pi55y!IW7g5&+!J13s)(C`hL5+Io?+ii$AL3oL{ZSmpE>|Lg7zEn65WD-ptF(
z-c1fXtl~=<8)hWRHDZubu6CCaR^R70jpOEP6nrAuEnUkwUbajL|3M)_4aYZ6RRABi
z+dV2j*MB(Y|1-yPXh6iJ+W%#aH*@~EO0 at 6+$1QRECpcb611PR3Tu%!MNcCmra^@+~
z!ZcQJt{lgR*f at V-oc!xJ-czM8!wuL~$MKRl{x*)YfgYS{x&F-Y_Bi`L$MLc_IrKh6
z(%- at rZ&tDuzT&t!j(;#RBK~GxkLr6IGgW++B7mDi$m6(WiGr(swUp!Sixhl4&$pW6
z4FwAD0LPnDey0L(cN8ArcuuZ at A3}ShYa7Sa_u9cN+10JeuTa7Wvg<g<3puX#d*g;s
z|8uSo|9-;DfRi19*T>44!|`@*w-ek?uIBibixvI??ixSmcv+l24{*GJ%Te`wisQ|3
z_-h<Db33d2-*UWxH&At+FccjE>E94nkC(8HVuWD1S<zGd%m=Qcw{mV#5rr=1a{5Fi
ztmeCh<H3s*T%AYV&T(^Gz5klyW!(Sae(ZXV<IUVYD!!lN?JCagd4l78+)whjoS|?$
zDzAC50zA#}^En>m?L~br-*n(3QsC#jzIZW&B^(#Hosk{8Y6$1|T5?+gyq(Kw=M^IH
zL{Bh&%yZ6G0Cf%fR028Oz=x-tFO)s0 at N@SUK1#rU61X{K_!;#(0u6Z}{VxG-;ks$b
zX>?E)%!>+nzG~cDk^{Rn at OEM65vvo(_i%m-4bZp_D at 3>p`0$jmg2?Cjs$cz$^PAy{
zbRATPu$S?lFRbMHsQhmcKj+bs&$*m3J`Qf;ktY-I4})V+xy-yB at orAIlyI$Jem at hq
z84GK5J*BePfKz>~jjONa#GeBGr&Lab)+UhOOgQw(S)%Z%@74VS$NRXRYQOh9!%c$v
zeqA+}U;_DXaXIaLeRM5P at NEMAR7{CTpUo>3M%Dhq30E`I&`jWDhmyGZvH~BMG7h#-
zn8))pPT=O0(ZaSkf2&L&e*>4JW~s`#kIA_}cqUHH-xA1qIRXAo0{jcYVTWM0lJhR!
zFAYL_A%8B6TgQzCK9GH;5Dq!rS1EGFs&)WwP8lQQ#ra8v%Fp|;cFunX$1N)q;OE?~
zzXwkGWX0+8pPavh_j_4f&RZPc%*&<vRfurTqa~++n+ND0hL{KDA5A#QYv$uVpJoYD
zIG)4(@H(E!l?mjO0yn2zAUqaVzv~n5OI*HXjUrg&^8XIp!tb@@Mb59jH&-2x-cKO^
zC~)#~^}W2Rtdj}&(}sheUW(nIaH{?J1;7Wg^P~iLA>q^>`2bSMGhW5`;h%hbxu0iH
z&iQ+I$Cl6ey}-#&?7SaV_1VPv%Pv(Ii#Y#gj&J4}c60nyj&I at qiEfr%pApXQwS?Y_
zJP<wvAoT-H|I~7$u7I=lvRJ|I;L%fwAM3}*_&QNdmBQ?g^%vslx<nyD9+%VOQ~<Sp
zmvMXx at 89@xMOZ^PPpc&x08&4uwjXtUcORG2&HHC`7wq~Qa2l6Qar3U7#IF_1 at 82bU
zlq-IG`78ndF@~oLW!xWTaydT|4nHa3`dq;AQSc8$opEIo4t@(4ppL(t3G%gZ{vIx0
z9TzGS$X^G1+yLYLFByKmu=z<vB~{K7z}dLT{ZmzVC&`DNeSAKt)<X~BDwBqu0M6zc
zygyX^d=M4_1My!7ob=4${JfhNrV~H4yJAH+?^Xmm at Zs9=y-bN;nLz%HjDLWB3^?`w
z@&11g$w9tm-hS(N+5QOJ!tb@@1<s!nr)L+(1 at 7Q#Ki@;RRsg^ME&*;FB?#)AS)1%)
z;3Kf<PhFh$%4Rt9=ksB8J$MDjyW`s9HNdI9^5Vv?3c|I5)%&FtZjW_xy--?OzPh^9
zQ@%zhUEo|i*XArOZS<}#b(A{l8a?h3cYRfLO?BOxC3uilUF(`$(a<0uWuwPiRfYZk
zN|)QUrn=GNa+i8)ODk&X>s*ZxSy^AYrlx*%c};1hr{3LITJCKU at c68u#^rHUPR<#a
zqqM5JuDZ0`?JnO?>Z<d&Hwabk@>*AErMI?r1CnS$rQr0$u*N)<h*f=lbq(|uP!8AC
z?rNwi)ZpP=)jY48V(O5Eu*T)l1f8zBHJ-I`pwLk6Zgkn|Dwn!Ek#runx2~ex<3eIL
zR3=;?)vC*Ds&8^B#&gZDch9Sb_3FI!-p1nkN*BsP&k;*W3#2HGm~cI*2QvVJ(57n7
zWU;()uFF-ogd5paDKH+&=xRKbtoM4BxIA8WT@)60uEj3*8rKrn&%G|xPNj-64J+I(
zi06FvYB!Zs)fIx5LUrg&0#NZnZEbmj8XJqAS+H2cGZkR at 0Z`K8jF?5JiWTMVGjOHW
z054nXnhU>kxgCp at 0xzyZf*LO?i at n@aF03daVj~}l%eJJZ+*4KWu63dwn$E~#xu?43
z3=lwEO>w8DmeA-auWg9S!%+wCsw+QJ5>pc5)|NLes;{%Vs>;1J9)OJ{F85N at IYlh>
zlzXZx=GCL2;Zd}(*y~wbwFutQcy+nE4i(Wz#aGI=#8r-lr}4Ci5qL9Q;;N{3R|-p(
z at f%d|B5y6 at BKx4?DA%jq<qZvxgV=}wiY5op#HlGMN#l}b#I8cb%@J3VXHg`BIJ+p7
zhVqw`uVL;bL>olShNT<oD!7BirCXsqlUIDAv~>OwTX9io(IR_k=@}K{@|6Cc(yiV%
zlB|fgxYMPG=s+T{5E7T%=x+V7zRY8f$F{I{t+j6f38{Bxy2sP<On0lhXU at Ll4v7Q;
zL_k1-IJjg95E9XbEB*o^0-}o$@dZSP<yWt&-t?=U<pNsTeLb&VRlQdqzpB at -JrUK#
zHRiu*0EPO-hhv0S9w%6gl0kcE<pOaXlMEXtX>(qT62$S6LggkOA^tXtTLKZ)oDOn^
zS)QDa>IN})l>$#j07vFZPlj1#9$D|hA{V2ZY{E1yEXV?909KuNDVPEy#pNhT;5cr{
zbqwb>(-EZ)mWvVyVT8%d8HqP)Eu#dnTmbiwJ^(gN;7IIwkKh&^&a-rKi>WM1IQ$sd
zrMc$?JwZ?v6)E-m*}Su&G*8U at pQQ72RHql|qNwi4wd(8Q at s$<F#`T6U%j_&7UJ;=1
zgszr#39;)5rL&}p5&2bTQ7mPgH9$)jnC$ZmU3N;e6d7u!!)(Y=Yng(F=FMhe0`IOZ
z&j2<aX8EYPKkL|*<g*MTp1g*_fbGp`(j^NIyBi4P%NZ8^VLmS=w*>E}qdY-6bWFUf
zT1`~xWQp%?R%yJrO^}HU`voO2B>rL%j~(!ni at 7-QfF%fL2$QTHjgdwtKJmtj4VgEq
z?Fjfg5kh5yA3f?{oE`B3P5_ZEwFvZzI0lEYQ*)dpRQd#&ou3nq*e5K;O1Gofx~rVh
zfPF?r-28;Bm;-z?{>S$KZe9n9mu2a7hk12WuEc_dCyzdik52`9PX|wYmH2o#ygI&)
zultAR#~nDPxwYX`QE6Qp at pR5ZUoqO#sc@*oybLp at b~pzoc^&9ezr#LpAm@)T=V?(l
zRB-S_9wW}lX^-zVfY>^%Z4gezrC74&p^2e#C1k}q(tLGra(;Gr6hG)a=<TAiP1B&G
zPAe`Ny>ZyFhw%MqiYoJc5|E823_r*NS?(EZ at 9-*+SERm7t12&Igd!leSPvx8sPCJi
zjQvuk0tEo`d{W+frf%%n^+mj{@vkqAsKURd2(jyJ0qFvOTt+US18z?BiX;vaEE7#S
z<7`wzt}B!581f2ajv315bei2o$9eLU>ZE8o$(wmsV81Nq$PUfylE=d^)*8M*g<m|u
z$AGAtIY`KKWtId?<J5Wf)60v|9oIp=iX+t`H?(!T%7F?~Y>evA-d_E>1t5QUZIVJo
zh>Bf8ind7+tOdUdCl+GQI at a8d!qxG?cv>};07vtWk!`_nGOL0Cva4b3#+cm-a2*d0
zKiJ*d+1@*TxIY-|4*N^zQD)gNMIF_7S!H=M4VbR^9QBX(`iFbN?c?_z?r#g9&@&PF
zVAE8csZDj~74TkCAO<}~&7@=s*ti(e$D7gM9u(O`eUjS<{)>TM#b><<=th+D5crM>
z=62Z}bt8U&%<JMEzD0 at ev?y+^_rHFxs3UzX;NT~kjOKs=vlyD6)G=iDMHxH~;&eD)
z)-&a;I8HI>fp5Ce%AH7XwwyNUJd9CeA<3vN*<+W_`zeUOSroZO*9M3_bvq*3xRARv
zg_V)iMLgqz1lDcC-1|FGeD)ZuQ7XsF8gN}!6N4jesYO>uM?r0*yFUll0?4RPoAsX6
ztQ9X0$ce0SDQnn1Qn<-vYwdI$%_(BhwfuBS)(jTXbzT>{nkHMoiPET3Nm4+eZsX!g
z0p+qN9g+c6rgfU$8hL^vd%F+zcl$g2-Qfq@!<~nNC9CWzZ2+tYod1QY6z71|q(Is>
z+{bk-p1oM+D8_ at wlr`6T%(MCm+oL(XLU5=bEk?;IGeGSW;lj}eGsUSj5GjIct`+Sb
zU at EG(rW>Xgl!xtfXBk$bk7BK?yEbxZ#kf;Dbii$LH<GtVfeM~pGRip at f#;O8Z$jJz
zgA8XM>P{I_;wNk`uNC*MBAU8c7-py&{I<P=3l1w=kmCvil%-U9DB1;uha2lENAHdf
zk5D90u at ht0TM-H*%h34fLRnOVIga&&l<MBth4ZEi8ek0>-s#Qghs66R7^1Xu$PGw=
zyntoYtM3=hXp9yp<=UCvIj}3EUS2e5?<QaN0JeBLgUvB_^$*Y9rPs9X8QvGoqF!>J
zgCDm|#ZS~{+%WkO<5`u?M?@fZ<-Cb{Zh<}A_HGLN#4AVq at 5+l_RXDr&g=&@9OK0*T
z7F*%S|6*SJMwVIVJ+UF-;OHhw`4%%R8j_2iAeN at IEpF{}rm+K~)-3FJ7YIEBeMEns
zK`$dTkN`8Nn_ItVXa0X*bRI-!8**$Mee(@wsqMFo1U(1QZaPM$Gc={&7kX{CbfC0)
zuA|+1xM|b>Q0TSY(t*<Ixs7%YaPzNMuL_laC-mCtxsaZ7Sox1YkJAc2|IdV8+kFxE
z7RJ!pm0ph7B06>_5|vNi+tH=%pN+!I&xk&wdDWW#&+$DMUE1n7q|RUEXnTvX|7p?x
zQ|PrlAZEDi{JA%ZKE at 3#QTe?iA3XV19;ke~{+|l{p)kCZgI{gc4n+Sg{Im4GKp*LZ
z=I1UwH<DJ*EA_FMLw`m;!(E$xOX#$HrVA4a!#?_zq5o74!L-%!Dp2RI?QabIFNI#)
zFRzu7Tg&frL$BZOXp7Hrg3GS|4?^#kS;}x}zfR8Gx-2~%x03$87HzpvdOoeKJ!_+}
z?dvc2!Svj#-xX^6Z;2<RvGV_F=|2%Z(yAVnUiGfO|Bf3vV^=ZycPY=wFu1I~m0sI_
zU<^KG@$@hLNL2W~ROoda_pbd{`@h5kTEEJFDfBOeeoG0ZRXXjaKPV#KS9<*p^$Vf@
zLKLp?q4c`0UxPM~|0m}B^0(X`aO3}1df$S$P4B<|{ESHsM7}ccbkgw+L$BWp9{i36
z%#=`CrPuDa4ZVKX^IYhy|6_P?eb3PA_hT=F{$((FZ9naPXz2Ca{qFZX;;ZOS484Be
zcksDLcqXkhYB%lu0q995YJdIS<KPWEz%~1hYy5wf{>LK!JwJkPlwR2Qr_7aYPMxXH
zN70raLpP^y^2l8eEd1EouYL{vr*AR+I|0|O{%MpE;V7O9{foDm!FSORB&dANzm)D&
lcRZ5+e}=zdq-%xm1=<()u6qdIHqrlWn at 9e{5SX9Qe*t|B#j^ka

literal 0
HcmV?d00001

diff --git a/llvm/test/tools/llvm-cov/Inputs/mcdc-macro.proftext b/llvm/test/tools/llvm-cov/Inputs/mcdc-macro.proftext
new file mode 100644
index 00000000000000..952f161f56c58d
--- /dev/null
+++ b/llvm/test/tools/llvm-cov/Inputs/mcdc-macro.proftext
@@ -0,0 +1,62 @@
+main
+# Func Hash:
+24
+# Num Counters:
+1
+# Counter Values:
+1
+
+foo
+# Func Hash:
+395201011017399473
+# Num Counters:
+22
+# Counter Values:
+1
+1
+0
+0
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+0
+1
+1
+1
+0
+0
+0
+0
+# Num Bitmap Bytes:
+$13
+# Bitmap Byte Values:
+0x0
+0x0
+0x0
+0x20
+0x8
+0x0
+0x20
+0x0
+0x0
+0x0
+0x0
+0x0
+0x0
+
+
+bar
+# Func Hash:
+24
+# Num Counters:
+1
+# Counter Values:
+3
+
diff --git a/llvm/test/tools/llvm-cov/mcdc-macro.test b/llvm/test/tools/llvm-cov/mcdc-macro.test
new file mode 100644
index 00000000000000..d59055ad2c29b1
--- /dev/null
+++ b/llvm/test/tools/llvm-cov/mcdc-macro.test
@@ -0,0 +1,92 @@
+// Test visualization of MC/DC constructs for branches in macro expansions.
+
+// RUN: llvm-profdata merge %S/Inputs/mcdc-macro.proftext -o %t.profdata
+// RUN: llvm-cov show --show-expansions --show-branches=count --show-mcdc %S/Inputs/mcdc-macro.o -instr-profile %t.profdata -path-equivalence=/tmp,%S/Inputs %S/Inputs/mcdc-macro.c | FileCheck %s
+
+// CHECK:  |  |  |  Branch (2:11): [Folded - Ignored]
+// CHECK:  |  |  |  Branch (3:11): [True: 0, False: 0]
+// CHECK:  |  |  |  Branch (3:23): [True: 0, False: 0]
+// CHECK:  |  Branch (9:7): [True: 0, False: 0]
+// CHECK-NEXT:  |  Branch (9:22): [True: 0, False: 0]
+// CHECK-NEXT:  ------------------
+// CHECK-NEXT:  |---> MC/DC Decision Region (9:7) to (9:23)
+// CHECK-NEXT:  |
+// CHECK-NEXT:  |  Number of Conditions: 5
+// CHECK-NEXT:  |     Condition C1 --> (9:7)
+// CHECK-NEXT:  |     Condition C2 --> (2:11)
+// CHECK-NEXT:  |     Condition C3 --> (3:11)
+// CHECK-NEXT:  |     Condition C4 --> (3:23)
+// CHECK-NEXT:  |     Condition C5 --> (9:22)
+// 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: constant folded
+// CHECK-NEXT:  |  C3-Pair: not covered
+// CHECK-NEXT:  |  C4-Pair: not covered
+// CHECK-NEXT:  |  C5-Pair: not covered
+// CHECK-NEXT:  |  MC/DC Coverage for Decision: 0.00%
+// CHECK-NEXT:  |
+// CHECK-NEXT:  ------------------
+
+// CHECK:  |  |  |  Branch (2:11): [Folded - Ignored]
+// CHECK:  |  Branch (11:7): [True: 0, False: 0]
+// CHECK-NEXT:  ------------------
+// CHECK-NEXT:  |---> MC/DC Decision Region (11:7) to (11:13)
+// CHECK-NEXT:  |
+// CHECK-NEXT:  |  Number of Conditions: 2
+// CHECK-NEXT:  |     Condition C1 --> (11:7)
+// CHECK-NEXT:  |     Condition C2 --> (2:11)
+// 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: constant folded
+// CHECK-NEXT:  |  MC/DC Coverage for Decision: 0.00%
+// CHECK-NEXT:  |
+// CHECK-NEXT:  ------------------
+
+// CHECK:  |  |  |  Branch (1:11): [True: 0, False: 0]
+// CHECK:  |  |  |  Branch (2:11): [Folded - Ignored]
+// CHECK:  |  |  |  |  |  Branch (3:11): [True: 0, False: 0]
+// CHECK:  |  |  |  |  |  Branch (3:23): [True: 0, False: 0]
+// CHECK:  |  Branch (13:7): [True: 0, False: 0]
+// CHECK-NEXT:  |  Branch (13:13): [True: 0, False: 0]
+// CHECK-NEXT:  ------------------
+// CHECK-NEXT:  |---> MC/DC Decision Region (13:7) to (13:32)
+// CHECK-NEXT:  |
+// CHECK-NEXT:  |  Number of Conditions: 6
+// CHECK-NEXT:  |     Condition C1 --> (13:7)
+// CHECK-NEXT:  |     Condition C2 --> (13:13)
+// CHECK-NEXT:  |     Condition C3 --> (1:11)
+// CHECK-NEXT:  |     Condition C4 --> (2:11)
+// CHECK-NEXT:  |     Condition C5 --> (3:11)
+// CHECK-NEXT:  |     Condition C6 --> (3:23)
+// 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: constant folded
+// CHECK-NEXT:  |  C5-Pair: not covered
+// CHECK-NEXT:  |  C6-Pair: not covered
+// CHECK-NEXT:  |  MC/DC Coverage for Decision: 0.00%
+// CHECK-NEXT:  |
+// CHECK-NEXT:  ------------------
+
+Instructions for regenerating the test:
+
+# cd %S/Inputs
+cp mcdc-macro.c /tmp
+
+clang -fcoverage-mcdc -fprofile-instr-generate -fcoverage-compilation-dir=. \
+    -fcoverage-mapping /tmp/mcdc-macro.c -o /tmp/mcdc-macro.o
+
+mv /tmp/mcdc-macro.o %S/Inputs

>From 83a0ff8e7f735d6d09fb0287cfe173bbab480f09 Mon Sep 17 00:00:00 2001
From: NAKAMURA Takumi <geek4civic at gmail.com>
Date: Sun, 28 Jan 2024 11:18:11 +0900
Subject: [PATCH 09/11] Update test for my impl

---
 llvm/test/tools/llvm-cov/mcdc-macro.test | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/llvm/test/tools/llvm-cov/mcdc-macro.test b/llvm/test/tools/llvm-cov/mcdc-macro.test
index d59055ad2c29b1..3d45e3a38d4092 100644
--- a/llvm/test/tools/llvm-cov/mcdc-macro.test
+++ b/llvm/test/tools/llvm-cov/mcdc-macro.test
@@ -13,18 +13,18 @@
 // CHECK-NEXT:  |
 // CHECK-NEXT:  |  Number of Conditions: 5
 // CHECK-NEXT:  |     Condition C1 --> (9:7)
-// CHECK-NEXT:  |     Condition C2 --> (2:11)
-// CHECK-NEXT:  |     Condition C3 --> (3:11)
-// CHECK-NEXT:  |     Condition C4 --> (3:23)
-// CHECK-NEXT:  |     Condition C5 --> (9:22)
+// CHECK-NEXT:  |     Condition C2 --> (9:22)
+// CHECK-NEXT:  |     Condition C3 --> (2:11)
+// CHECK-NEXT:  |     Condition C4 --> (3:11)
+// CHECK-NEXT:  |     Condition C5 --> (3:23)
 // 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: constant folded
-// CHECK-NEXT:  |  C3-Pair: not covered
+// CHECK-NEXT:  |  C2-Pair: not covered
+// CHECK-NEXT:  |  C3-Pair: constant folded
 // CHECK-NEXT:  |  C4-Pair: not covered
 // CHECK-NEXT:  |  C5-Pair: not covered
 // CHECK-NEXT:  |  MC/DC Coverage for Decision: 0.00%

>From 200a32f784bbbde9c0b8991c878adc9a3b89b2b4 Mon Sep 17 00:00:00 2001
From: NAKAMURA Takumi <geek4civic at gmail.com>
Date: Sun, 28 Jan 2024 16:55:20 +0900
Subject: [PATCH 10/11] Reflect @MaskRay's suggestions

---
 .../ProfileData/Coverage/CoverageMapping.cpp  | 57 +++++++++----------
 1 file changed, 26 insertions(+), 31 deletions(-)

diff --git a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
index 60ec2fad9f08ed..a211097f335e48 100644
--- a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
+++ b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
@@ -587,7 +587,6 @@ namespace {
 
 /// Collect Decisions, Branchs, and Expansions and associate them.
 class MCDCDecisionRecorder {
-
 private:
   /// This holds the DecisionRegion and MCDCBranches under it.
   /// Also traverses Expansion(s).
@@ -694,16 +693,15 @@ class MCDCDecisionRecorder {
     assert(Decisions.empty() && "All Decisions have not been resolved");
   }
 
-  /// Register Region and start recording if it is MCDCDecisionRegion.
-  /// \param Region to be inspected
-  /// \returns true if recording started.
-  bool registerDecision(const CounterMappingRegion &Region) {
-    if (Region.Kind != CounterMappingRegion::MCDCDecisionRegion)
-      return false;
+  /// Register Region and start recording.
+  void registerDecision(const CounterMappingRegion &Decision) {
+    Decisions.emplace_back(Decision);
+  }
 
-    // Start recording Region to create DecisionRecord
-    Decisions.emplace_back(Region);
-    return true;
+  void recordExpansion(const CounterMappingRegion &Expansion) {
+    any_of(Decisions, [&Expansion](auto &Decision) {
+      return Decision.recordExpansion(Expansion);
+    });
   }
 
   using DecisionAndBranches =
@@ -711,31 +709,16 @@ class MCDCDecisionRecorder {
                 SmallVector<const CounterMappingRegion *> /// Branches
                 >;
 
-  /// If Region is ExpansionRegion, record it.
-  /// If Region is MCDCBranchRegion, add it to DecisionRecord.
-  /// \param Region to be inspected
+  /// Add MCDCBranchRegion to DecisionRecord.
+  /// \param Branch to be processed
   /// \returns DecisionsAndBranches if DecisionRecord completed.
   ///     Or returns nullopt.
   std::optional<DecisionAndBranches>
-  processRegion(const CounterMappingRegion &Region) {
-
-    // Record ExpansionRegion.
-    if (Region.Kind == CounterMappingRegion::ExpansionRegion) {
-      for (auto &Decision : reverse(Decisions)) {
-        if (Decision.recordExpansion(Region))
-          break;
-      }
-      return std::nullopt; // It doesn't complete.
-    }
-
-    // Do nothing unless MCDCBranchRegion.
-    if (Region.Kind != CounterMappingRegion::MCDCBranchRegion)
-      return std::nullopt;
-
+  processBranch(const CounterMappingRegion &Branch) {
     // Seek each Decision and apply Region to it.
     for (auto DecisionIter = Decisions.begin(), DecisionEnd = Decisions.end();
          DecisionIter != DecisionEnd; ++DecisionIter)
-      switch (DecisionIter->addBranch(Region)) {
+      switch (DecisionIter->addBranch(Branch)) {
       case DecisionRecord::NotProcessed:
         continue;
       case DecisionRecord::Processed:
@@ -815,8 +798,10 @@ Error CoverageMapping::loadFunctionRecord(
   for (const auto &Region : Record.MappingRegions) {
     // MCDCDecisionRegion should be handled first since it overlaps with
     // others inside.
-    if (MCDCDecisions.registerDecision(Region))
+    if (Region.Kind == CounterMappingRegion::MCDCDecisionRegion) {
+      MCDCDecisions.registerDecision(Region);
       continue;
+    }
     Expected<int64_t> ExecutionCount = Ctx.evaluate(Region.Count);
     if (auto E = ExecutionCount.takeError()) {
       consumeError(std::move(E));
@@ -829,7 +814,17 @@ Error CoverageMapping::loadFunctionRecord(
     }
     Function.pushRegion(Region, *ExecutionCount, *AltExecutionCount);
 
-    auto Result = MCDCDecisions.processRegion(Region);
+    // Record ExpansionRegion.
+    if (Region.Kind == CounterMappingRegion::ExpansionRegion) {
+      MCDCDecisions.recordExpansion(Region);
+      continue;
+    }
+
+    // Do nothing unless MCDCBranchRegion.
+    if (Region.Kind != CounterMappingRegion::MCDCBranchRegion)
+      continue;
+
+    auto Result = MCDCDecisions.processBranch(Region);
     if (!Result) // Any Decision doesn't complete.
       continue;
 

>From 0e4b05b4cc97ce77d456002c6d84401b9ae03b10 Mon Sep 17 00:00:00 2001
From: NAKAMURA Takumi <geek4civic at gmail.com>
Date: Sun, 28 Jan 2024 16:56:37 +0900
Subject: [PATCH 11/11] Mark `dominates()` const

---
 llvm/lib/ProfileData/Coverage/CoverageMapping.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
index a211097f335e48..d596d0d9d206a4 100644
--- a/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
+++ b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
@@ -620,7 +620,7 @@ class MCDCDecisionRecorder {
     }
 
     /// Determine whether DecisionRecord dominates `R`.
-    bool dominates(const CounterMappingRegion &R) {
+    bool dominates(const CounterMappingRegion &R) const {
       // Determine whether `R` is included in `DecisionRegion`.
       if (R.FileID == DecisionRegion->FileID &&
           R.startLoc() >= DecisionStartLoc && R.endLoc() <= DecisionEndLoc)



More information about the llvm-commits mailing list