[llvm-branch-commits] [llvm] PR for llvm/llvm-project#77871 (PR #80513)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Mon Feb 5 11:33:16 PST 2024
https://github.com/llvmbot updated https://github.com/llvm/llvm-project/pull/80513
>From a6817b7315af5da94cfbe69767c8e8f827fecbca Mon Sep 17 00:00:00 2001
From: NAKAMURA Takumi <geek4civic at gmail.com>
Date: Fri, 2 Feb 2024 18:37:10 +0900
Subject: [PATCH 1/2] CoverageMappingWriter: Emit `Decision` before `Expansion`
(#78966)
To relax scanning record, tweak order by `Decision < Expansion`, or
`Expansion` could not be distinguished whether it belonged to `Decision`
or not.
Relevant to #77871
(cherry picked from commit 438fe1db09b0c20708ea1020519d8073c37feae8)
---
.../Coverage/CoverageMappingWriter.cpp | 10 +++++-
.../ProfileData/CoverageMappingTest.cpp | 36 +++++++++++++++++++
2 files changed, 45 insertions(+), 1 deletion(-)
diff --git a/llvm/lib/ProfileData/Coverage/CoverageMappingWriter.cpp b/llvm/lib/ProfileData/Coverage/CoverageMappingWriter.cpp
index 1c7d8a8909c48..27727f216b051 100644
--- a/llvm/lib/ProfileData/Coverage/CoverageMappingWriter.cpp
+++ b/llvm/lib/ProfileData/Coverage/CoverageMappingWriter.cpp
@@ -167,7 +167,15 @@ void CoverageMappingWriter::write(raw_ostream &OS) {
return LHS.FileID < RHS.FileID;
if (LHS.startLoc() != RHS.startLoc())
return LHS.startLoc() < RHS.startLoc();
- return LHS.Kind < RHS.Kind;
+
+ // Put `Decision` before `Expansion`.
+ auto getKindKey = [](CounterMappingRegion::RegionKind Kind) {
+ return (Kind == CounterMappingRegion::MCDCDecisionRegion
+ ? 2 * CounterMappingRegion::ExpansionRegion - 1
+ : 2 * Kind);
+ };
+
+ return getKindKey(LHS.Kind) < getKindKey(RHS.Kind);
});
// Write out the fileid -> filename mapping.
diff --git a/llvm/unittests/ProfileData/CoverageMappingTest.cpp b/llvm/unittests/ProfileData/CoverageMappingTest.cpp
index 23f66a0232ddb..2849781a9dc43 100644
--- a/llvm/unittests/ProfileData/CoverageMappingTest.cpp
+++ b/llvm/unittests/ProfileData/CoverageMappingTest.cpp
@@ -890,6 +890,42 @@ TEST_P(CoverageMappingTest, non_code_region_bitmask) {
ASSERT_EQ(1U, Names.size());
}
+// Test the order of MCDCDecision before Expansion
+TEST_P(CoverageMappingTest, decision_before_expansion) {
+ startFunction("foo", 0x1234);
+ addCMR(Counter::getCounter(0), "foo", 3, 23, 5, 2);
+
+ // This(4:11) was put after Expansion(4:11) before the fix
+ addMCDCDecisionCMR(0, 2, "foo", 4, 11, 4, 20);
+
+ addExpansionCMR("foo", "A", 4, 11, 4, 12);
+ addExpansionCMR("foo", "B", 4, 19, 4, 20);
+ addCMR(Counter::getCounter(0), "A", 1, 14, 1, 17);
+ addCMR(Counter::getCounter(0), "A", 1, 14, 1, 17);
+ addMCDCBranchCMR(Counter::getCounter(0), Counter::getCounter(1), 1, 2, 0, "A",
+ 1, 14, 1, 17);
+ addCMR(Counter::getCounter(1), "B", 1, 14, 1, 17);
+ addMCDCBranchCMR(Counter::getCounter(1), Counter::getCounter(2), 2, 0, 0, "B",
+ 1, 14, 1, 17);
+
+ // InputFunctionCoverageData::Regions is rewritten after the write.
+ auto InputRegions = InputFunctions.back().Regions;
+
+ writeAndReadCoverageRegions();
+
+ const auto &OutputRegions = OutputFunctions.back().Regions;
+
+ size_t N = ArrayRef(InputRegions).size();
+ ASSERT_EQ(N, OutputRegions.size());
+ for (size_t I = 0; I < N; ++I) {
+ ASSERT_EQ(InputRegions[I].Kind, OutputRegions[I].Kind);
+ ASSERT_EQ(InputRegions[I].FileID, OutputRegions[I].FileID);
+ ASSERT_EQ(InputRegions[I].ExpandedFileID, OutputRegions[I].ExpandedFileID);
+ ASSERT_EQ(InputRegions[I].startLoc(), OutputRegions[I].startLoc());
+ ASSERT_EQ(InputRegions[I].endLoc(), OutputRegions[I].endLoc());
+ }
+}
+
TEST_P(CoverageMappingTest, strip_filename_prefix) {
ProfileWriter.addRecord({"file1:func", 0x1234, {0}}, Err);
>From b50a84e303378df35996d7330aa80aa4ea1f497a Mon Sep 17 00:00:00 2001
From: NAKAMURA Takumi <geek4civic at gmail.com>
Date: Fri, 2 Feb 2024 20:34:12 +0900
Subject: [PATCH 2/2] [Coverage] Let `Decision` take account of expansions
(#78969)
The current implementation (D138849) assumes `Branch`(es) would follow
after the corresponding `Decision`. It is not true if `Branch`(es) are
forwarded to expanded file ID. As a result, consecutive `Decision`(s)
would be confused with insufficient number of `Branch`(es).
`Expansion` will point `Branch`(es) in other file IDs if `Expansion` is
included in the range of `Decision`.
Fixes #77871
---------
Co-authored-by: Alan Phipps <a-phipps at ti.com>
(cherry picked from commit d912f1f0cb49465b08f82fae89ece222404e5640)
---
.../ProfileData/Coverage/CoverageMapping.cpp | 240 ++++++++++++++----
llvm/test/tools/llvm-cov/Inputs/mcdc-macro.c | 20 ++
llvm/test/tools/llvm-cov/Inputs/mcdc-macro.o | Bin 0 -> 6424 bytes
.../tools/llvm-cov/Inputs/mcdc-macro.proftext | 62 +++++
llvm/test/tools/llvm-cov/mcdc-macro.test | 99 ++++++++
5 files changed, 378 insertions(+), 43 deletions(-)
create mode 100644 llvm/test/tools/llvm-cov/Inputs/mcdc-macro.c
create mode 100644 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/lib/ProfileData/Coverage/CoverageMapping.cpp b/llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
index da8e1d87319dd..a357b4cb49211 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"
@@ -583,6 +584,160 @@ static unsigned getMaxBitmapSize(const CounterMappingContext &Ctx,
return MaxBitmapID + (SizeInBits / CHAR_BIT);
}
+namespace {
+
+/// Collect Decisions, Branchs, and Expansions and associate them.
+class MCDCDecisionRecorder {
+private:
+ /// This holds the DecisionRegion and MCDCBranches under it.
+ /// Also traverses Expansion(s).
+ /// The Decision has the number of MCDCBranches and will complete
+ /// when it is filled with unique ConditionID of MCDCBranches.
+ 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);
+ }
+
+ /// Determine whether DecisionRecord dominates `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)
+ 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
+ /// \param Branch expects MCDCBranchRegion
+ /// \returns NotProcessed/Processed/Completed
+ Result addBranch(const CounterMappingRegion &Branch) {
+ assert(Branch.Kind == CounterMappingRegion::MCDCBranchRegion);
+
+ auto ConditionID = Branch.MCDCParams.ID;
+ assert(ConditionID > 0 && "ConditionID should begin with 1");
+
+ 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
+ MCDCBranches.push_back(&Branch);
+
+ // Mark `ID` as `assigned`.
+ 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;
+
+ ExpandedFileIDs.insert(Expansion.ExpandedFileID);
+ return true;
+ }
+ };
+
+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.
+ void registerDecision(const CounterMappingRegion &Decision) {
+ Decisions.emplace_back(Decision);
+ }
+
+ void recordExpansion(const CounterMappingRegion &Expansion) {
+ any_of(Decisions, [&Expansion](auto &Decision) {
+ return Decision.recordExpansion(Expansion);
+ });
+ }
+
+ using DecisionAndBranches =
+ std::pair<const CounterMappingRegion *, /// Decision
+ SmallVector<const CounterMappingRegion *> /// Branches
+ >;
+
+ /// Add MCDCBranchRegion to DecisionRecord.
+ /// \param Branch to be processed
+ /// \returns DecisionsAndBranches if DecisionRecord completed.
+ /// Or returns nullopt.
+ std::optional<DecisionAndBranches>
+ 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(Branch)) {
+ 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");
+ }
+};
+
+} // namespace
+
Error CoverageMapping::loadFunctionRecord(
const CoverageMappingRecord &Record,
IndexedInstrProfReader &ProfileReader) {
@@ -639,18 +794,13 @@ 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;
-
+ MCDCDecisionRecorder MCDCDecisions;
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.
+ // MCDCDecisionRegion should be handled first since it overlaps with
+ // others inside.
if (Region.Kind == CounterMappingRegion::MCDCDecisionRegion) {
- assert(NumConds == 0);
- MCDCDecision = &Region;
- NumConds = Region.MCDCParams.NumConditions;
+ MCDCDecisions.registerDecision(Region);
continue;
}
Expected<int64_t> ExecutionCount = Ctx.evaluate(Region.Count);
@@ -665,43 +815,47 @@ Error CoverageMapping::loadFunctionRecord(
}
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();
- }
+ // Record ExpansionRegion.
+ if (Region.Kind == CounterMappingRegion::ExpansionRegion) {
+ MCDCDecisions.recordExpansion(Region);
+ continue;
+ }
- // 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();
- }
+ // Do nothing unless MCDCBranchRegion.
+ if (Region.Kind != CounterMappingRegion::MCDCBranchRegion)
+ continue;
- // Save the MC/DC Record so that it can be visualized later.
- Function.pushMCDCRecord(*Record);
- MCDCBranches.clear();
- }
+ auto Result = MCDCDecisions.processBranch(Region);
+ if (!Result) // Any Decision doesn't complete.
+ continue;
+
+ 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();
}
+
+ // 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);
}
// Don't create records for (filenames, function) pairs we've already seen.
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 0000000000000..bd2b979bd257f
--- /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 100644
index 0000000000000000000000000000000000000000..667ccd132d2fb848ae86b9bbbc72f7b3a917feab
GIT binary patch
literal 6424
zcmdT|Z){sv6~FgAJ8{~?PSUhZvNd%eMHy-=d(A(WhWWKgmO?htvM^xlT-R~ZKpcnU
zHR`IeS!y#a8-b!xu at 8s|2_%sA0fschBp_}lwGn6<gGrMHQbE85(U72Mnivqyx$hkN
z`thDR!6%O7ckcO}zxUjG- at D19Bjclnl%^n(;=@9+pz+rt6(OaDDX|YM$F_nfU5)I$
zsL)C4e(Mt-pIH5cwfZ*cR{uGY*jm}LBA2h(6E`CT<uDyt-k6TOTe=x}RLNwUSWj+7
zF0Wf_!`B+^eFU;@19Z=n)&h?|vYnvKXkxvr9a(uha=9co>Pk4e_$1V`dVTdu=}%@?
zZR&ZsT1vh0hp{!4EfwlD$nQ-o8yxJ12`Dxo67nll*{{51XDbHps+c^J#QMtXY6|1m
z0`^^$aPvGxFTH6Ne^dLMeC4-eYfhq;kdskD&@fs35SL)nNNkWyjJi|05v(g+*f5ft
z_N0fwGwY=rfx1$1^N|6{KDGs&_|MGug at WvU;l<Dc)<ahM#2<0;e#T|1tJA#R6BOYm
z2KfCLx6upuP+dx_f9mR%iw>T>ar61lJo~FUFlvP#lKjwjihVDA>(5dODQotN0l+^0
zx2Mt{`E+;gr_hP-9s2T%$-47FwyT#KZIhjK4YEfL4Owk_ns!<v$0m>7(<MU(Yhhd0
zijWkwq5&Vx<;U)B5d>g&ywi|D14f~GQ7;-=M3ZQ)6T3iB(}2@~QBbtXm140N3d*2>
zyd1DQfPM|}o^28=0lgg~+#$k_^&M5#Etoml9jrkX9|}uTB^?5nmLbs~!f~tFgn%&W
z63t>q5;_yzFWN+VQnZV`y`n?x?Gv3&=spZ>b{gBE908BG5JB*TQK-$_E_NW;p&s!)
zy8 at 62utQi}lh`df;yc?-Vc=*OlC&8;7zaY&bYWyz66KMO03a|jlc-PP5RGb-ksJ?z
zEZfjjHsQ1O7-mMZj`-yJYT!74cpoM`s*>LG0X!j)^g&E2k15Zi_!NJCb?uG*smrhX
z?uVv&n1e%2#GwpfgoUWjXQ#3~`Al}P5X+K|8SgXRV~qQ8uXmoG?f(PL=ZDYEt!myy
z2I`%p8T0!5w|$I==`-`$gv6)JXJ+R`b}lnN6J5?NF3lF^qlv*-JQkmswJB!{ae3~z
zkY^j^ZS}RGORzYNfZtBK5b-hKuq_q6feUDb8xMzD4~L^xxZ4WHF9g3B_>%dUVSZnt
z996nTD;ydL#~-Lh1m->Ogc4EY5bL3ENPP=&zhQXHi+mgWkhoX)X~g<?f9^mf&`$Va
zUw5Mb)aEd88*rS<9+JJ-9!L}rf7$ZV;R$xrIV=o3yvT&(=%y3OzTOQh*(}ejqMlRs
zaOgb96x!uL)0?U*8URB>#c8evNiqd at uA**K_Pi at 9IF3x!;cCpmc{f*dFQ}pSE;h?6
zQ?b9UhV2zV8<}baHGqa at d52c(On8C2$%c6~YwO5+x|ML;Pe^KC1AiHQ%AS@!3NjLY
zD?9PUOtg<Uwyo3PsFgUl?>Y_M!2<4Z4)FyL!=u+f=)1j_^oZkrXS~;iA0m0!2mh!G
z-%s*M7tV2KTsYQ^bk1R_b68Io$ty0LclL7{j;Da@|B;4c96mW$2uB&uFWbLFIQQW}
zl7Fe;7 at w~@zt(V!KTh&nKKNe=cjx_=%iborMx6`i!;Ndlb%r$@^RoT>UHC<cv)_fc
z5I&^gdf!G}IM4UEhU<Axx$rrPKkLH3M)(C6{!PN4aN%Dd{2MO(D}?_*!}a>FXgJn6
zMfN`@+};1*Y4&;?xN<2%hHo?Oe=Fe_2j4=B^LfTM5S}kx22tIG at f7nLO4nnZy3F=i
zkC#2x<7JN++ecxHgymAnO72z93eItgE}WfO+u8#)p}Or2;l}12Eh3%H7M2%t*>r#3
zor8T=uhlo2JTTfjFnp$>8a|xt>+e0#KQb^lJlr>G>8c~vk$&rN|LB2{_`u)+P{kH=
zbD5Z(J8O$rwvf-~=52^HH at BQmFDy={v-9?nE}xpMmgh71+>(gREEdizupLyG&*&1|
zO+dG>SeQPQPpdnMBV`>SHeaxFvAge`>{+ri+0TlY%AT2;f*Z^X6_`7no?e7pl{k3e
zAt{?yLe*_Gwq!5bnNuRR^l)BroToOisf?Y0*ahUV?2?`6uMVB4ik%R#@goyQ6X`fD
z)Bnsq2>YJD33{-5=?cK-ly!7WSQPie2(ikS$x)>oCZhVL1B~|>sl{;^;2BCWKs-dB
zu9H+;7vLC*zjVC6KL*J$Kb{4hPEuZXK9-q(6`c4!r~5~19f|W{e$0urA~Am!#;PYq
z6yY*0P=5aIvV!BW?3cgWBR}3NIWKd+{7+MUzE^O*_nJTMksohKoR_&@{vsLNYW`n`
zn5usw$|WlPZSwD`LM!XfuwVZMJ?amF6W7gr88FOW{^OGZ|BMYtX6Dq&k`R9RQ<R_g
z8O{SC4$FKRFt7YClYx7^-11%E?B)L&`KKVhPHz8$;H*q(zp(9T{1^en9j5MII2ZQk
z^XE7J36K8ck>+_b_v?QvE*21#;Rx`=x$5MX|C1j5uLnoY%iOR3-IV{kdNLUE{2BJ`
zKl!KFkht65um4Yb^dIjT?0>8NpZ4fK{`JQ5X71a6_Y0lsf3N*t at aR9jXE`r(zy7yU
zex5I-FVCN4zy3$bKgEW`-SPbTf8C at 1__qoBbN?R$40~dTC_F24lJdL1E$;rE02jpg
zoN<KDkw5=0fpPX{{wn#ip7|5x&oXoNWBDBz0LGsyhQ%fFzf?w?0neY|n=rt&Off+G
z9qQj0$z8xZ;m7{mzuyC9j&Bu!72#K=6x~1gKN0AU<gWit09>ywWmlyA_c$rh5C6_{
G=l?hL>vwAa
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 0000000000000..35ecc42b5802a
--- /dev/null
+++ b/llvm/test/tools/llvm-cov/Inputs/mcdc-macro.proftext
@@ -0,0 +1,62 @@
+func
+# 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
+
+
+func1
+# Func Hash:
+24
+# Num Counters:
+1
+# Counter Values:
+3
+
+main
+# Func Hash:
+24
+# Num Counters:
+1
+# Counter Values:
+1
+
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 0000000000000..339284bba2c9b
--- /dev/null
+++ b/llvm/test/tools/llvm-cov/mcdc-macro.test
@@ -0,0 +1,99 @@
+// 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 --compilation-dir=%S/Inputs | FileCheck %s
+
+// CHECK: | | | Branch (2:11): [Folded - Ignored]
+// CHECK: | | | Branch (3:11): [True: 1, False: 0]
+// CHECK: | | | Branch (3:23): [True: 1, False: 0]
+// CHECK: | Branch (9:7): [True: 1, 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 --> (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: | C1, C2, C3, C4, C5 Result
+// CHECK-NEXT: | 1 { T, -, C, T, T = T }
+// CHECK-NEXT: |
+// CHECK-NEXT: | C1-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%
+// CHECK-NEXT: |
+// CHECK-NEXT: ------------------
+
+// CHECK: | | | Branch (2:11): [Folded - Ignored]
+// CHECK: | Branch (11:7): [True: 1, 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: | C1, C2 Result
+// CHECK-NEXT: | 1 { T, C = T }
+// 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: 1, 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: 1, False: 0]
+// CHECK-NEXT: | Branch (13:13): [True: 1, 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: | C1, C2, C3, C4, C5, C6 Result
+// CHECK-NEXT: | 1 { T, T, T, C, -, - = T }
+// 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 # Or copy mcdc-macro.c into the working directory
+
+clang -fcoverage-mcdc -fprofile-instr-generate -fcoverage-compilation-dir=. \
+ -O3 -mllvm -enable-name-compression=false \
+ -fcoverage-mapping mcdc-macro.c -c
+
+# Instructions for generating proftext
+clang -fprofile-instr-generate mcdc-macro.o
+./a.out
+llvm-profdata merge --sparse -o default.profdata default.profraw
+llvm-profdata merge --text -o mcdc-macro.proftext default.profdata
More information about the llvm-branch-commits
mailing list