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