Skip to content

Commit b50a84e

Browse files
chapunievodius96
authored andcommitted
[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 <[email protected]> (cherry picked from commit d912f1f)
1 parent a6817b7 commit b50a84e

File tree

5 files changed

+378
-43
lines changed

5 files changed

+378
-43
lines changed

llvm/lib/ProfileData/Coverage/CoverageMapping.cpp

+197-43
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "llvm/ProfileData/Coverage/CoverageMapping.h"
1515
#include "llvm/ADT/ArrayRef.h"
1616
#include "llvm/ADT/DenseMap.h"
17+
#include "llvm/ADT/STLExtras.h"
1718
#include "llvm/ADT/SmallBitVector.h"
1819
#include "llvm/ADT/SmallVector.h"
1920
#include "llvm/ADT/StringExtras.h"
@@ -583,6 +584,160 @@ static unsigned getMaxBitmapSize(const CounterMappingContext &Ctx,
583584
return MaxBitmapID + (SizeInBits / CHAR_BIT);
584585
}
585586

587+
namespace {
588+
589+
/// Collect Decisions, Branchs, and Expansions and associate them.
590+
class MCDCDecisionRecorder {
591+
private:
592+
/// This holds the DecisionRegion and MCDCBranches under it.
593+
/// Also traverses Expansion(s).
594+
/// The Decision has the number of MCDCBranches and will complete
595+
/// when it is filled with unique ConditionID of MCDCBranches.
596+
struct DecisionRecord {
597+
const CounterMappingRegion *DecisionRegion;
598+
599+
/// They are reflected from DecisionRegion for convenience.
600+
LineColPair DecisionStartLoc;
601+
LineColPair DecisionEndLoc;
602+
603+
/// This is passed to `MCDCRecordProcessor`, so this should be compatible
604+
/// to`ArrayRef<const CounterMappingRegion *>`.
605+
SmallVector<const CounterMappingRegion *> MCDCBranches;
606+
607+
/// IDs that are stored in MCDCBranches
608+
/// Complete when all IDs (1 to NumConditions) are met.
609+
DenseSet<CounterMappingRegion::MCDCConditionID> ConditionIDs;
610+
611+
/// Set of IDs of Expansion(s) that are relevant to DecisionRegion
612+
/// and its children (via expansions).
613+
/// FileID pointed by ExpandedFileID is dedicated to the expansion, so
614+
/// the location in the expansion doesn't matter.
615+
DenseSet<unsigned> ExpandedFileIDs;
616+
617+
DecisionRecord(const CounterMappingRegion &Decision)
618+
: DecisionRegion(&Decision), DecisionStartLoc(Decision.startLoc()),
619+
DecisionEndLoc(Decision.endLoc()) {
620+
assert(Decision.Kind == CounterMappingRegion::MCDCDecisionRegion);
621+
}
622+
623+
/// Determine whether DecisionRecord dominates `R`.
624+
bool dominates(const CounterMappingRegion &R) const {
625+
// Determine whether `R` is included in `DecisionRegion`.
626+
if (R.FileID == DecisionRegion->FileID &&
627+
R.startLoc() >= DecisionStartLoc && R.endLoc() <= DecisionEndLoc)
628+
return true;
629+
630+
// Determine whether `R` is pointed by any of Expansions.
631+
return ExpandedFileIDs.contains(R.FileID);
632+
}
633+
634+
enum Result {
635+
NotProcessed = 0, /// Irrelevant to this Decision
636+
Processed, /// Added to this Decision
637+
Completed, /// Added and filled this Decision
638+
};
639+
640+
/// Add Branch into the Decision
641+
/// \param Branch expects MCDCBranchRegion
642+
/// \returns NotProcessed/Processed/Completed
643+
Result addBranch(const CounterMappingRegion &Branch) {
644+
assert(Branch.Kind == CounterMappingRegion::MCDCBranchRegion);
645+
646+
auto ConditionID = Branch.MCDCParams.ID;
647+
assert(ConditionID > 0 && "ConditionID should begin with 1");
648+
649+
if (ConditionIDs.contains(ConditionID) ||
650+
ConditionID > DecisionRegion->MCDCParams.NumConditions)
651+
return NotProcessed;
652+
653+
if (!this->dominates(Branch))
654+
return NotProcessed;
655+
656+
assert(MCDCBranches.size() < DecisionRegion->MCDCParams.NumConditions);
657+
658+
// Put `ID=1` in front of `MCDCBranches` for convenience
659+
// even if `MCDCBranches` is not topological.
660+
if (ConditionID == 1)
661+
MCDCBranches.insert(MCDCBranches.begin(), &Branch);
662+
else
663+
MCDCBranches.push_back(&Branch);
664+
665+
// Mark `ID` as `assigned`.
666+
ConditionIDs.insert(ConditionID);
667+
668+
// `Completed` when `MCDCBranches` is full
669+
return (MCDCBranches.size() == DecisionRegion->MCDCParams.NumConditions
670+
? Completed
671+
: Processed);
672+
}
673+
674+
/// Record Expansion if it is relevant to this Decision.
675+
/// Each `Expansion` may nest.
676+
/// \returns true if recorded.
677+
bool recordExpansion(const CounterMappingRegion &Expansion) {
678+
if (!this->dominates(Expansion))
679+
return false;
680+
681+
ExpandedFileIDs.insert(Expansion.ExpandedFileID);
682+
return true;
683+
}
684+
};
685+
686+
private:
687+
/// Decisions in progress
688+
/// DecisionRecord is added for each MCDCDecisionRegion.
689+
/// DecisionRecord is removed when Decision is completed.
690+
SmallVector<DecisionRecord> Decisions;
691+
692+
public:
693+
~MCDCDecisionRecorder() {
694+
assert(Decisions.empty() && "All Decisions have not been resolved");
695+
}
696+
697+
/// Register Region and start recording.
698+
void registerDecision(const CounterMappingRegion &Decision) {
699+
Decisions.emplace_back(Decision);
700+
}
701+
702+
void recordExpansion(const CounterMappingRegion &Expansion) {
703+
any_of(Decisions, [&Expansion](auto &Decision) {
704+
return Decision.recordExpansion(Expansion);
705+
});
706+
}
707+
708+
using DecisionAndBranches =
709+
std::pair<const CounterMappingRegion *, /// Decision
710+
SmallVector<const CounterMappingRegion *> /// Branches
711+
>;
712+
713+
/// Add MCDCBranchRegion to DecisionRecord.
714+
/// \param Branch to be processed
715+
/// \returns DecisionsAndBranches if DecisionRecord completed.
716+
/// Or returns nullopt.
717+
std::optional<DecisionAndBranches>
718+
processBranch(const CounterMappingRegion &Branch) {
719+
// Seek each Decision and apply Region to it.
720+
for (auto DecisionIter = Decisions.begin(), DecisionEnd = Decisions.end();
721+
DecisionIter != DecisionEnd; ++DecisionIter)
722+
switch (DecisionIter->addBranch(Branch)) {
723+
case DecisionRecord::NotProcessed:
724+
continue;
725+
case DecisionRecord::Processed:
726+
return std::nullopt;
727+
case DecisionRecord::Completed:
728+
DecisionAndBranches Result =
729+
std::make_pair(DecisionIter->DecisionRegion,
730+
std::move(DecisionIter->MCDCBranches));
731+
Decisions.erase(DecisionIter); // No longer used.
732+
return Result;
733+
}
734+
735+
llvm_unreachable("Branch not found in Decisions");
736+
}
737+
};
738+
739+
} // namespace
740+
586741
Error CoverageMapping::loadFunctionRecord(
587742
const CoverageMappingRecord &Record,
588743
IndexedInstrProfReader &ProfileReader) {
@@ -639,18 +794,13 @@ Error CoverageMapping::loadFunctionRecord(
639794
Record.MappingRegions[0].Count.isZero() && Counts[0] > 0)
640795
return Error::success();
641796

642-
unsigned NumConds = 0;
643-
const CounterMappingRegion *MCDCDecision;
644-
std::vector<const CounterMappingRegion *> MCDCBranches;
645-
797+
MCDCDecisionRecorder MCDCDecisions;
646798
FunctionRecord Function(OrigFuncName, Record.Filenames);
647799
for (const auto &Region : Record.MappingRegions) {
648-
// If an MCDCDecisionRegion is seen, track the BranchRegions that follow
649-
// it according to Region.NumConditions.
800+
// MCDCDecisionRegion should be handled first since it overlaps with
801+
// others inside.
650802
if (Region.Kind == CounterMappingRegion::MCDCDecisionRegion) {
651-
assert(NumConds == 0);
652-
MCDCDecision = &Region;
653-
NumConds = Region.MCDCParams.NumConditions;
803+
MCDCDecisions.registerDecision(Region);
654804
continue;
655805
}
656806
Expected<int64_t> ExecutionCount = Ctx.evaluate(Region.Count);
@@ -665,43 +815,47 @@ Error CoverageMapping::loadFunctionRecord(
665815
}
666816
Function.pushRegion(Region, *ExecutionCount, *AltExecutionCount);
667817

668-
// If a MCDCDecisionRegion was seen, store the BranchRegions that
669-
// correspond to it in a vector, according to the number of conditions
670-
// recorded for the region (tracked by NumConds).
671-
if (NumConds > 0 && Region.Kind == CounterMappingRegion::MCDCBranchRegion) {
672-
MCDCBranches.push_back(&Region);
673-
674-
// As we move through all of the MCDCBranchRegions that follow the
675-
// MCDCDecisionRegion, decrement NumConds to make sure we account for
676-
// them all before we calculate the bitmap of executed test vectors.
677-
if (--NumConds == 0) {
678-
// Evaluating the test vector bitmap for the decision region entails
679-
// calculating precisely what bits are pertinent to this region alone.
680-
// This is calculated based on the recorded offset into the global
681-
// profile bitmap; the length is calculated based on the recorded
682-
// number of conditions.
683-
Expected<BitVector> ExecutedTestVectorBitmap =
684-
Ctx.evaluateBitmap(MCDCDecision);
685-
if (auto E = ExecutedTestVectorBitmap.takeError()) {
686-
consumeError(std::move(E));
687-
return Error::success();
688-
}
818+
// Record ExpansionRegion.
819+
if (Region.Kind == CounterMappingRegion::ExpansionRegion) {
820+
MCDCDecisions.recordExpansion(Region);
821+
continue;
822+
}
689823

690-
// Since the bitmap identifies the executed test vectors for an MC/DC
691-
// DecisionRegion, all of the information is now available to process.
692-
// This is where the bulk of the MC/DC progressing takes place.
693-
Expected<MCDCRecord> Record = Ctx.evaluateMCDCRegion(
694-
*MCDCDecision, *ExecutedTestVectorBitmap, MCDCBranches);
695-
if (auto E = Record.takeError()) {
696-
consumeError(std::move(E));
697-
return Error::success();
698-
}
824+
// Do nothing unless MCDCBranchRegion.
825+
if (Region.Kind != CounterMappingRegion::MCDCBranchRegion)
826+
continue;
699827

700-
// Save the MC/DC Record so that it can be visualized later.
701-
Function.pushMCDCRecord(*Record);
702-
MCDCBranches.clear();
703-
}
828+
auto Result = MCDCDecisions.processBranch(Region);
829+
if (!Result) // Any Decision doesn't complete.
830+
continue;
831+
832+
auto MCDCDecision = Result->first;
833+
auto &MCDCBranches = Result->second;
834+
835+
// Evaluating the test vector bitmap for the decision region entails
836+
// calculating precisely what bits are pertinent to this region alone.
837+
// This is calculated based on the recorded offset into the global
838+
// profile bitmap; the length is calculated based on the recorded
839+
// number of conditions.
840+
Expected<BitVector> ExecutedTestVectorBitmap =
841+
Ctx.evaluateBitmap(MCDCDecision);
842+
if (auto E = ExecutedTestVectorBitmap.takeError()) {
843+
consumeError(std::move(E));
844+
return Error::success();
704845
}
846+
847+
// Since the bitmap identifies the executed test vectors for an MC/DC
848+
// DecisionRegion, all of the information is now available to process.
849+
// This is where the bulk of the MC/DC progressing takes place.
850+
Expected<MCDCRecord> Record = Ctx.evaluateMCDCRegion(
851+
*MCDCDecision, *ExecutedTestVectorBitmap, MCDCBranches);
852+
if (auto E = Record.takeError()) {
853+
consumeError(std::move(E));
854+
return Error::success();
855+
}
856+
857+
// Save the MC/DC Record so that it can be visualized later.
858+
Function.pushMCDCRecord(*Record);
705859
}
706860

707861
// Don't create records for (filenames, function) pairs we've already seen.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#define C c
2+
#define D 1
3+
#define E (C != a) && (C > a)
4+
#define F E
5+
6+
void __attribute__((noinline)) func1(void) { return; }
7+
8+
void __attribute__((noinline)) func(int a, int b, int c) {
9+
if (a && D && E || b)
10+
func1();
11+
if (b && D)
12+
func1();
13+
if (a && (b && C) || (D && F))
14+
func1();
15+
}
16+
17+
int main() {
18+
func(2, 3, 3);
19+
return 0;
20+
}
6.27 KB
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
func
2+
# Func Hash:
3+
395201011017399473
4+
# Num Counters:
5+
22
6+
# Counter Values:
7+
1
8+
1
9+
0
10+
0
11+
1
12+
1
13+
1
14+
1
15+
1
16+
1
17+
1
18+
1
19+
1
20+
1
21+
0
22+
1
23+
1
24+
1
25+
0
26+
0
27+
0
28+
0
29+
# Num Bitmap Bytes:
30+
$13
31+
# Bitmap Byte Values:
32+
0x0
33+
0x0
34+
0x0
35+
0x20
36+
0x8
37+
0x0
38+
0x20
39+
0x0
40+
0x0
41+
0x0
42+
0x0
43+
0x0
44+
0x0
45+
46+
47+
func1
48+
# Func Hash:
49+
24
50+
# Num Counters:
51+
1
52+
# Counter Values:
53+
3
54+
55+
main
56+
# Func Hash:
57+
24
58+
# Num Counters:
59+
1
60+
# Counter Values:
61+
1
62+

0 commit comments

Comments
 (0)