diff options
Diffstat (limited to 'contrib/llvm-project/clang/lib/CodeGen/CoverageMappingGen.cpp')
| -rw-r--r-- | contrib/llvm-project/clang/lib/CodeGen/CoverageMappingGen.cpp | 437 | 
1 files changed, 285 insertions, 152 deletions
| diff --git a/contrib/llvm-project/clang/lib/CodeGen/CoverageMappingGen.cpp b/contrib/llvm-project/clang/lib/CodeGen/CoverageMappingGen.cpp index b245abd16c3f..5eca00f22bb8 100644 --- a/contrib/llvm-project/clang/lib/CodeGen/CoverageMappingGen.cpp +++ b/contrib/llvm-project/clang/lib/CodeGen/CoverageMappingGen.cpp @@ -37,7 +37,7 @@ static llvm::cl::opt<bool> EmptyLineCommentCoverage(                     "disable it on test)"),      llvm::cl::init(true), llvm::cl::Hidden); -static llvm::cl::opt<bool> SystemHeadersCoverage( +llvm::cl::opt<bool> SystemHeadersCoverage(      "system-headers-coverage",      llvm::cl::desc("Enable collecting coverage from system headers"),      llvm::cl::init(false), llvm::cl::Hidden); @@ -119,12 +119,16 @@ class SourceMappingRegion {    /// as the line execution count if there are no other regions on the line.    bool GapRegion; +  /// Whetever this region is skipped ('if constexpr' or 'if consteval' untaken +  /// branch, or anything skipped but not empty line / comments) +  bool SkippedRegion; +  public:    SourceMappingRegion(Counter Count, std::optional<SourceLocation> LocStart,                        std::optional<SourceLocation> LocEnd,                        bool GapRegion = false) -      : Count(Count), LocStart(LocStart), LocEnd(LocEnd), GapRegion(GapRegion) { -  } +      : Count(Count), LocStart(LocStart), LocEnd(LocEnd), GapRegion(GapRegion), +        SkippedRegion(false) {}    SourceMappingRegion(Counter Count, std::optional<Counter> FalseCount,                        MCDCParameters MCDCParams, @@ -132,13 +136,14 @@ public:                        std::optional<SourceLocation> LocEnd,                        bool GapRegion = false)        : Count(Count), FalseCount(FalseCount), MCDCParams(MCDCParams), -        LocStart(LocStart), LocEnd(LocEnd), GapRegion(GapRegion) {} +        LocStart(LocStart), LocEnd(LocEnd), GapRegion(GapRegion), +        SkippedRegion(false) {}    SourceMappingRegion(MCDCParameters MCDCParams,                        std::optional<SourceLocation> LocStart,                        std::optional<SourceLocation> LocEnd)        : MCDCParams(MCDCParams), LocStart(LocStart), LocEnd(LocEnd), -        GapRegion(false) {} +        GapRegion(false), SkippedRegion(false) {}    const Counter &getCounter() const { return Count; } @@ -174,6 +179,10 @@ public:    void setGap(bool Gap) { GapRegion = Gap; } +  bool isSkipped() const { return SkippedRegion; } + +  void setSkipped(bool Skipped) { SkippedRegion = Skipped; } +    bool isBranch() const { return FalseCount.has_value(); }    bool isMCDCDecision() const { return MCDCParams.NumConditions != 0; } @@ -468,6 +477,10 @@ public:          MappingRegions.push_back(CounterMappingRegion::makeGapRegion(              Region.getCounter(), *CovFileID, SR.LineStart, SR.ColumnStart,              SR.LineEnd, SR.ColumnEnd)); +      } else if (Region.isSkipped()) { +        MappingRegions.push_back(CounterMappingRegion::makeSkipped( +            *CovFileID, SR.LineStart, SR.ColumnStart, SR.LineEnd, +            SR.ColumnEnd));        } else if (Region.isBranch()) {          MappingRegions.push_back(CounterMappingRegion::makeBranchRegion(              Region.getCounter(), Region.getFalseCounter(), @@ -573,6 +586,11 @@ struct EmptyCoverageMappingBuilder : public CoverageMappingBuilder {  /// creation.  struct MCDCCoverageBuilder { +  struct DecisionIDPair { +    MCDCConditionID TrueID = 0; +    MCDCConditionID FalseID = 0; +  }; +    /// The AST walk recursively visits nested logical-AND or logical-OR binary    /// operator nodes and then visits their LHS and RHS children nodes.  As this    /// happens, the algorithm will assign IDs to each operator's LHS and RHS side @@ -616,14 +634,14 @@ struct MCDCCoverageBuilder {    ///    /// A node ID of '0' always means MC/DC isn't being tracked.    /// -  /// As the AST walk proceeds recursively, the algorithm will also use stacks +  /// As the AST walk proceeds recursively, the algorithm will also use a stack    /// to track the IDs of logical-AND and logical-OR operations on the RHS so    /// that it can be determined which nodes are executed next, depending on how    /// a LHS or RHS of a logical-AND or logical-OR is evaluated.  This    /// information relies on the assigned IDs and are embedded within the    /// coverage region IDs of each branch region associated with a leaf-level    /// condition. This information helps the visualization tool reconstruct all -  /// possible test vectors for the purposes of MC/DC analysis. if a "next" node +  /// possible test vectors for the purposes of MC/DC analysis. If a "next" node    /// ID is '0', it means it's the end of the test vector. The following rules    /// are used:    /// @@ -663,54 +681,40 @@ struct MCDCCoverageBuilder {  private:    CodeGenModule &CGM; -  llvm::SmallVector<MCDCConditionID> AndRHS; -  llvm::SmallVector<MCDCConditionID> OrRHS; -  llvm::SmallVector<const BinaryOperator *> NestLevel; +  llvm::SmallVector<DecisionIDPair> DecisionStack;    llvm::DenseMap<const Stmt *, MCDCConditionID> &CondIDs;    llvm::DenseMap<const Stmt *, unsigned> &MCDCBitmapMap;    MCDCConditionID NextID = 1;    bool NotMapped = false; +  /// Represent a sentinel value of [0,0] for the bottom of DecisionStack. +  static constexpr DecisionIDPair DecisionStackSentinel{0, 0}; +    /// Is this a logical-AND operation?    bool isLAnd(const BinaryOperator *E) const {      return E->getOpcode() == BO_LAnd;    } -  /// Push an ID onto the corresponding RHS stack. -  void pushRHS(const BinaryOperator *E) { -    llvm::SmallVector<MCDCConditionID> &rhs = isLAnd(E) ? AndRHS : OrRHS; -    rhs.push_back(CondIDs[CodeGenFunction::stripCond(E->getRHS())]); -  } - -  /// Pop an ID from the corresponding RHS stack. -  void popRHS(const BinaryOperator *E) { -    llvm::SmallVector<MCDCConditionID> &rhs = isLAnd(E) ? AndRHS : OrRHS; -    if (!rhs.empty()) -      rhs.pop_back(); -  } - -  /// If the expected ID is on top, pop it off the corresponding RHS stack. -  void popRHSifTop(const BinaryOperator *E) { -    if (!OrRHS.empty() && CondIDs[E] == OrRHS.back()) -      OrRHS.pop_back(); -    else if (!AndRHS.empty() && CondIDs[E] == AndRHS.back()) -      AndRHS.pop_back(); -  } -  public:    MCDCCoverageBuilder(CodeGenModule &CGM,                        llvm::DenseMap<const Stmt *, MCDCConditionID> &CondIDMap,                        llvm::DenseMap<const Stmt *, unsigned> &MCDCBitmapMap) -      : CGM(CGM), CondIDs(CondIDMap), MCDCBitmapMap(MCDCBitmapMap) {} +      : CGM(CGM), DecisionStack(1, DecisionStackSentinel), CondIDs(CondIDMap), +        MCDCBitmapMap(MCDCBitmapMap) {} -  /// Return the ID of the RHS of the next, upper nest-level logical-OR. -  MCDCConditionID getNextLOrCondID() const { -    return OrRHS.empty() ? 0 : OrRHS.back(); -  } +  /// Return whether the build of the control flow map is at the top-level +  /// (root) of a logical operator nest in a boolean expression prior to the +  /// assignment of condition IDs. +  bool isIdle() const { return (NextID == 1 && !NotMapped); } -  /// Return the ID of the RHS of the next, upper nest-level logical-AND. -  MCDCConditionID getNextLAndCondID() const { -    return AndRHS.empty() ? 0 : AndRHS.back(); +  /// Return whether any IDs have been assigned in the build of the control +  /// flow map, indicating that the map is being generated for this boolean +  /// expression. +  bool isBuilding() const { return (NextID > 1); } + +  /// Set the given condition's ID. +  void setCondID(const Expr *Cond, MCDCConditionID ID) { +    CondIDs[CodeGenFunction::stripCond(Cond)] = ID;    }    /// Return the ID of a given condition. @@ -722,6 +726,9 @@ public:        return I->second;    } +  /// Return the LHS Decision ([0,0] if not set). +  const DecisionIDPair &back() const { return DecisionStack.back(); } +    /// Push the binary operator statement to track the nest level and assign IDs    /// to the operator's LHS and RHS.  The RHS may be a larger subtree that is    /// broken up on successive levels. @@ -730,68 +737,67 @@ public:        return;      // If binary expression is disqualified, don't do mapping. -    if (NestLevel.empty() && MCDCBitmapMap.find(CodeGenFunction::stripCond( -                                 E)) == MCDCBitmapMap.end()) +    if (!isBuilding() && !MCDCBitmapMap.contains(CodeGenFunction::stripCond(E)))        NotMapped = true; -    // Push Stmt on 'NestLevel' stack to keep track of nest location. -    NestLevel.push_back(E); -      // Don't go any further if we don't need to map condition IDs.      if (NotMapped)        return; +    const DecisionIDPair &ParentDecision = DecisionStack.back(); +      // If the operator itself has an assigned ID, this means it represents a -    // larger subtree.  In this case, pop its ID out of the RHS stack and -    // assign that ID to its LHS node.  Its RHS will receive a new ID. -    if (CondIDs.find(CodeGenFunction::stripCond(E)) != CondIDs.end()) { -      // If Stmt has an ID, assign its ID to LHS -      CondIDs[CodeGenFunction::stripCond(E->getLHS())] = CondIDs[E]; - -      // Since the operator's LHS assumes the operator's same ID, pop the -      // operator from the RHS stack so that if LHS short-circuits, it won't be -      // incorrectly re-used as the node executed next. -      popRHSifTop(E); -    } else { -      // Otherwise, assign ID+1 to LHS. -      CondIDs[CodeGenFunction::stripCond(E->getLHS())] = NextID++; -    } +    // larger subtree.  In this case, assign that ID to its LHS node.  Its RHS +    // will receive a new ID below. Otherwise, assign ID+1 to LHS. +    if (CondIDs.contains(CodeGenFunction::stripCond(E))) +      setCondID(E->getLHS(), getCondID(E)); +    else +      setCondID(E->getLHS(), NextID++); -    // Assign ID+1 to RHS. -    CondIDs[CodeGenFunction::stripCond(E->getRHS())] = NextID++; +    // Assign a ID+1 for the RHS. +    MCDCConditionID RHSid = NextID++; +    setCondID(E->getRHS(), RHSid); -    // Push ID of Stmt's RHS so that LHS nodes know about it -    pushRHS(E); +    // Push the LHS decision IDs onto the DecisionStack. +    if (isLAnd(E)) +      DecisionStack.push_back({RHSid, ParentDecision.FalseID}); +    else +      DecisionStack.push_back({ParentDecision.TrueID, RHSid}); +  } + +  /// Pop and return the LHS Decision ([0,0] if not set). +  DecisionIDPair pop() { +    if (!CGM.getCodeGenOpts().MCDCCoverage || NotMapped) +      return DecisionStack.front(); + +    assert(DecisionStack.size() > 1); +    DecisionIDPair D = DecisionStack.back(); +    DecisionStack.pop_back(); +    return D;    } -  /// Pop the binary operator from the next level. If the walk is at the top of -  /// the next, assign the total number of conditions. -  unsigned popAndReturnCondCount(const BinaryOperator *E) { +  /// Return the total number of conditions and reset the state. The number of +  /// conditions is zero if the expression isn't mapped. +  unsigned getTotalConditionsAndReset(const BinaryOperator *E) {      if (!CGM.getCodeGenOpts().MCDCCoverage)        return 0; -    unsigned TotalConds = 0; - -    // Pop Stmt from 'NestLevel' stack. -    assert(NestLevel.back() == E); -    NestLevel.pop_back(); +    assert(!isIdle()); +    assert(DecisionStack.size() == 1);      // Reset state if not doing mapping. -    if (NestLevel.empty() && NotMapped) { +    if (NotMapped) {        NotMapped = false; +      assert(NextID == 1);        return 0;      } -    // Pop RHS ID. -    popRHS(E); +    // Set number of conditions and reset. +    unsigned TotalConds = NextID - 1; -    // If at the parent (NestLevel=0), set conds and reset. -    if (NestLevel.empty()) { -      TotalConds = NextID - 1; +    // Reset ID back to beginning. +    NextID = 1; -      // Reset ID back to beginning. -      NextID = 1; -    }      return TotalConds;    }  }; @@ -1018,13 +1024,15 @@ struct CounterCoverageMappingBuilder      return (Cond->EvaluateAsInt(Result, CVM.getCodeGenModule().getContext()));    } +  using MCDCDecisionIDPair = MCDCCoverageBuilder::DecisionIDPair; +    /// Create a Branch Region around an instrumentable condition for coverage    /// and add it to the function's SourceRegions.  A branch region tracks a    /// "True" counter and a "False" counter for boolean expressions that    /// result in the generation of a branch. -  void createBranchRegion(const Expr *C, Counter TrueCnt, Counter FalseCnt, -                          MCDCConditionID ID = 0, MCDCConditionID TrueID = 0, -                          MCDCConditionID FalseID = 0) { +  void +  createBranchRegion(const Expr *C, Counter TrueCnt, Counter FalseCnt, +                     const MCDCDecisionIDPair &IDPair = MCDCDecisionIDPair()) {      // Check for NULL conditions.      if (!C)        return; @@ -1034,6 +1042,10 @@ struct CounterCoverageMappingBuilder      // function's SourceRegions) because it doesn't apply to any other source      // code other than the Condition.      if (CodeGenFunction::isInstrumentedCondition(C)) { +      MCDCConditionID ID = MCDCBuilder.getCondID(C); +      MCDCConditionID TrueID = IDPair.TrueID; +      MCDCConditionID FalseID = IDPair.FalseID; +        // If a condition can fold to true or false, the corresponding branch        // will be removed.  Create a region with both counters hard-coded to        // zero. This allows us to visualize them in a special way. @@ -1252,6 +1264,69 @@ struct CounterCoverageMappingBuilder      popRegions(Index);    } +  /// Find a valid range starting with \p StartingLoc and ending before \p +  /// BeforeLoc. +  std::optional<SourceRange> findAreaStartingFromTo(SourceLocation StartingLoc, +                                                    SourceLocation BeforeLoc) { +    // If StartingLoc is in function-like macro, use its start location. +    if (StartingLoc.isMacroID()) { +      FileID FID = SM.getFileID(StartingLoc); +      const SrcMgr::ExpansionInfo *EI = &SM.getSLocEntry(FID).getExpansion(); +      if (EI->isFunctionMacroExpansion()) +        StartingLoc = EI->getExpansionLocStart(); +    } + +    size_t StartDepth = locationDepth(StartingLoc); +    size_t EndDepth = locationDepth(BeforeLoc); +    while (!SM.isWrittenInSameFile(StartingLoc, BeforeLoc)) { +      bool UnnestStart = StartDepth >= EndDepth; +      bool UnnestEnd = EndDepth >= StartDepth; +      if (UnnestEnd) { +        assert(SM.isWrittenInSameFile(getStartOfFileOrMacro(BeforeLoc), +                                      BeforeLoc)); + +        BeforeLoc = getIncludeOrExpansionLoc(BeforeLoc); +        assert(BeforeLoc.isValid()); +        EndDepth--; +      } +      if (UnnestStart) { +        assert(SM.isWrittenInSameFile(StartingLoc, +                                      getStartOfFileOrMacro(StartingLoc))); + +        StartingLoc = getIncludeOrExpansionLoc(StartingLoc); +        assert(StartingLoc.isValid()); +        StartDepth--; +      } +    } +    // If the start and end locations of the gap are both within the same macro +    // file, the range may not be in source order. +    if (StartingLoc.isMacroID() || BeforeLoc.isMacroID()) +      return std::nullopt; +    if (!SM.isWrittenInSameFile(StartingLoc, BeforeLoc) || +        !SpellingRegion(SM, StartingLoc, BeforeLoc).isInSourceOrder()) +      return std::nullopt; +    return {{StartingLoc, BeforeLoc}}; +  } + +  void markSkipped(SourceLocation StartLoc, SourceLocation BeforeLoc) { +    const auto Skipped = findAreaStartingFromTo(StartLoc, BeforeLoc); + +    if (!Skipped) +      return; + +    const auto NewStartLoc = Skipped->getBegin(); +    const auto EndLoc = Skipped->getEnd(); + +    if (NewStartLoc == EndLoc) +      return; +    assert(SpellingRegion(SM, NewStartLoc, EndLoc).isInSourceOrder()); +    handleFileExit(NewStartLoc); +    size_t Index = pushRegion({}, NewStartLoc, EndLoc); +    getRegion().setSkipped(true); +    handleFileExit(EndLoc); +    popRegions(Index); +  } +    /// Keep counts of breaks and continues inside loops.    struct BreakContinue {      Counter BreakCount; @@ -1701,43 +1776,119 @@ struct CounterCoverageMappingBuilder      Visit(S->getSubStmt());    } +  void coverIfConsteval(const IfStmt *S) { +    assert(S->isConsteval()); + +    const auto *Then = S->getThen(); +    const auto *Else = S->getElse(); + +    // It's better for llvm-cov to create a new region with same counter +    // so line-coverage can be properly calculated for lines containing +    // a skipped region (without it the line is marked uncovered) +    const Counter ParentCount = getRegion().getCounter(); + +    extendRegion(S); + +    if (S->isNegatedConsteval()) { +      // ignore 'if consteval' +      markSkipped(S->getIfLoc(), getStart(Then)); +      propagateCounts(ParentCount, Then); + +      if (Else) { +        // ignore 'else <else>' +        markSkipped(getEnd(Then), getEnd(Else)); +      } +    } else { +      assert(S->isNonNegatedConsteval()); +      // ignore 'if consteval <then> [else]' +      markSkipped(S->getIfLoc(), Else ? getStart(Else) : getEnd(Then)); + +      if (Else) +        propagateCounts(ParentCount, Else); +    } +  } + +  void coverIfConstexpr(const IfStmt *S) { +    assert(S->isConstexpr()); + +    // evaluate constant condition... +    const auto *E = cast<ConstantExpr>(S->getCond()); +    const bool isTrue = E->getResultAsAPSInt().getExtValue(); + +    extendRegion(S); + +    // I'm using 'propagateCounts' later as new region is better and allows me +    // to properly calculate line coverage in llvm-cov utility +    const Counter ParentCount = getRegion().getCounter(); + +    // ignore 'if constexpr (' +    SourceLocation startOfSkipped = S->getIfLoc(); + +    if (const auto *Init = S->getInit()) { +      const auto start = getStart(Init); +      const auto end = getEnd(Init); + +      // this check is to make sure typedef here which doesn't have valid source +      // location won't crash it +      if (start.isValid() && end.isValid()) { +        markSkipped(startOfSkipped, start); +        propagateCounts(ParentCount, Init); +        startOfSkipped = getEnd(Init); +      } +    } + +    const auto *Then = S->getThen(); +    const auto *Else = S->getElse(); + +    if (isTrue) { +      // ignore '<condition>)' +      markSkipped(startOfSkipped, getStart(Then)); +      propagateCounts(ParentCount, Then); + +      if (Else) +        // ignore 'else <else>' +        markSkipped(getEnd(Then), getEnd(Else)); +    } else { +      // ignore '<condition>) <then> [else]' +      markSkipped(startOfSkipped, Else ? getStart(Else) : getEnd(Then)); + +      if (Else) +        propagateCounts(ParentCount, Else); +    } +  } +    void VisitIfStmt(const IfStmt *S) { +    // "if constexpr" and "if consteval" are not normal conditional statements, +    // their discarded statement should be skipped +    if (S->isConsteval()) +      return coverIfConsteval(S); +    else if (S->isConstexpr()) +      return coverIfConstexpr(S); +      extendRegion(S);      if (S->getInit())        Visit(S->getInit());      // Extend into the condition before we propagate through it below - this is      // needed to handle macros that generate the "if" but not the condition. -    if (!S->isConsteval()) -      extendRegion(S->getCond()); +    extendRegion(S->getCond());      Counter ParentCount = getRegion().getCounter(); +    Counter ThenCount = getRegionCounter(S); -    // If this is "if !consteval" the then-branch will never be taken, we don't -    // need to change counter -    Counter ThenCount = -        S->isNegatedConsteval() ? ParentCount : getRegionCounter(S); - -    if (!S->isConsteval()) { -      // Emitting a counter for the condition makes it easier to interpret the -      // counter for the body when looking at the coverage. -      propagateCounts(ParentCount, S->getCond()); +    // Emitting a counter for the condition makes it easier to interpret the +    // counter for the body when looking at the coverage. +    propagateCounts(ParentCount, S->getCond()); -      // The 'then' count applies to the area immediately after the condition. -      std::optional<SourceRange> Gap = -          findGapAreaBetween(S->getRParenLoc(), getStart(S->getThen())); -      if (Gap) -        fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(), ThenCount); -    } +    // The 'then' count applies to the area immediately after the condition. +    std::optional<SourceRange> Gap = +        findGapAreaBetween(S->getRParenLoc(), getStart(S->getThen())); +    if (Gap) +      fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(), ThenCount);      extendRegion(S->getThen());      Counter OutCount = propagateCounts(ThenCount, S->getThen()); - -    // If this is "if consteval" the else-branch will never be taken, we don't -    // need to change counter -    Counter ElseCount = S->isNonNegatedConsteval() -                            ? ParentCount -                            : subtractCounters(ParentCount, ThenCount); +    Counter ElseCount = subtractCounters(ParentCount, ThenCount);      if (const Stmt *Else = S->getElse()) {        bool ThenHasTerminateStmt = HasTerminateStmt; @@ -1760,11 +1911,9 @@ struct CounterCoverageMappingBuilder        GapRegionCounter = OutCount;      } -    if (!S->isConsteval()) { -      // Create Branch Region around condition. -      createBranchRegion(S->getCond(), ThenCount, -                         subtractCounters(ParentCount, ThenCount)); -    } +    // Create Branch Region around condition. +    createBranchRegion(S->getCond(), ThenCount, +                       subtractCounters(ParentCount, ThenCount));    }    void VisitCXXTryStmt(const CXXTryStmt *S) { @@ -1822,20 +1971,28 @@ struct CounterCoverageMappingBuilder    }    void VisitBinLAnd(const BinaryOperator *E) { -    // Keep track of Binary Operator and assign MCDC condition IDs +    bool IsRootNode = MCDCBuilder.isIdle(); + +    // Keep track of Binary Operator and assign MCDC condition IDs.      MCDCBuilder.pushAndAssignIDs(E);      extendRegion(E->getLHS());      propagateCounts(getRegion().getCounter(), E->getLHS());      handleFileExit(getEnd(E->getLHS())); +    // Track LHS True/False Decision. +    const auto DecisionLHS = MCDCBuilder.pop(); +      // Counter tracks the right hand side of a logical and operator.      extendRegion(E->getRHS());      propagateCounts(getRegionCounter(E), E->getRHS()); -    // Process Binary Operator and create MCDC Decision Region if top-level +    // Track RHS True/False Decision. +    const auto DecisionRHS = MCDCBuilder.back(); + +    // Create MCDC Decision Region if at top-level (root).      unsigned NumConds = 0; -    if ((NumConds = MCDCBuilder.popAndReturnCondCount(E))) +    if (IsRootNode && (NumConds = MCDCBuilder.getTotalConditionsAndReset(E)))        createDecisionRegion(E, getRegionBitmap(E), NumConds);      // Extract the RHS's Execution Counter. @@ -1847,30 +2004,13 @@ struct CounterCoverageMappingBuilder      // Extract the Parent Region Counter.      Counter ParentCnt = getRegion().getCounter(); -    // Extract the MCDC condition IDs (returns 0 if not needed). -    MCDCConditionID NextOrID = MCDCBuilder.getNextLOrCondID(); -    MCDCConditionID NextAndID = MCDCBuilder.getNextLAndCondID(); -    MCDCConditionID LHSid = MCDCBuilder.getCondID(E->getLHS()); -    MCDCConditionID RHSid = MCDCBuilder.getCondID(E->getRHS()); -      // Create Branch Region around LHS condition. -    // MC/DC: For "LHS && RHS" -    // - If LHS is TRUE, execution goes to the RHS. -    // - If LHS is FALSE, execution goes to the LHS of the next logical-OR. -    //   If that does not exist, execution exits (ID == 0).      createBranchRegion(E->getLHS(), RHSExecCnt, -                       subtractCounters(ParentCnt, RHSExecCnt), LHSid, RHSid, -                       NextOrID); +                       subtractCounters(ParentCnt, RHSExecCnt), DecisionLHS);      // Create Branch Region around RHS condition. -    // MC/DC: For "LHS && RHS" -    // - If RHS is TRUE, execution goes to LHS of the next logical-AND. -    //   If that does not exist, execution exits (ID == 0). -    // - If RHS is FALSE, execution goes to the LHS of the next logical-OR. -    //   If that does not exist, execution exits (ID == 0).      createBranchRegion(E->getRHS(), RHSTrueCnt, -                       subtractCounters(RHSExecCnt, RHSTrueCnt), RHSid, -                       NextAndID, NextOrID); +                       subtractCounters(RHSExecCnt, RHSTrueCnt), DecisionRHS);    }    // Determine whether the right side of OR operation need to be visited. @@ -1884,20 +2024,28 @@ struct CounterCoverageMappingBuilder    }    void VisitBinLOr(const BinaryOperator *E) { -    // Keep track of Binary Operator and assign MCDC condition IDs +    bool IsRootNode = MCDCBuilder.isIdle(); + +    // Keep track of Binary Operator and assign MCDC condition IDs.      MCDCBuilder.pushAndAssignIDs(E);      extendRegion(E->getLHS());      Counter OutCount = propagateCounts(getRegion().getCounter(), E->getLHS());      handleFileExit(getEnd(E->getLHS())); +    // Track LHS True/False Decision. +    const auto DecisionLHS = MCDCBuilder.pop(); +      // Counter tracks the right hand side of a logical or operator.      extendRegion(E->getRHS());      propagateCounts(getRegionCounter(E), E->getRHS()); -    // Process Binary Operator and create MCDC Decision Region if top-level +    // Track RHS True/False Decision. +    const auto DecisionRHS = MCDCBuilder.back(); + +    // Create MCDC Decision Region if at top-level (root).      unsigned NumConds = 0; -    if ((NumConds = MCDCBuilder.popAndReturnCondCount(E))) +    if (IsRootNode && (NumConds = MCDCBuilder.getTotalConditionsAndReset(E)))        createDecisionRegion(E, getRegionBitmap(E), NumConds);      // Extract the RHS's Execution Counter. @@ -1913,28 +2061,13 @@ struct CounterCoverageMappingBuilder      // Extract the Parent Region Counter.      Counter ParentCnt = getRegion().getCounter(); -    // Extract the MCDC condition IDs (returns 0 if not needed). -    MCDCConditionID NextOrID = MCDCBuilder.getNextLOrCondID(); -    MCDCConditionID NextAndID = MCDCBuilder.getNextLAndCondID(); -    MCDCConditionID LHSid = MCDCBuilder.getCondID(E->getLHS()); -    MCDCConditionID RHSid = MCDCBuilder.getCondID(E->getRHS()); -      // Create Branch Region around LHS condition. -    // MC/DC: For "LHS || RHS" -    // - If LHS is TRUE, execution goes to the LHS of the next logical-AND. -    //   If that does not exist, execution exits (ID == 0). -    // - If LHS is FALSE, execution goes to the RHS.      createBranchRegion(E->getLHS(), subtractCounters(ParentCnt, RHSExecCnt), -                       RHSExecCnt, LHSid, NextAndID, RHSid); +                       RHSExecCnt, DecisionLHS);      // Create Branch Region around RHS condition. -    // MC/DC: For "LHS || RHS" -    // - If RHS is TRUE, execution goes to LHS of the next logical-AND. -    //   If that does not exist, execution exits (ID == 0). -    // - If RHS is FALSE, execution goes to the LHS of the next logical-OR. -    //   If that does not exist, execution exits (ID == 0).      createBranchRegion(E->getRHS(), subtractCounters(RHSExecCnt, RHSFalseCnt), -                       RHSFalseCnt, RHSid, NextAndID, NextOrID); +                       RHSFalseCnt, DecisionRHS);    }    void VisitLambdaExpr(const LambdaExpr *LE) { | 
