Skip to content

Commit d912f1f

Browse files
chapunievodius96
andauthored
[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]>
1 parent ffd84a6 commit d912f1f

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"
@@ -523,6 +524,160 @@ static unsigned getMaxBitmapSize(const CounterMappingContext &Ctx,
523524
return MaxBitmapID + (SizeInBits / CHAR_BIT);
524525
}
525526

527+
namespace {
528+
529+
/// Collect Decisions, Branchs, and Expansions and associate them.
530+
class MCDCDecisionRecorder {
531+
private:
532+
/// This holds the DecisionRegion and MCDCBranches under it.
533+
/// Also traverses Expansion(s).
534+
/// The Decision has the number of MCDCBranches and will complete
535+
/// when it is filled with unique ConditionID of MCDCBranches.
536+
struct DecisionRecord {
537+
const CounterMappingRegion *DecisionRegion;
538+
539+
/// They are reflected from DecisionRegion for convenience.
540+
LineColPair DecisionStartLoc;
541+
LineColPair DecisionEndLoc;
542+
543+
/// This is passed to `MCDCRecordProcessor`, so this should be compatible
544+
/// to`ArrayRef<const CounterMappingRegion *>`.
545+
SmallVector<const CounterMappingRegion *> MCDCBranches;
546+
547+
/// IDs that are stored in MCDCBranches
548+
/// Complete when all IDs (1 to NumConditions) are met.
549+
DenseSet<CounterMappingRegion::MCDCConditionID> ConditionIDs;
550+
551+
/// Set of IDs of Expansion(s) that are relevant to DecisionRegion
552+
/// and its children (via expansions).
553+
/// FileID pointed by ExpandedFileID is dedicated to the expansion, so
554+
/// the location in the expansion doesn't matter.
555+
DenseSet<unsigned> ExpandedFileIDs;
556+
557+
DecisionRecord(const CounterMappingRegion &Decision)
558+
: DecisionRegion(&Decision), DecisionStartLoc(Decision.startLoc()),
559+
DecisionEndLoc(Decision.endLoc()) {
560+
assert(Decision.Kind == CounterMappingRegion::MCDCDecisionRegion);
561+
}
562+
563+
/// Determine whether DecisionRecord dominates `R`.
564+
bool dominates(const CounterMappingRegion &R) const {
565+
// Determine whether `R` is included in `DecisionRegion`.
566+
if (R.FileID == DecisionRegion->FileID &&
567+
R.startLoc() >= DecisionStartLoc && R.endLoc() <= DecisionEndLoc)
568+
return true;
569+
570+
// Determine whether `R` is pointed by any of Expansions.
571+
return ExpandedFileIDs.contains(R.FileID);
572+
}
573+
574+
enum Result {
575+
NotProcessed = 0, /// Irrelevant to this Decision
576+
Processed, /// Added to this Decision
577+
Completed, /// Added and filled this Decision
578+
};
579+
580+
/// Add Branch into the Decision
581+
/// \param Branch expects MCDCBranchRegion
582+
/// \returns NotProcessed/Processed/Completed
583+
Result addBranch(const CounterMappingRegion &Branch) {
584+
assert(Branch.Kind == CounterMappingRegion::MCDCBranchRegion);
585+
586+
auto ConditionID = Branch.MCDCParams.ID;
587+
assert(ConditionID > 0 && "ConditionID should begin with 1");
588+
589+
if (ConditionIDs.contains(ConditionID) ||
590+
ConditionID > DecisionRegion->MCDCParams.NumConditions)
591+
return NotProcessed;
592+
593+
if (!this->dominates(Branch))
594+
return NotProcessed;
595+
596+
assert(MCDCBranches.size() < DecisionRegion->MCDCParams.NumConditions);
597+
598+
// Put `ID=1` in front of `MCDCBranches` for convenience
599+
// even if `MCDCBranches` is not topological.
600+
if (ConditionID == 1)
601+
MCDCBranches.insert(MCDCBranches.begin(), &Branch);
602+
else
603+
MCDCBranches.push_back(&Branch);
604+
605+
// Mark `ID` as `assigned`.
606+
ConditionIDs.insert(ConditionID);
607+
608+
// `Completed` when `MCDCBranches` is full
609+
return (MCDCBranches.size() == DecisionRegion->MCDCParams.NumConditions
610+
? Completed
611+
: Processed);
612+
}
613+
614+
/// Record Expansion if it is relevant to this Decision.
615+
/// Each `Expansion` may nest.
616+
/// \returns true if recorded.
617+
bool recordExpansion(const CounterMappingRegion &Expansion) {
618+
if (!this->dominates(Expansion))
619+
return false;
620+
621+
ExpandedFileIDs.insert(Expansion.ExpandedFileID);
622+
return true;
623+
}
624+
};
625+
626+
private:
627+
/// Decisions in progress
628+
/// DecisionRecord is added for each MCDCDecisionRegion.
629+
/// DecisionRecord is removed when Decision is completed.
630+
SmallVector<DecisionRecord> Decisions;
631+
632+
public:
633+
~MCDCDecisionRecorder() {
634+
assert(Decisions.empty() && "All Decisions have not been resolved");
635+
}
636+
637+
/// Register Region and start recording.
638+
void registerDecision(const CounterMappingRegion &Decision) {
639+
Decisions.emplace_back(Decision);
640+
}
641+
642+
void recordExpansion(const CounterMappingRegion &Expansion) {
643+
any_of(Decisions, [&Expansion](auto &Decision) {
644+
return Decision.recordExpansion(Expansion);
645+
});
646+
}
647+
648+
using DecisionAndBranches =
649+
std::pair<const CounterMappingRegion *, /// Decision
650+
SmallVector<const CounterMappingRegion *> /// Branches
651+
>;
652+
653+
/// Add MCDCBranchRegion to DecisionRecord.
654+
/// \param Branch to be processed
655+
/// \returns DecisionsAndBranches if DecisionRecord completed.
656+
/// Or returns nullopt.
657+
std::optional<DecisionAndBranches>
658+
processBranch(const CounterMappingRegion &Branch) {
659+
// Seek each Decision and apply Region to it.
660+
for (auto DecisionIter = Decisions.begin(), DecisionEnd = Decisions.end();
661+
DecisionIter != DecisionEnd; ++DecisionIter)
662+
switch (DecisionIter->addBranch(Branch)) {
663+
case DecisionRecord::NotProcessed:
664+
continue;
665+
case DecisionRecord::Processed:
666+
return std::nullopt;
667+
case DecisionRecord::Completed:
668+
DecisionAndBranches Result =
669+
std::make_pair(DecisionIter->DecisionRegion,
670+
std::move(DecisionIter->MCDCBranches));
671+
Decisions.erase(DecisionIter); // No longer used.
672+
return Result;
673+
}
674+
675+
llvm_unreachable("Branch not found in Decisions");
676+
}
677+
};
678+
679+
} // namespace
680+
526681
Error CoverageMapping::loadFunctionRecord(
527682
const CoverageMappingRecord &Record,
528683
IndexedInstrProfReader &ProfileReader) {
@@ -579,18 +734,13 @@ Error CoverageMapping::loadFunctionRecord(
579734
Record.MappingRegions[0].Count.isZero() && Counts[0] > 0)
580735
return Error::success();
581736

582-
unsigned NumConds = 0;
583-
const CounterMappingRegion *MCDCDecision;
584-
std::vector<const CounterMappingRegion *> MCDCBranches;
585-
737+
MCDCDecisionRecorder MCDCDecisions;
586738
FunctionRecord Function(OrigFuncName, Record.Filenames);
587739
for (const auto &Region : Record.MappingRegions) {
588-
// If an MCDCDecisionRegion is seen, track the BranchRegions that follow
589-
// it according to Region.NumConditions.
740+
// MCDCDecisionRegion should be handled first since it overlaps with
741+
// others inside.
590742
if (Region.Kind == CounterMappingRegion::MCDCDecisionRegion) {
591-
assert(NumConds == 0);
592-
MCDCDecision = &Region;
593-
NumConds = Region.MCDCParams.NumConditions;
743+
MCDCDecisions.registerDecision(Region);
594744
continue;
595745
}
596746
Expected<int64_t> ExecutionCount = Ctx.evaluate(Region.Count);
@@ -605,43 +755,47 @@ Error CoverageMapping::loadFunctionRecord(
605755
}
606756
Function.pushRegion(Region, *ExecutionCount, *AltExecutionCount);
607757

608-
// If a MCDCDecisionRegion was seen, store the BranchRegions that
609-
// correspond to it in a vector, according to the number of conditions
610-
// recorded for the region (tracked by NumConds).
611-
if (NumConds > 0 && Region.Kind == CounterMappingRegion::MCDCBranchRegion) {
612-
MCDCBranches.push_back(&Region);
613-
614-
// As we move through all of the MCDCBranchRegions that follow the
615-
// MCDCDecisionRegion, decrement NumConds to make sure we account for
616-
// them all before we calculate the bitmap of executed test vectors.
617-
if (--NumConds == 0) {
618-
// Evaluating the test vector bitmap for the decision region entails
619-
// calculating precisely what bits are pertinent to this region alone.
620-
// This is calculated based on the recorded offset into the global
621-
// profile bitmap; the length is calculated based on the recorded
622-
// number of conditions.
623-
Expected<BitVector> ExecutedTestVectorBitmap =
624-
Ctx.evaluateBitmap(MCDCDecision);
625-
if (auto E = ExecutedTestVectorBitmap.takeError()) {
626-
consumeError(std::move(E));
627-
return Error::success();
628-
}
758+
// Record ExpansionRegion.
759+
if (Region.Kind == CounterMappingRegion::ExpansionRegion) {
760+
MCDCDecisions.recordExpansion(Region);
761+
continue;
762+
}
629763

630-
// Since the bitmap identifies the executed test vectors for an MC/DC
631-
// DecisionRegion, all of the information is now available to process.
632-
// This is where the bulk of the MC/DC progressing takes place.
633-
Expected<MCDCRecord> Record = Ctx.evaluateMCDCRegion(
634-
*MCDCDecision, *ExecutedTestVectorBitmap, MCDCBranches);
635-
if (auto E = Record.takeError()) {
636-
consumeError(std::move(E));
637-
return Error::success();
638-
}
764+
// Do nothing unless MCDCBranchRegion.
765+
if (Region.Kind != CounterMappingRegion::MCDCBranchRegion)
766+
continue;
639767

640-
// Save the MC/DC Record so that it can be visualized later.
641-
Function.pushMCDCRecord(*Record);
642-
MCDCBranches.clear();
643-
}
768+
auto Result = MCDCDecisions.processBranch(Region);
769+
if (!Result) // Any Decision doesn't complete.
770+
continue;
771+
772+
auto MCDCDecision = Result->first;
773+
auto &MCDCBranches = Result->second;
774+
775+
// Evaluating the test vector bitmap for the decision region entails
776+
// calculating precisely what bits are pertinent to this region alone.
777+
// This is calculated based on the recorded offset into the global
778+
// profile bitmap; the length is calculated based on the recorded
779+
// number of conditions.
780+
Expected<BitVector> ExecutedTestVectorBitmap =
781+
Ctx.evaluateBitmap(MCDCDecision);
782+
if (auto E = ExecutedTestVectorBitmap.takeError()) {
783+
consumeError(std::move(E));
784+
return Error::success();
644785
}
786+
787+
// Since the bitmap identifies the executed test vectors for an MC/DC
788+
// DecisionRegion, all of the information is now available to process.
789+
// This is where the bulk of the MC/DC progressing takes place.
790+
Expected<MCDCRecord> Record = Ctx.evaluateMCDCRegion(
791+
*MCDCDecision, *ExecutedTestVectorBitmap, MCDCBranches);
792+
if (auto E = Record.takeError()) {
793+
consumeError(std::move(E));
794+
return Error::success();
795+
}
796+
797+
// Save the MC/DC Record so that it can be visualized later.
798+
Function.pushMCDCRecord(*Record);
645799
}
646800

647801
// 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)