@@ -573,6 +573,11 @@ struct EmptyCoverageMappingBuilder : public CoverageMappingBuilder {
573
573
// / creation.
574
574
struct MCDCCoverageBuilder {
575
575
576
+ struct DecisionIDPair {
577
+ MCDCConditionID TrueID = 0 ;
578
+ MCDCConditionID FalseID = 0 ;
579
+ };
580
+
576
581
// / The AST walk recursively visits nested logical-AND or logical-OR binary
577
582
// / operator nodes and then visits their LHS and RHS children nodes. As this
578
583
// / happens, the algorithm will assign IDs to each operator's LHS and RHS side
@@ -616,14 +621,14 @@ struct MCDCCoverageBuilder {
616
621
// /
617
622
// / A node ID of '0' always means MC/DC isn't being tracked.
618
623
// /
619
- // / As the AST walk proceeds recursively, the algorithm will also use stacks
624
+ // / As the AST walk proceeds recursively, the algorithm will also use a stack
620
625
// / to track the IDs of logical-AND and logical-OR operations on the RHS so
621
626
// / that it can be determined which nodes are executed next, depending on how
622
627
// / a LHS or RHS of a logical-AND or logical-OR is evaluated. This
623
628
// / information relies on the assigned IDs and are embedded within the
624
629
// / coverage region IDs of each branch region associated with a leaf-level
625
630
// / condition. This information helps the visualization tool reconstruct all
626
- // / possible test vectors for the purposes of MC/DC analysis. if a "next" node
631
+ // / possible test vectors for the purposes of MC/DC analysis. If a "next" node
627
632
// / ID is '0', it means it's the end of the test vector. The following rules
628
633
// / are used:
629
634
// /
@@ -663,54 +668,40 @@ struct MCDCCoverageBuilder {
663
668
private:
664
669
CodeGenModule &CGM;
665
670
666
- llvm::SmallVector<MCDCConditionID> AndRHS;
667
- llvm::SmallVector<MCDCConditionID> OrRHS;
668
- llvm::SmallVector<const BinaryOperator *> NestLevel;
671
+ llvm::SmallVector<DecisionIDPair> DecisionStack;
669
672
llvm::DenseMap<const Stmt *, MCDCConditionID> &CondIDs;
670
673
llvm::DenseMap<const Stmt *, unsigned > &MCDCBitmapMap;
671
674
MCDCConditionID NextID = 1 ;
672
675
bool NotMapped = false ;
673
676
677
+ // / Represent a sentinel value of [0,0] for the bottom of DecisionStack.
678
+ static constexpr DecisionIDPair DecisionStackSentinel{0 , 0 };
679
+
674
680
// / Is this a logical-AND operation?
675
681
bool isLAnd (const BinaryOperator *E) const {
676
682
return E->getOpcode () == BO_LAnd;
677
683
}
678
684
679
- // / Push an ID onto the corresponding RHS stack.
680
- void pushRHS (const BinaryOperator *E) {
681
- llvm::SmallVector<MCDCConditionID> &rhs = isLAnd (E) ? AndRHS : OrRHS;
682
- rhs.push_back (CondIDs[CodeGenFunction::stripCond (E->getRHS ())]);
683
- }
684
-
685
- // / Pop an ID from the corresponding RHS stack.
686
- void popRHS (const BinaryOperator *E) {
687
- llvm::SmallVector<MCDCConditionID> &rhs = isLAnd (E) ? AndRHS : OrRHS;
688
- if (!rhs.empty ())
689
- rhs.pop_back ();
690
- }
691
-
692
- // / If the expected ID is on top, pop it off the corresponding RHS stack.
693
- void popRHSifTop (const BinaryOperator *E) {
694
- if (!OrRHS.empty () && CondIDs[E] == OrRHS.back ())
695
- OrRHS.pop_back ();
696
- else if (!AndRHS.empty () && CondIDs[E] == AndRHS.back ())
697
- AndRHS.pop_back ();
698
- }
699
-
700
685
public:
701
686
MCDCCoverageBuilder (CodeGenModule &CGM,
702
687
llvm::DenseMap<const Stmt *, MCDCConditionID> &CondIDMap,
703
688
llvm::DenseMap<const Stmt *, unsigned > &MCDCBitmapMap)
704
- : CGM(CGM), CondIDs(CondIDMap), MCDCBitmapMap(MCDCBitmapMap) {}
689
+ : CGM(CGM), DecisionStack(1 , DecisionStackSentinel), CondIDs(CondIDMap),
690
+ MCDCBitmapMap (MCDCBitmapMap) {}
705
691
706
- // / Return the ID of the RHS of the next, upper nest-level logical-OR.
707
- MCDCConditionID getNextLOrCondID () const {
708
- return OrRHS.empty () ? 0 : OrRHS.back ();
709
- }
692
+ // / Return whether the build of the control flow map is at the top-level
693
+ // / (root) of a logical operator nest in a boolean expression prior to the
694
+ // / assignment of condition IDs.
695
+ bool isIdle () const { return (NextID == 1 && !NotMapped); }
696
+
697
+ // / Return whether any IDs have been assigned in the build of the control
698
+ // / flow map, indicating that the map is being generated for this boolean
699
+ // / expression.
700
+ bool isBuilding () const { return (NextID > 1 ); }
710
701
711
- // / Return the ID of the RHS of the next, upper nest-level logical-AND .
712
- MCDCConditionID getNextLAndCondID () const {
713
- return AndRHS. empty () ? 0 : AndRHS. back () ;
702
+ // / Set the given condition's ID .
703
+ void setCondID ( const Expr *Cond, MCDCConditionID ID) {
704
+ CondIDs[ CodeGenFunction::stripCond (Cond)] = ID ;
714
705
}
715
706
716
707
// / Return the ID of a given condition.
@@ -722,6 +713,9 @@ struct MCDCCoverageBuilder {
722
713
return I->second ;
723
714
}
724
715
716
+ // / Return the LHS Decision ([0,0] if not set).
717
+ const DecisionIDPair &back () const { return DecisionStack.back (); }
718
+
725
719
// / Push the binary operator statement to track the nest level and assign IDs
726
720
// / to the operator's LHS and RHS. The RHS may be a larger subtree that is
727
721
// / broken up on successive levels.
@@ -730,68 +724,67 @@ struct MCDCCoverageBuilder {
730
724
return ;
731
725
732
726
// If binary expression is disqualified, don't do mapping.
733
- if (NestLevel.empty () &&
734
- !MCDCBitmapMap.contains (CodeGenFunction::stripCond (E)))
727
+ if (!isBuilding () && !MCDCBitmapMap.contains (CodeGenFunction::stripCond (E)))
735
728
NotMapped = true ;
736
729
737
- // Push Stmt on 'NestLevel' stack to keep track of nest location.
738
- NestLevel.push_back (E);
739
-
740
730
// Don't go any further if we don't need to map condition IDs.
741
731
if (NotMapped)
742
732
return ;
743
733
734
+ const DecisionIDPair &ParentDecision = DecisionStack.back ();
735
+
744
736
// If the operator itself has an assigned ID, this means it represents a
745
- // larger subtree. In this case, pop its ID out of the RHS stack and
746
- // assign that ID to its LHS node. Its RHS will receive a new ID.
747
- if (CondIDs.contains (CodeGenFunction::stripCond (E))) {
748
- // If Stmt has an ID, assign its ID to LHS
749
- CondIDs[CodeGenFunction::stripCond (E->getLHS ())] = CondIDs[E];
750
-
751
- // Since the operator's LHS assumes the operator's same ID, pop the
752
- // operator from the RHS stack so that if LHS short-circuits, it won't be
753
- // incorrectly re-used as the node executed next.
754
- popRHSifTop (E);
755
- } else {
756
- // Otherwise, assign ID+1 to LHS.
757
- CondIDs[CodeGenFunction::stripCond (E->getLHS ())] = NextID++;
758
- }
737
+ // larger subtree. In this case, assign that ID to its LHS node. Its RHS
738
+ // will receive a new ID below. Otherwise, assign ID+1 to LHS.
739
+ if (CondIDs.contains (CodeGenFunction::stripCond (E)))
740
+ setCondID (E->getLHS (), getCondID (E));
741
+ else
742
+ setCondID (E->getLHS (), NextID++);
759
743
760
- // Assign ID+1 to RHS.
761
- CondIDs[CodeGenFunction::stripCond (E->getRHS ())] = NextID++;
744
+ // Assign a ID+1 for the RHS.
745
+ MCDCConditionID RHSid = NextID++;
746
+ setCondID (E->getRHS (), RHSid);
762
747
763
- // Push ID of Stmt's RHS so that LHS nodes know about it
764
- pushRHS (E);
748
+ // Push the LHS decision IDs onto the DecisionStack.
749
+ if (isLAnd (E))
750
+ DecisionStack.push_back ({RHSid, ParentDecision.FalseID });
751
+ else
752
+ DecisionStack.push_back ({ParentDecision.TrueID , RHSid});
753
+ }
754
+
755
+ // / Pop and return the LHS Decision ([0,0] if not set).
756
+ DecisionIDPair pop () {
757
+ if (!CGM.getCodeGenOpts ().MCDCCoverage || NotMapped)
758
+ return DecisionStack.front ();
759
+
760
+ assert (DecisionStack.size () > 1 );
761
+ DecisionIDPair D = DecisionStack.back ();
762
+ DecisionStack.pop_back ();
763
+ return D;
765
764
}
766
765
767
- // / Pop the binary operator from the next level. If the walk is at the top of
768
- // / the next, assign the total number of conditions .
769
- unsigned popAndReturnCondCount (const BinaryOperator *E) {
766
+ // / Return the total number of conditions and reset the state. The number of
767
+ // / conditions is zero if the expression isn't mapped .
768
+ unsigned getTotalConditionsAndReset (const BinaryOperator *E) {
770
769
if (!CGM.getCodeGenOpts ().MCDCCoverage )
771
770
return 0 ;
772
771
773
- unsigned TotalConds = 0 ;
774
-
775
- // Pop Stmt from 'NestLevel' stack.
776
- assert (NestLevel.back () == E);
777
- NestLevel.pop_back ();
772
+ assert (!isIdle ());
773
+ assert (DecisionStack.size () == 1 );
778
774
779
775
// Reset state if not doing mapping.
780
- if (NestLevel. empty () && NotMapped) {
776
+ if (NotMapped) {
781
777
NotMapped = false ;
778
+ assert (NextID == 1 );
782
779
return 0 ;
783
780
}
784
781
785
- // Pop RHS ID .
786
- popRHS (E) ;
782
+ // Set number of conditions and reset .
783
+ unsigned TotalConds = NextID - 1 ;
787
784
788
- // If at the parent (NestLevel=0), set conds and reset.
789
- if (NestLevel.empty ()) {
790
- TotalConds = NextID - 1 ;
785
+ // Reset ID back to beginning.
786
+ NextID = 1 ;
791
787
792
- // Reset ID back to beginning.
793
- NextID = 1 ;
794
- }
795
788
return TotalConds;
796
789
}
797
790
};
@@ -1018,13 +1011,15 @@ struct CounterCoverageMappingBuilder
1018
1011
return (Cond->EvaluateAsInt (Result, CVM.getCodeGenModule ().getContext ()));
1019
1012
}
1020
1013
1014
+ using MCDCDecisionIDPair = MCDCCoverageBuilder::DecisionIDPair;
1015
+
1021
1016
// / Create a Branch Region around an instrumentable condition for coverage
1022
1017
// / and add it to the function's SourceRegions. A branch region tracks a
1023
1018
// / "True" counter and a "False" counter for boolean expressions that
1024
1019
// / result in the generation of a branch.
1025
- void createBranchRegion ( const Expr *C, Counter TrueCnt, Counter FalseCnt,
1026
- MCDCConditionID ID = 0 , MCDCConditionID TrueID = 0 ,
1027
- MCDCConditionID FalseID = 0 ) {
1020
+ void
1021
+ createBranchRegion ( const Expr *C, Counter TrueCnt, Counter FalseCnt ,
1022
+ const MCDCDecisionIDPair &IDPair = MCDCDecisionIDPair() ) {
1028
1023
// Check for NULL conditions.
1029
1024
if (!C)
1030
1025
return ;
@@ -1034,6 +1029,10 @@ struct CounterCoverageMappingBuilder
1034
1029
// function's SourceRegions) because it doesn't apply to any other source
1035
1030
// code other than the Condition.
1036
1031
if (CodeGenFunction::isInstrumentedCondition (C)) {
1032
+ MCDCConditionID ID = MCDCBuilder.getCondID (C);
1033
+ MCDCConditionID TrueID = IDPair.TrueID ;
1034
+ MCDCConditionID FalseID = IDPair.FalseID ;
1035
+
1037
1036
// If a condition can fold to true or false, the corresponding branch
1038
1037
// will be removed. Create a region with both counters hard-coded to
1039
1038
// zero. This allows us to visualize them in a special way.
@@ -1822,20 +1821,28 @@ struct CounterCoverageMappingBuilder
1822
1821
}
1823
1822
1824
1823
void VisitBinLAnd (const BinaryOperator *E) {
1825
- // Keep track of Binary Operator and assign MCDC condition IDs
1824
+ bool IsRootNode = MCDCBuilder.isIdle ();
1825
+
1826
+ // Keep track of Binary Operator and assign MCDC condition IDs.
1826
1827
MCDCBuilder.pushAndAssignIDs (E);
1827
1828
1828
1829
extendRegion (E->getLHS ());
1829
1830
propagateCounts (getRegion ().getCounter (), E->getLHS ());
1830
1831
handleFileExit (getEnd (E->getLHS ()));
1831
1832
1833
+ // Track LHS True/False Decision.
1834
+ const auto DecisionLHS = MCDCBuilder.pop ();
1835
+
1832
1836
// Counter tracks the right hand side of a logical and operator.
1833
1837
extendRegion (E->getRHS ());
1834
1838
propagateCounts (getRegionCounter (E), E->getRHS ());
1835
1839
1836
- // Process Binary Operator and create MCDC Decision Region if top-level
1840
+ // Track RHS True/False Decision.
1841
+ const auto DecisionRHS = MCDCBuilder.back ();
1842
+
1843
+ // Create MCDC Decision Region if at top-level (root).
1837
1844
unsigned NumConds = 0 ;
1838
- if ((NumConds = MCDCBuilder.popAndReturnCondCount (E)))
1845
+ if (IsRootNode && (NumConds = MCDCBuilder.getTotalConditionsAndReset (E)))
1839
1846
createDecisionRegion (E, getRegionBitmap (E), NumConds);
1840
1847
1841
1848
// Extract the RHS's Execution Counter.
@@ -1847,30 +1854,13 @@ struct CounterCoverageMappingBuilder
1847
1854
// Extract the Parent Region Counter.
1848
1855
Counter ParentCnt = getRegion ().getCounter ();
1849
1856
1850
- // Extract the MCDC condition IDs (returns 0 if not needed).
1851
- MCDCConditionID NextOrID = MCDCBuilder.getNextLOrCondID ();
1852
- MCDCConditionID NextAndID = MCDCBuilder.getNextLAndCondID ();
1853
- MCDCConditionID LHSid = MCDCBuilder.getCondID (E->getLHS ());
1854
- MCDCConditionID RHSid = MCDCBuilder.getCondID (E->getRHS ());
1855
-
1856
1857
// Create Branch Region around LHS condition.
1857
- // MC/DC: For "LHS && RHS"
1858
- // - If LHS is TRUE, execution goes to the RHS.
1859
- // - If LHS is FALSE, execution goes to the LHS of the next logical-OR.
1860
- // If that does not exist, execution exits (ID == 0).
1861
1858
createBranchRegion (E->getLHS (), RHSExecCnt,
1862
- subtractCounters (ParentCnt, RHSExecCnt), LHSid, RHSid,
1863
- NextOrID);
1859
+ subtractCounters (ParentCnt, RHSExecCnt), DecisionLHS);
1864
1860
1865
1861
// Create Branch Region around RHS condition.
1866
- // MC/DC: For "LHS && RHS"
1867
- // - If RHS is TRUE, execution goes to LHS of the next logical-AND.
1868
- // If that does not exist, execution exits (ID == 0).
1869
- // - If RHS is FALSE, execution goes to the LHS of the next logical-OR.
1870
- // If that does not exist, execution exits (ID == 0).
1871
1862
createBranchRegion (E->getRHS (), RHSTrueCnt,
1872
- subtractCounters (RHSExecCnt, RHSTrueCnt), RHSid,
1873
- NextAndID, NextOrID);
1863
+ subtractCounters (RHSExecCnt, RHSTrueCnt), DecisionRHS);
1874
1864
}
1875
1865
1876
1866
// Determine whether the right side of OR operation need to be visited.
@@ -1884,20 +1874,28 @@ struct CounterCoverageMappingBuilder
1884
1874
}
1885
1875
1886
1876
void VisitBinLOr (const BinaryOperator *E) {
1887
- // Keep track of Binary Operator and assign MCDC condition IDs
1877
+ bool IsRootNode = MCDCBuilder.isIdle ();
1878
+
1879
+ // Keep track of Binary Operator and assign MCDC condition IDs.
1888
1880
MCDCBuilder.pushAndAssignIDs (E);
1889
1881
1890
1882
extendRegion (E->getLHS ());
1891
1883
Counter OutCount = propagateCounts (getRegion ().getCounter (), E->getLHS ());
1892
1884
handleFileExit (getEnd (E->getLHS ()));
1893
1885
1886
+ // Track LHS True/False Decision.
1887
+ const auto DecisionLHS = MCDCBuilder.pop ();
1888
+
1894
1889
// Counter tracks the right hand side of a logical or operator.
1895
1890
extendRegion (E->getRHS ());
1896
1891
propagateCounts (getRegionCounter (E), E->getRHS ());
1897
1892
1898
- // Process Binary Operator and create MCDC Decision Region if top-level
1893
+ // Track RHS True/False Decision.
1894
+ const auto DecisionRHS = MCDCBuilder.back ();
1895
+
1896
+ // Create MCDC Decision Region if at top-level (root).
1899
1897
unsigned NumConds = 0 ;
1900
- if ((NumConds = MCDCBuilder.popAndReturnCondCount (E)))
1898
+ if (IsRootNode && (NumConds = MCDCBuilder.getTotalConditionsAndReset (E)))
1901
1899
createDecisionRegion (E, getRegionBitmap (E), NumConds);
1902
1900
1903
1901
// Extract the RHS's Execution Counter.
@@ -1913,28 +1911,13 @@ struct CounterCoverageMappingBuilder
1913
1911
// Extract the Parent Region Counter.
1914
1912
Counter ParentCnt = getRegion ().getCounter ();
1915
1913
1916
- // Extract the MCDC condition IDs (returns 0 if not needed).
1917
- MCDCConditionID NextOrID = MCDCBuilder.getNextLOrCondID ();
1918
- MCDCConditionID NextAndID = MCDCBuilder.getNextLAndCondID ();
1919
- MCDCConditionID LHSid = MCDCBuilder.getCondID (E->getLHS ());
1920
- MCDCConditionID RHSid = MCDCBuilder.getCondID (E->getRHS ());
1921
-
1922
1914
// Create Branch Region around LHS condition.
1923
- // MC/DC: For "LHS || RHS"
1924
- // - If LHS is TRUE, execution goes to the LHS of the next logical-AND.
1925
- // If that does not exist, execution exits (ID == 0).
1926
- // - If LHS is FALSE, execution goes to the RHS.
1927
1915
createBranchRegion (E->getLHS (), subtractCounters (ParentCnt, RHSExecCnt),
1928
- RHSExecCnt, LHSid, NextAndID, RHSid );
1916
+ RHSExecCnt, DecisionLHS );
1929
1917
1930
1918
// Create Branch Region around RHS condition.
1931
- // MC/DC: For "LHS || RHS"
1932
- // - If RHS is TRUE, execution goes to LHS of the next logical-AND.
1933
- // If that does not exist, execution exits (ID == 0).
1934
- // - If RHS is FALSE, execution goes to the LHS of the next logical-OR.
1935
- // If that does not exist, execution exits (ID == 0).
1936
1919
createBranchRegion (E->getRHS (), subtractCounters (RHSExecCnt, RHSFalseCnt),
1937
- RHSFalseCnt, RHSid, NextAndID, NextOrID );
1920
+ RHSFalseCnt, DecisionRHS );
1938
1921
}
1939
1922
1940
1923
void VisitLambdaExpr (const LambdaExpr *LE) {
0 commit comments