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+
586741Error 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.
0 commit comments