10b57cec5SDimitry Andric //===--- CoverageMappingGen.cpp - Coverage mapping generation ---*- C++ -*-===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // Instrumentation-based code coverage mapping generator
100b57cec5SDimitry Andric //
110b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
120b57cec5SDimitry Andric
130b57cec5SDimitry Andric #include "CoverageMappingGen.h"
140b57cec5SDimitry Andric #include "CodeGenFunction.h"
150b57cec5SDimitry Andric #include "clang/AST/StmtVisitor.h"
165ffd83dbSDimitry Andric #include "clang/Basic/Diagnostic.h"
175ffd83dbSDimitry Andric #include "clang/Basic/FileManager.h"
185ffd83dbSDimitry Andric #include "clang/Frontend/FrontendDiagnostic.h"
190b57cec5SDimitry Andric #include "clang/Lex/Lexer.h"
200b57cec5SDimitry Andric #include "llvm/ADT/SmallSet.h"
210b57cec5SDimitry Andric #include "llvm/ADT/StringExtras.h"
220b57cec5SDimitry Andric #include "llvm/ProfileData/Coverage/CoverageMapping.h"
230b57cec5SDimitry Andric #include "llvm/ProfileData/Coverage/CoverageMappingReader.h"
240b57cec5SDimitry Andric #include "llvm/ProfileData/Coverage/CoverageMappingWriter.h"
250b57cec5SDimitry Andric #include "llvm/ProfileData/InstrProfReader.h"
260b57cec5SDimitry Andric #include "llvm/Support/FileSystem.h"
270b57cec5SDimitry Andric #include "llvm/Support/Path.h"
28bdd1243dSDimitry Andric #include <optional>
290b57cec5SDimitry Andric
305ffd83dbSDimitry Andric // This selects the coverage mapping format defined when `InstrProfData.inc`
315ffd83dbSDimitry Andric // is textually included.
325ffd83dbSDimitry Andric #define COVMAP_V3
335ffd83dbSDimitry Andric
34e8d8bef9SDimitry Andric static llvm::cl::opt<bool> EmptyLineCommentCoverage(
35e8d8bef9SDimitry Andric "emptyline-comment-coverage",
36e8d8bef9SDimitry Andric llvm::cl::desc("Emit emptylines and comment lines as skipped regions (only "
37e8d8bef9SDimitry Andric "disable it on test)"),
38e8d8bef9SDimitry Andric llvm::cl::init(true), llvm::cl::Hidden);
39e8d8bef9SDimitry Andric
40a58f00eaSDimitry Andric llvm::cl::opt<bool> SystemHeadersCoverage(
41fe013be4SDimitry Andric "system-headers-coverage",
42fe013be4SDimitry Andric llvm::cl::desc("Enable collecting coverage from system headers"),
43fe013be4SDimitry Andric llvm::cl::init(false), llvm::cl::Hidden);
44fe013be4SDimitry Andric
450b57cec5SDimitry Andric using namespace clang;
460b57cec5SDimitry Andric using namespace CodeGen;
470b57cec5SDimitry Andric using namespace llvm::coverage;
480b57cec5SDimitry Andric
49e8d8bef9SDimitry Andric CoverageSourceInfo *
setUpCoverageCallbacks(Preprocessor & PP)50e8d8bef9SDimitry Andric CoverageMappingModuleGen::setUpCoverageCallbacks(Preprocessor &PP) {
51e8d8bef9SDimitry Andric CoverageSourceInfo *CoverageInfo =
52e8d8bef9SDimitry Andric new CoverageSourceInfo(PP.getSourceManager());
53e8d8bef9SDimitry Andric PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(CoverageInfo));
54e8d8bef9SDimitry Andric if (EmptyLineCommentCoverage) {
55e8d8bef9SDimitry Andric PP.addCommentHandler(CoverageInfo);
56e8d8bef9SDimitry Andric PP.setEmptylineHandler(CoverageInfo);
57e8d8bef9SDimitry Andric PP.setPreprocessToken(true);
58e8d8bef9SDimitry Andric PP.setTokenWatcher([CoverageInfo](clang::Token Tok) {
59e8d8bef9SDimitry Andric // Update previous token location.
60e8d8bef9SDimitry Andric CoverageInfo->PrevTokLoc = Tok.getLocation();
61e8d8bef9SDimitry Andric if (Tok.getKind() != clang::tok::eod)
62e8d8bef9SDimitry Andric CoverageInfo->updateNextTokLoc(Tok.getLocation());
63e8d8bef9SDimitry Andric });
64e8d8bef9SDimitry Andric }
65e8d8bef9SDimitry Andric return CoverageInfo;
66e8d8bef9SDimitry Andric }
67e8d8bef9SDimitry Andric
AddSkippedRange(SourceRange Range,SkippedRange::Kind RangeKind)6881ad6265SDimitry Andric void CoverageSourceInfo::AddSkippedRange(SourceRange Range,
6981ad6265SDimitry Andric SkippedRange::Kind RangeKind) {
70e8d8bef9SDimitry Andric if (EmptyLineCommentCoverage && !SkippedRanges.empty() &&
71e8d8bef9SDimitry Andric PrevTokLoc == SkippedRanges.back().PrevTokLoc &&
72e8d8bef9SDimitry Andric SourceMgr.isWrittenInSameFile(SkippedRanges.back().Range.getEnd(),
73e8d8bef9SDimitry Andric Range.getBegin()))
74e8d8bef9SDimitry Andric SkippedRanges.back().Range.setEnd(Range.getEnd());
75e8d8bef9SDimitry Andric else
7681ad6265SDimitry Andric SkippedRanges.push_back({Range, RangeKind, PrevTokLoc});
77e8d8bef9SDimitry Andric }
78e8d8bef9SDimitry Andric
SourceRangeSkipped(SourceRange Range,SourceLocation)790b57cec5SDimitry Andric void CoverageSourceInfo::SourceRangeSkipped(SourceRange Range, SourceLocation) {
8081ad6265SDimitry Andric AddSkippedRange(Range, SkippedRange::PPIfElse);
81e8d8bef9SDimitry Andric }
82e8d8bef9SDimitry Andric
HandleEmptyline(SourceRange Range)83e8d8bef9SDimitry Andric void CoverageSourceInfo::HandleEmptyline(SourceRange Range) {
8481ad6265SDimitry Andric AddSkippedRange(Range, SkippedRange::EmptyLine);
85e8d8bef9SDimitry Andric }
86e8d8bef9SDimitry Andric
HandleComment(Preprocessor & PP,SourceRange Range)87e8d8bef9SDimitry Andric bool CoverageSourceInfo::HandleComment(Preprocessor &PP, SourceRange Range) {
8881ad6265SDimitry Andric AddSkippedRange(Range, SkippedRange::Comment);
89e8d8bef9SDimitry Andric return false;
90e8d8bef9SDimitry Andric }
91e8d8bef9SDimitry Andric
updateNextTokLoc(SourceLocation Loc)92e8d8bef9SDimitry Andric void CoverageSourceInfo::updateNextTokLoc(SourceLocation Loc) {
93e8d8bef9SDimitry Andric if (!SkippedRanges.empty() && SkippedRanges.back().NextTokLoc.isInvalid())
94e8d8bef9SDimitry Andric SkippedRanges.back().NextTokLoc = Loc;
950b57cec5SDimitry Andric }
960b57cec5SDimitry Andric
970b57cec5SDimitry Andric namespace {
98cdc20ff6SDimitry Andric using MCDCConditionID = CounterMappingRegion::MCDCConditionID;
99cdc20ff6SDimitry Andric using MCDCParameters = CounterMappingRegion::MCDCParameters;
1000b57cec5SDimitry Andric
1010b57cec5SDimitry Andric /// A region of source code that can be mapped to a counter.
1020b57cec5SDimitry Andric class SourceMappingRegion {
103e8d8bef9SDimitry Andric /// Primary Counter that is also used for Branch Regions for "True" branches.
1040b57cec5SDimitry Andric Counter Count;
1050b57cec5SDimitry Andric
106e8d8bef9SDimitry Andric /// Secondary Counter used for Branch Regions for "False" branches.
107bdd1243dSDimitry Andric std::optional<Counter> FalseCount;
108e8d8bef9SDimitry Andric
109cdc20ff6SDimitry Andric /// Parameters used for Modified Condition/Decision Coverage
110cdc20ff6SDimitry Andric MCDCParameters MCDCParams;
111cdc20ff6SDimitry Andric
1120b57cec5SDimitry Andric /// The region's starting location.
113bdd1243dSDimitry Andric std::optional<SourceLocation> LocStart;
1140b57cec5SDimitry Andric
1150b57cec5SDimitry Andric /// The region's ending location.
116bdd1243dSDimitry Andric std::optional<SourceLocation> LocEnd;
1170b57cec5SDimitry Andric
1180b57cec5SDimitry Andric /// Whether this region is a gap region. The count from a gap region is set
1190b57cec5SDimitry Andric /// as the line execution count if there are no other regions on the line.
1200b57cec5SDimitry Andric bool GapRegion;
1210b57cec5SDimitry Andric
122a58f00eaSDimitry Andric /// Whetever this region is skipped ('if constexpr' or 'if consteval' untaken
123a58f00eaSDimitry Andric /// branch, or anything skipped but not empty line / comments)
124a58f00eaSDimitry Andric bool SkippedRegion;
125a58f00eaSDimitry Andric
1260b57cec5SDimitry Andric public:
SourceMappingRegion(Counter Count,std::optional<SourceLocation> LocStart,std::optional<SourceLocation> LocEnd,bool GapRegion=false)127bdd1243dSDimitry Andric SourceMappingRegion(Counter Count, std::optional<SourceLocation> LocStart,
128bdd1243dSDimitry Andric std::optional<SourceLocation> LocEnd,
129bdd1243dSDimitry Andric bool GapRegion = false)
130a58f00eaSDimitry Andric : Count(Count), LocStart(LocStart), LocEnd(LocEnd), GapRegion(GapRegion),
131a58f00eaSDimitry Andric SkippedRegion(false) {}
1320b57cec5SDimitry Andric
SourceMappingRegion(Counter Count,std::optional<Counter> FalseCount,MCDCParameters MCDCParams,std::optional<SourceLocation> LocStart,std::optional<SourceLocation> LocEnd,bool GapRegion=false)133bdd1243dSDimitry Andric SourceMappingRegion(Counter Count, std::optional<Counter> FalseCount,
134cdc20ff6SDimitry Andric MCDCParameters MCDCParams,
135bdd1243dSDimitry Andric std::optional<SourceLocation> LocStart,
136bdd1243dSDimitry Andric std::optional<SourceLocation> LocEnd,
137bdd1243dSDimitry Andric bool GapRegion = false)
138cdc20ff6SDimitry Andric : Count(Count), FalseCount(FalseCount), MCDCParams(MCDCParams),
139a58f00eaSDimitry Andric LocStart(LocStart), LocEnd(LocEnd), GapRegion(GapRegion),
140a58f00eaSDimitry Andric SkippedRegion(false) {}
141cdc20ff6SDimitry Andric
SourceMappingRegion(MCDCParameters MCDCParams,std::optional<SourceLocation> LocStart,std::optional<SourceLocation> LocEnd)142cdc20ff6SDimitry Andric SourceMappingRegion(MCDCParameters MCDCParams,
143cdc20ff6SDimitry Andric std::optional<SourceLocation> LocStart,
144cdc20ff6SDimitry Andric std::optional<SourceLocation> LocEnd)
145cdc20ff6SDimitry Andric : MCDCParams(MCDCParams), LocStart(LocStart), LocEnd(LocEnd),
146a58f00eaSDimitry Andric GapRegion(false), SkippedRegion(false) {}
147e8d8bef9SDimitry Andric
getCounter() const1480b57cec5SDimitry Andric const Counter &getCounter() const { return Count; }
1490b57cec5SDimitry Andric
getFalseCounter() const150e8d8bef9SDimitry Andric const Counter &getFalseCounter() const {
151e8d8bef9SDimitry Andric assert(FalseCount && "Region has no alternate counter");
152e8d8bef9SDimitry Andric return *FalseCount;
153e8d8bef9SDimitry Andric }
154e8d8bef9SDimitry Andric
setCounter(Counter C)1550b57cec5SDimitry Andric void setCounter(Counter C) { Count = C; }
1560b57cec5SDimitry Andric
hasStartLoc() const15781ad6265SDimitry Andric bool hasStartLoc() const { return LocStart.has_value(); }
1580b57cec5SDimitry Andric
setStartLoc(SourceLocation Loc)1590b57cec5SDimitry Andric void setStartLoc(SourceLocation Loc) { LocStart = Loc; }
1600b57cec5SDimitry Andric
getBeginLoc() const1610b57cec5SDimitry Andric SourceLocation getBeginLoc() const {
1620b57cec5SDimitry Andric assert(LocStart && "Region has no start location");
1630b57cec5SDimitry Andric return *LocStart;
1640b57cec5SDimitry Andric }
1650b57cec5SDimitry Andric
hasEndLoc() const16681ad6265SDimitry Andric bool hasEndLoc() const { return LocEnd.has_value(); }
1670b57cec5SDimitry Andric
setEndLoc(SourceLocation Loc)1680b57cec5SDimitry Andric void setEndLoc(SourceLocation Loc) {
1690b57cec5SDimitry Andric assert(Loc.isValid() && "Setting an invalid end location");
1700b57cec5SDimitry Andric LocEnd = Loc;
1710b57cec5SDimitry Andric }
1720b57cec5SDimitry Andric
getEndLoc() const1730b57cec5SDimitry Andric SourceLocation getEndLoc() const {
1740b57cec5SDimitry Andric assert(LocEnd && "Region has no end location");
1750b57cec5SDimitry Andric return *LocEnd;
1760b57cec5SDimitry Andric }
1770b57cec5SDimitry Andric
isGap() const1780b57cec5SDimitry Andric bool isGap() const { return GapRegion; }
1790b57cec5SDimitry Andric
setGap(bool Gap)1800b57cec5SDimitry Andric void setGap(bool Gap) { GapRegion = Gap; }
181e8d8bef9SDimitry Andric
isSkipped() const182a58f00eaSDimitry Andric bool isSkipped() const { return SkippedRegion; }
183a58f00eaSDimitry Andric
setSkipped(bool Skipped)184a58f00eaSDimitry Andric void setSkipped(bool Skipped) { SkippedRegion = Skipped; }
185a58f00eaSDimitry Andric
isBranch() const18681ad6265SDimitry Andric bool isBranch() const { return FalseCount.has_value(); }
187cdc20ff6SDimitry Andric
isMCDCDecision() const188cdc20ff6SDimitry Andric bool isMCDCDecision() const { return MCDCParams.NumConditions != 0; }
189cdc20ff6SDimitry Andric
getMCDCParams() const190cdc20ff6SDimitry Andric const MCDCParameters &getMCDCParams() const { return MCDCParams; }
1910b57cec5SDimitry Andric };
1920b57cec5SDimitry Andric
1930b57cec5SDimitry Andric /// Spelling locations for the start and end of a source region.
1940b57cec5SDimitry Andric struct SpellingRegion {
1950b57cec5SDimitry Andric /// The line where the region starts.
1960b57cec5SDimitry Andric unsigned LineStart;
1970b57cec5SDimitry Andric
1980b57cec5SDimitry Andric /// The column where the region starts.
1990b57cec5SDimitry Andric unsigned ColumnStart;
2000b57cec5SDimitry Andric
2010b57cec5SDimitry Andric /// The line where the region ends.
2020b57cec5SDimitry Andric unsigned LineEnd;
2030b57cec5SDimitry Andric
2040b57cec5SDimitry Andric /// The column where the region ends.
2050b57cec5SDimitry Andric unsigned ColumnEnd;
2060b57cec5SDimitry Andric
SpellingRegion__anonc2c3cfe60211::SpellingRegion2070b57cec5SDimitry Andric SpellingRegion(SourceManager &SM, SourceLocation LocStart,
2080b57cec5SDimitry Andric SourceLocation LocEnd) {
2090b57cec5SDimitry Andric LineStart = SM.getSpellingLineNumber(LocStart);
2100b57cec5SDimitry Andric ColumnStart = SM.getSpellingColumnNumber(LocStart);
2110b57cec5SDimitry Andric LineEnd = SM.getSpellingLineNumber(LocEnd);
2120b57cec5SDimitry Andric ColumnEnd = SM.getSpellingColumnNumber(LocEnd);
2130b57cec5SDimitry Andric }
2140b57cec5SDimitry Andric
SpellingRegion__anonc2c3cfe60211::SpellingRegion2150b57cec5SDimitry Andric SpellingRegion(SourceManager &SM, SourceMappingRegion &R)
2160b57cec5SDimitry Andric : SpellingRegion(SM, R.getBeginLoc(), R.getEndLoc()) {}
2170b57cec5SDimitry Andric
2180b57cec5SDimitry Andric /// Check if the start and end locations appear in source order, i.e
2190b57cec5SDimitry Andric /// top->bottom, left->right.
isInSourceOrder__anonc2c3cfe60211::SpellingRegion2200b57cec5SDimitry Andric bool isInSourceOrder() const {
2210b57cec5SDimitry Andric return (LineStart < LineEnd) ||
2220b57cec5SDimitry Andric (LineStart == LineEnd && ColumnStart <= ColumnEnd);
2230b57cec5SDimitry Andric }
2240b57cec5SDimitry Andric };
2250b57cec5SDimitry Andric
2260b57cec5SDimitry Andric /// Provides the common functionality for the different
2270b57cec5SDimitry Andric /// coverage mapping region builders.
2280b57cec5SDimitry Andric class CoverageMappingBuilder {
2290b57cec5SDimitry Andric public:
2300b57cec5SDimitry Andric CoverageMappingModuleGen &CVM;
2310b57cec5SDimitry Andric SourceManager &SM;
2320b57cec5SDimitry Andric const LangOptions &LangOpts;
2330b57cec5SDimitry Andric
2340b57cec5SDimitry Andric private:
2350b57cec5SDimitry Andric /// Map of clang's FileIDs to IDs used for coverage mapping.
2360b57cec5SDimitry Andric llvm::SmallDenseMap<FileID, std::pair<unsigned, SourceLocation>, 8>
2370b57cec5SDimitry Andric FileIDMapping;
2380b57cec5SDimitry Andric
2390b57cec5SDimitry Andric public:
2400b57cec5SDimitry Andric /// The coverage mapping regions for this function
2410b57cec5SDimitry Andric llvm::SmallVector<CounterMappingRegion, 32> MappingRegions;
2420b57cec5SDimitry Andric /// The source mapping regions for this function.
2430b57cec5SDimitry Andric std::vector<SourceMappingRegion> SourceRegions;
2440b57cec5SDimitry Andric
2450b57cec5SDimitry Andric /// A set of regions which can be used as a filter.
2460b57cec5SDimitry Andric ///
2470b57cec5SDimitry Andric /// It is produced by emitExpansionRegions() and is used in
2480b57cec5SDimitry Andric /// emitSourceRegions() to suppress producing code regions if
2490b57cec5SDimitry Andric /// the same area is covered by expansion regions.
2500b57cec5SDimitry Andric typedef llvm::SmallSet<std::pair<SourceLocation, SourceLocation>, 8>
2510b57cec5SDimitry Andric SourceRegionFilter;
2520b57cec5SDimitry Andric
CoverageMappingBuilder(CoverageMappingModuleGen & CVM,SourceManager & SM,const LangOptions & LangOpts)2530b57cec5SDimitry Andric CoverageMappingBuilder(CoverageMappingModuleGen &CVM, SourceManager &SM,
2540b57cec5SDimitry Andric const LangOptions &LangOpts)
2550b57cec5SDimitry Andric : CVM(CVM), SM(SM), LangOpts(LangOpts) {}
2560b57cec5SDimitry Andric
2570b57cec5SDimitry Andric /// Return the precise end location for the given token.
getPreciseTokenLocEnd(SourceLocation Loc)2580b57cec5SDimitry Andric SourceLocation getPreciseTokenLocEnd(SourceLocation Loc) {
2590b57cec5SDimitry Andric // We avoid getLocForEndOfToken here, because it doesn't do what we want for
2600b57cec5SDimitry Andric // macro locations, which we just treat as expanded files.
2610b57cec5SDimitry Andric unsigned TokLen =
2620b57cec5SDimitry Andric Lexer::MeasureTokenLength(SM.getSpellingLoc(Loc), SM, LangOpts);
2630b57cec5SDimitry Andric return Loc.getLocWithOffset(TokLen);
2640b57cec5SDimitry Andric }
2650b57cec5SDimitry Andric
2660b57cec5SDimitry Andric /// Return the start location of an included file or expanded macro.
getStartOfFileOrMacro(SourceLocation Loc)2670b57cec5SDimitry Andric SourceLocation getStartOfFileOrMacro(SourceLocation Loc) {
2680b57cec5SDimitry Andric if (Loc.isMacroID())
2690b57cec5SDimitry Andric return Loc.getLocWithOffset(-SM.getFileOffset(Loc));
2700b57cec5SDimitry Andric return SM.getLocForStartOfFile(SM.getFileID(Loc));
2710b57cec5SDimitry Andric }
2720b57cec5SDimitry Andric
2730b57cec5SDimitry Andric /// Return the end location of an included file or expanded macro.
getEndOfFileOrMacro(SourceLocation Loc)2740b57cec5SDimitry Andric SourceLocation getEndOfFileOrMacro(SourceLocation Loc) {
2750b57cec5SDimitry Andric if (Loc.isMacroID())
2760b57cec5SDimitry Andric return Loc.getLocWithOffset(SM.getFileIDSize(SM.getFileID(Loc)) -
2770b57cec5SDimitry Andric SM.getFileOffset(Loc));
2780b57cec5SDimitry Andric return SM.getLocForEndOfFile(SM.getFileID(Loc));
2790b57cec5SDimitry Andric }
2800b57cec5SDimitry Andric
2810b57cec5SDimitry Andric /// Find out where the current file is included or macro is expanded.
getIncludeOrExpansionLoc(SourceLocation Loc)2820b57cec5SDimitry Andric SourceLocation getIncludeOrExpansionLoc(SourceLocation Loc) {
2830b57cec5SDimitry Andric return Loc.isMacroID() ? SM.getImmediateExpansionRange(Loc).getBegin()
2840b57cec5SDimitry Andric : SM.getIncludeLoc(SM.getFileID(Loc));
2850b57cec5SDimitry Andric }
2860b57cec5SDimitry Andric
2870b57cec5SDimitry Andric /// Return true if \c Loc is a location in a built-in macro.
isInBuiltin(SourceLocation Loc)2880b57cec5SDimitry Andric bool isInBuiltin(SourceLocation Loc) {
2890b57cec5SDimitry Andric return SM.getBufferName(SM.getSpellingLoc(Loc)) == "<built-in>";
2900b57cec5SDimitry Andric }
2910b57cec5SDimitry Andric
2920b57cec5SDimitry Andric /// Check whether \c Loc is included or expanded from \c Parent.
isNestedIn(SourceLocation Loc,FileID Parent)2930b57cec5SDimitry Andric bool isNestedIn(SourceLocation Loc, FileID Parent) {
2940b57cec5SDimitry Andric do {
2950b57cec5SDimitry Andric Loc = getIncludeOrExpansionLoc(Loc);
2960b57cec5SDimitry Andric if (Loc.isInvalid())
2970b57cec5SDimitry Andric return false;
2980b57cec5SDimitry Andric } while (!SM.isInFileID(Loc, Parent));
2990b57cec5SDimitry Andric return true;
3000b57cec5SDimitry Andric }
3010b57cec5SDimitry Andric
3020b57cec5SDimitry Andric /// Get the start of \c S ignoring macro arguments and builtin macros.
getStart(const Stmt * S)3030b57cec5SDimitry Andric SourceLocation getStart(const Stmt *S) {
3040b57cec5SDimitry Andric SourceLocation Loc = S->getBeginLoc();
3050b57cec5SDimitry Andric while (SM.isMacroArgExpansion(Loc) || isInBuiltin(Loc))
3060b57cec5SDimitry Andric Loc = SM.getImmediateExpansionRange(Loc).getBegin();
3070b57cec5SDimitry Andric return Loc;
3080b57cec5SDimitry Andric }
3090b57cec5SDimitry Andric
3100b57cec5SDimitry Andric /// Get the end of \c S ignoring macro arguments and builtin macros.
getEnd(const Stmt * S)3110b57cec5SDimitry Andric SourceLocation getEnd(const Stmt *S) {
3120b57cec5SDimitry Andric SourceLocation Loc = S->getEndLoc();
3130b57cec5SDimitry Andric while (SM.isMacroArgExpansion(Loc) || isInBuiltin(Loc))
3140b57cec5SDimitry Andric Loc = SM.getImmediateExpansionRange(Loc).getBegin();
3150b57cec5SDimitry Andric return getPreciseTokenLocEnd(Loc);
3160b57cec5SDimitry Andric }
3170b57cec5SDimitry Andric
3180b57cec5SDimitry Andric /// Find the set of files we have regions for and assign IDs
3190b57cec5SDimitry Andric ///
3200b57cec5SDimitry Andric /// Fills \c Mapping with the virtual file mapping needed to write out
3210b57cec5SDimitry Andric /// coverage and collects the necessary file information to emit source and
3220b57cec5SDimitry Andric /// expansion regions.
gatherFileIDs(SmallVectorImpl<unsigned> & Mapping)3230b57cec5SDimitry Andric void gatherFileIDs(SmallVectorImpl<unsigned> &Mapping) {
3240b57cec5SDimitry Andric FileIDMapping.clear();
3250b57cec5SDimitry Andric
3260b57cec5SDimitry Andric llvm::SmallSet<FileID, 8> Visited;
3270b57cec5SDimitry Andric SmallVector<std::pair<SourceLocation, unsigned>, 8> FileLocs;
3280b57cec5SDimitry Andric for (const auto &Region : SourceRegions) {
3290b57cec5SDimitry Andric SourceLocation Loc = Region.getBeginLoc();
3300b57cec5SDimitry Andric FileID File = SM.getFileID(Loc);
3310b57cec5SDimitry Andric if (!Visited.insert(File).second)
3320b57cec5SDimitry Andric continue;
3330b57cec5SDimitry Andric
334fe013be4SDimitry Andric // Do not map FileID's associated with system headers unless collecting
335fe013be4SDimitry Andric // coverage from system headers is explicitly enabled.
336fe013be4SDimitry Andric if (!SystemHeadersCoverage && SM.isInSystemHeader(SM.getSpellingLoc(Loc)))
3370b57cec5SDimitry Andric continue;
3380b57cec5SDimitry Andric
3390b57cec5SDimitry Andric unsigned Depth = 0;
3400b57cec5SDimitry Andric for (SourceLocation Parent = getIncludeOrExpansionLoc(Loc);
3410b57cec5SDimitry Andric Parent.isValid(); Parent = getIncludeOrExpansionLoc(Parent))
3420b57cec5SDimitry Andric ++Depth;
3430b57cec5SDimitry Andric FileLocs.push_back(std::make_pair(Loc, Depth));
3440b57cec5SDimitry Andric }
3450b57cec5SDimitry Andric llvm::stable_sort(FileLocs, llvm::less_second());
3460b57cec5SDimitry Andric
3470b57cec5SDimitry Andric for (const auto &FL : FileLocs) {
3480b57cec5SDimitry Andric SourceLocation Loc = FL.first;
3490b57cec5SDimitry Andric FileID SpellingFile = SM.getDecomposedSpellingLoc(Loc).first;
350c9157d92SDimitry Andric auto Entry = SM.getFileEntryRefForID(SpellingFile);
3510b57cec5SDimitry Andric if (!Entry)
3520b57cec5SDimitry Andric continue;
3530b57cec5SDimitry Andric
3540b57cec5SDimitry Andric FileIDMapping[SM.getFileID(Loc)] = std::make_pair(Mapping.size(), Loc);
355c9157d92SDimitry Andric Mapping.push_back(CVM.getFileID(*Entry));
3560b57cec5SDimitry Andric }
3570b57cec5SDimitry Andric }
3580b57cec5SDimitry Andric
3590b57cec5SDimitry Andric /// Get the coverage mapping file ID for \c Loc.
3600b57cec5SDimitry Andric ///
361bdd1243dSDimitry Andric /// If such file id doesn't exist, return std::nullopt.
getCoverageFileID(SourceLocation Loc)362bdd1243dSDimitry Andric std::optional<unsigned> getCoverageFileID(SourceLocation Loc) {
3630b57cec5SDimitry Andric auto Mapping = FileIDMapping.find(SM.getFileID(Loc));
3640b57cec5SDimitry Andric if (Mapping != FileIDMapping.end())
3650b57cec5SDimitry Andric return Mapping->second.first;
366bdd1243dSDimitry Andric return std::nullopt;
3670b57cec5SDimitry Andric }
3680b57cec5SDimitry Andric
369e8d8bef9SDimitry Andric /// This shrinks the skipped range if it spans a line that contains a
370e8d8bef9SDimitry Andric /// non-comment token. If shrinking the skipped range would make it empty,
371bdd1243dSDimitry Andric /// this returns std::nullopt.
37281ad6265SDimitry Andric /// Note this function can potentially be expensive because
37381ad6265SDimitry Andric /// getSpellingLineNumber uses getLineNumber, which is expensive.
adjustSkippedRange(SourceManager & SM,SourceLocation LocStart,SourceLocation LocEnd,SourceLocation PrevTokLoc,SourceLocation NextTokLoc)374bdd1243dSDimitry Andric std::optional<SpellingRegion> adjustSkippedRange(SourceManager &SM,
375e8d8bef9SDimitry Andric SourceLocation LocStart,
376e8d8bef9SDimitry Andric SourceLocation LocEnd,
377e8d8bef9SDimitry Andric SourceLocation PrevTokLoc,
378e8d8bef9SDimitry Andric SourceLocation NextTokLoc) {
379e8d8bef9SDimitry Andric SpellingRegion SR{SM, LocStart, LocEnd};
380e8d8bef9SDimitry Andric SR.ColumnStart = 1;
381e8d8bef9SDimitry Andric if (PrevTokLoc.isValid() && SM.isWrittenInSameFile(LocStart, PrevTokLoc) &&
382e8d8bef9SDimitry Andric SR.LineStart == SM.getSpellingLineNumber(PrevTokLoc))
383e8d8bef9SDimitry Andric SR.LineStart++;
384e8d8bef9SDimitry Andric if (NextTokLoc.isValid() && SM.isWrittenInSameFile(LocEnd, NextTokLoc) &&
385e8d8bef9SDimitry Andric SR.LineEnd == SM.getSpellingLineNumber(NextTokLoc)) {
386e8d8bef9SDimitry Andric SR.LineEnd--;
387e8d8bef9SDimitry Andric SR.ColumnEnd++;
388e8d8bef9SDimitry Andric }
389e8d8bef9SDimitry Andric if (SR.isInSourceOrder())
390e8d8bef9SDimitry Andric return SR;
391bdd1243dSDimitry Andric return std::nullopt;
392e8d8bef9SDimitry Andric }
393e8d8bef9SDimitry Andric
3940b57cec5SDimitry Andric /// Gather all the regions that were skipped by the preprocessor
395e8d8bef9SDimitry Andric /// using the constructs like #if or comments.
gatherSkippedRegions()3960b57cec5SDimitry Andric void gatherSkippedRegions() {
3970b57cec5SDimitry Andric /// An array of the minimum lineStarts and the maximum lineEnds
3980b57cec5SDimitry Andric /// for mapping regions from the appropriate source files.
3990b57cec5SDimitry Andric llvm::SmallVector<std::pair<unsigned, unsigned>, 8> FileLineRanges;
4000b57cec5SDimitry Andric FileLineRanges.resize(
4010b57cec5SDimitry Andric FileIDMapping.size(),
4020b57cec5SDimitry Andric std::make_pair(std::numeric_limits<unsigned>::max(), 0));
4030b57cec5SDimitry Andric for (const auto &R : MappingRegions) {
4040b57cec5SDimitry Andric FileLineRanges[R.FileID].first =
4050b57cec5SDimitry Andric std::min(FileLineRanges[R.FileID].first, R.LineStart);
4060b57cec5SDimitry Andric FileLineRanges[R.FileID].second =
4070b57cec5SDimitry Andric std::max(FileLineRanges[R.FileID].second, R.LineEnd);
4080b57cec5SDimitry Andric }
4090b57cec5SDimitry Andric
4100b57cec5SDimitry Andric auto SkippedRanges = CVM.getSourceInfo().getSkippedRanges();
411e8d8bef9SDimitry Andric for (auto &I : SkippedRanges) {
412e8d8bef9SDimitry Andric SourceRange Range = I.Range;
413e8d8bef9SDimitry Andric auto LocStart = Range.getBegin();
414e8d8bef9SDimitry Andric auto LocEnd = Range.getEnd();
4150b57cec5SDimitry Andric assert(SM.isWrittenInSameFile(LocStart, LocEnd) &&
4160b57cec5SDimitry Andric "region spans multiple files");
4170b57cec5SDimitry Andric
4180b57cec5SDimitry Andric auto CovFileID = getCoverageFileID(LocStart);
4190b57cec5SDimitry Andric if (!CovFileID)
4200b57cec5SDimitry Andric continue;
421bdd1243dSDimitry Andric std::optional<SpellingRegion> SR;
42281ad6265SDimitry Andric if (I.isComment())
42381ad6265SDimitry Andric SR = adjustSkippedRange(SM, LocStart, LocEnd, I.PrevTokLoc,
42481ad6265SDimitry Andric I.NextTokLoc);
42581ad6265SDimitry Andric else if (I.isPPIfElse() || I.isEmptyLine())
42681ad6265SDimitry Andric SR = {SM, LocStart, LocEnd};
42781ad6265SDimitry Andric
42881ad6265SDimitry Andric if (!SR)
429e8d8bef9SDimitry Andric continue;
4300b57cec5SDimitry Andric auto Region = CounterMappingRegion::makeSkipped(
431e8d8bef9SDimitry Andric *CovFileID, SR->LineStart, SR->ColumnStart, SR->LineEnd,
432e8d8bef9SDimitry Andric SR->ColumnEnd);
4330b57cec5SDimitry Andric // Make sure that we only collect the regions that are inside
4340b57cec5SDimitry Andric // the source code of this function.
4350b57cec5SDimitry Andric if (Region.LineStart >= FileLineRanges[*CovFileID].first &&
4360b57cec5SDimitry Andric Region.LineEnd <= FileLineRanges[*CovFileID].second)
4370b57cec5SDimitry Andric MappingRegions.push_back(Region);
4380b57cec5SDimitry Andric }
4390b57cec5SDimitry Andric }
4400b57cec5SDimitry Andric
4410b57cec5SDimitry Andric /// Generate the coverage counter mapping regions from collected
4420b57cec5SDimitry Andric /// source regions.
emitSourceRegions(const SourceRegionFilter & Filter)4430b57cec5SDimitry Andric void emitSourceRegions(const SourceRegionFilter &Filter) {
4440b57cec5SDimitry Andric for (const auto &Region : SourceRegions) {
4450b57cec5SDimitry Andric assert(Region.hasEndLoc() && "incomplete region");
4460b57cec5SDimitry Andric
4470b57cec5SDimitry Andric SourceLocation LocStart = Region.getBeginLoc();
4480b57cec5SDimitry Andric assert(SM.getFileID(LocStart).isValid() && "region in invalid file");
4490b57cec5SDimitry Andric
450fe013be4SDimitry Andric // Ignore regions from system headers unless collecting coverage from
451fe013be4SDimitry Andric // system headers is explicitly enabled.
452fe013be4SDimitry Andric if (!SystemHeadersCoverage &&
453fe013be4SDimitry Andric SM.isInSystemHeader(SM.getSpellingLoc(LocStart)))
4540b57cec5SDimitry Andric continue;
4550b57cec5SDimitry Andric
4560b57cec5SDimitry Andric auto CovFileID = getCoverageFileID(LocStart);
4570b57cec5SDimitry Andric // Ignore regions that don't have a file, such as builtin macros.
4580b57cec5SDimitry Andric if (!CovFileID)
4590b57cec5SDimitry Andric continue;
4600b57cec5SDimitry Andric
4610b57cec5SDimitry Andric SourceLocation LocEnd = Region.getEndLoc();
4620b57cec5SDimitry Andric assert(SM.isWrittenInSameFile(LocStart, LocEnd) &&
4630b57cec5SDimitry Andric "region spans multiple files");
4640b57cec5SDimitry Andric
4650b57cec5SDimitry Andric // Don't add code regions for the area covered by expansion regions.
4660b57cec5SDimitry Andric // This not only suppresses redundant regions, but sometimes prevents
4670b57cec5SDimitry Andric // creating regions with wrong counters if, for example, a statement's
4680b57cec5SDimitry Andric // body ends at the end of a nested macro.
4690b57cec5SDimitry Andric if (Filter.count(std::make_pair(LocStart, LocEnd)))
4700b57cec5SDimitry Andric continue;
4710b57cec5SDimitry Andric
4720b57cec5SDimitry Andric // Find the spelling locations for the mapping region.
4730b57cec5SDimitry Andric SpellingRegion SR{SM, LocStart, LocEnd};
4740b57cec5SDimitry Andric assert(SR.isInSourceOrder() && "region start and end out of order");
4750b57cec5SDimitry Andric
4760b57cec5SDimitry Andric if (Region.isGap()) {
4770b57cec5SDimitry Andric MappingRegions.push_back(CounterMappingRegion::makeGapRegion(
4780b57cec5SDimitry Andric Region.getCounter(), *CovFileID, SR.LineStart, SR.ColumnStart,
4790b57cec5SDimitry Andric SR.LineEnd, SR.ColumnEnd));
480a58f00eaSDimitry Andric } else if (Region.isSkipped()) {
481a58f00eaSDimitry Andric MappingRegions.push_back(CounterMappingRegion::makeSkipped(
482a58f00eaSDimitry Andric *CovFileID, SR.LineStart, SR.ColumnStart, SR.LineEnd,
483a58f00eaSDimitry Andric SR.ColumnEnd));
484e8d8bef9SDimitry Andric } else if (Region.isBranch()) {
485e8d8bef9SDimitry Andric MappingRegions.push_back(CounterMappingRegion::makeBranchRegion(
486cdc20ff6SDimitry Andric Region.getCounter(), Region.getFalseCounter(),
487cdc20ff6SDimitry Andric Region.getMCDCParams(), *CovFileID, SR.LineStart, SR.ColumnStart,
488cdc20ff6SDimitry Andric SR.LineEnd, SR.ColumnEnd));
489cdc20ff6SDimitry Andric } else if (Region.isMCDCDecision()) {
490cdc20ff6SDimitry Andric MappingRegions.push_back(CounterMappingRegion::makeDecisionRegion(
491cdc20ff6SDimitry Andric Region.getMCDCParams(), *CovFileID, SR.LineStart, SR.ColumnStart,
492cdc20ff6SDimitry Andric SR.LineEnd, SR.ColumnEnd));
4930b57cec5SDimitry Andric } else {
4940b57cec5SDimitry Andric MappingRegions.push_back(CounterMappingRegion::makeRegion(
4950b57cec5SDimitry Andric Region.getCounter(), *CovFileID, SR.LineStart, SR.ColumnStart,
4960b57cec5SDimitry Andric SR.LineEnd, SR.ColumnEnd));
4970b57cec5SDimitry Andric }
4980b57cec5SDimitry Andric }
4990b57cec5SDimitry Andric }
5000b57cec5SDimitry Andric
5010b57cec5SDimitry Andric /// Generate expansion regions for each virtual file we've seen.
emitExpansionRegions()5020b57cec5SDimitry Andric SourceRegionFilter emitExpansionRegions() {
5030b57cec5SDimitry Andric SourceRegionFilter Filter;
5040b57cec5SDimitry Andric for (const auto &FM : FileIDMapping) {
5050b57cec5SDimitry Andric SourceLocation ExpandedLoc = FM.second.second;
5060b57cec5SDimitry Andric SourceLocation ParentLoc = getIncludeOrExpansionLoc(ExpandedLoc);
5070b57cec5SDimitry Andric if (ParentLoc.isInvalid())
5080b57cec5SDimitry Andric continue;
5090b57cec5SDimitry Andric
5100b57cec5SDimitry Andric auto ParentFileID = getCoverageFileID(ParentLoc);
5110b57cec5SDimitry Andric if (!ParentFileID)
5120b57cec5SDimitry Andric continue;
5130b57cec5SDimitry Andric auto ExpandedFileID = getCoverageFileID(ExpandedLoc);
5140b57cec5SDimitry Andric assert(ExpandedFileID && "expansion in uncovered file");
5150b57cec5SDimitry Andric
5160b57cec5SDimitry Andric SourceLocation LocEnd = getPreciseTokenLocEnd(ParentLoc);
5170b57cec5SDimitry Andric assert(SM.isWrittenInSameFile(ParentLoc, LocEnd) &&
5180b57cec5SDimitry Andric "region spans multiple files");
5190b57cec5SDimitry Andric Filter.insert(std::make_pair(ParentLoc, LocEnd));
5200b57cec5SDimitry Andric
5210b57cec5SDimitry Andric SpellingRegion SR{SM, ParentLoc, LocEnd};
5220b57cec5SDimitry Andric assert(SR.isInSourceOrder() && "region start and end out of order");
5230b57cec5SDimitry Andric MappingRegions.push_back(CounterMappingRegion::makeExpansion(
5240b57cec5SDimitry Andric *ParentFileID, *ExpandedFileID, SR.LineStart, SR.ColumnStart,
5250b57cec5SDimitry Andric SR.LineEnd, SR.ColumnEnd));
5260b57cec5SDimitry Andric }
5270b57cec5SDimitry Andric return Filter;
5280b57cec5SDimitry Andric }
5290b57cec5SDimitry Andric };
5300b57cec5SDimitry Andric
5310b57cec5SDimitry Andric /// Creates unreachable coverage regions for the functions that
5320b57cec5SDimitry Andric /// are not emitted.
5330b57cec5SDimitry Andric struct EmptyCoverageMappingBuilder : public CoverageMappingBuilder {
EmptyCoverageMappingBuilder__anonc2c3cfe60211::EmptyCoverageMappingBuilder5340b57cec5SDimitry Andric EmptyCoverageMappingBuilder(CoverageMappingModuleGen &CVM, SourceManager &SM,
5350b57cec5SDimitry Andric const LangOptions &LangOpts)
5360b57cec5SDimitry Andric : CoverageMappingBuilder(CVM, SM, LangOpts) {}
5370b57cec5SDimitry Andric
VisitDecl__anonc2c3cfe60211::EmptyCoverageMappingBuilder5380b57cec5SDimitry Andric void VisitDecl(const Decl *D) {
5390b57cec5SDimitry Andric if (!D->hasBody())
5400b57cec5SDimitry Andric return;
5410b57cec5SDimitry Andric auto Body = D->getBody();
5420b57cec5SDimitry Andric SourceLocation Start = getStart(Body);
5430b57cec5SDimitry Andric SourceLocation End = getEnd(Body);
5440b57cec5SDimitry Andric if (!SM.isWrittenInSameFile(Start, End)) {
5450b57cec5SDimitry Andric // Walk up to find the common ancestor.
5460b57cec5SDimitry Andric // Correct the locations accordingly.
5470b57cec5SDimitry Andric FileID StartFileID = SM.getFileID(Start);
5480b57cec5SDimitry Andric FileID EndFileID = SM.getFileID(End);
5490b57cec5SDimitry Andric while (StartFileID != EndFileID && !isNestedIn(End, StartFileID)) {
5500b57cec5SDimitry Andric Start = getIncludeOrExpansionLoc(Start);
5510b57cec5SDimitry Andric assert(Start.isValid() &&
5520b57cec5SDimitry Andric "Declaration start location not nested within a known region");
5530b57cec5SDimitry Andric StartFileID = SM.getFileID(Start);
5540b57cec5SDimitry Andric }
5550b57cec5SDimitry Andric while (StartFileID != EndFileID) {
5560b57cec5SDimitry Andric End = getPreciseTokenLocEnd(getIncludeOrExpansionLoc(End));
5570b57cec5SDimitry Andric assert(End.isValid() &&
5580b57cec5SDimitry Andric "Declaration end location not nested within a known region");
5590b57cec5SDimitry Andric EndFileID = SM.getFileID(End);
5600b57cec5SDimitry Andric }
5610b57cec5SDimitry Andric }
5620b57cec5SDimitry Andric SourceRegions.emplace_back(Counter(), Start, End);
5630b57cec5SDimitry Andric }
5640b57cec5SDimitry Andric
5650b57cec5SDimitry Andric /// Write the mapping data to the output stream
write__anonc2c3cfe60211::EmptyCoverageMappingBuilder5660b57cec5SDimitry Andric void write(llvm::raw_ostream &OS) {
5670b57cec5SDimitry Andric SmallVector<unsigned, 16> FileIDMapping;
5680b57cec5SDimitry Andric gatherFileIDs(FileIDMapping);
5690b57cec5SDimitry Andric emitSourceRegions(SourceRegionFilter());
5700b57cec5SDimitry Andric
5710b57cec5SDimitry Andric if (MappingRegions.empty())
5720b57cec5SDimitry Andric return;
5730b57cec5SDimitry Andric
574bdd1243dSDimitry Andric CoverageMappingWriter Writer(FileIDMapping, std::nullopt, MappingRegions);
5750b57cec5SDimitry Andric Writer.write(OS);
5760b57cec5SDimitry Andric }
5770b57cec5SDimitry Andric };
5780b57cec5SDimitry Andric
579cdc20ff6SDimitry Andric /// A wrapper object for maintaining stacks to track the resursive AST visitor
580cdc20ff6SDimitry Andric /// walks for the purpose of assigning IDs to leaf-level conditions measured by
581cdc20ff6SDimitry Andric /// MC/DC. The object is created with a reference to the MCDCBitmapMap that was
582cdc20ff6SDimitry Andric /// created during the initial AST walk. The presence of a bitmap associated
583cdc20ff6SDimitry Andric /// with a boolean expression (top-level logical operator nest) indicates that
584cdc20ff6SDimitry Andric /// the boolean expression qualified for MC/DC. The resulting condition IDs
585cdc20ff6SDimitry Andric /// are preserved in a map reference that is also provided during object
586cdc20ff6SDimitry Andric /// creation.
587cdc20ff6SDimitry Andric struct MCDCCoverageBuilder {
588cdc20ff6SDimitry Andric
589a58f00eaSDimitry Andric struct DecisionIDPair {
590a58f00eaSDimitry Andric MCDCConditionID TrueID = 0;
591a58f00eaSDimitry Andric MCDCConditionID FalseID = 0;
592a58f00eaSDimitry Andric };
593a58f00eaSDimitry Andric
594cdc20ff6SDimitry Andric /// The AST walk recursively visits nested logical-AND or logical-OR binary
595cdc20ff6SDimitry Andric /// operator nodes and then visits their LHS and RHS children nodes. As this
596cdc20ff6SDimitry Andric /// happens, the algorithm will assign IDs to each operator's LHS and RHS side
597cdc20ff6SDimitry Andric /// as the walk moves deeper into the nest. At each level of the recursive
598cdc20ff6SDimitry Andric /// nest, the LHS and RHS may actually correspond to larger subtrees (not
599cdc20ff6SDimitry Andric /// leaf-conditions). If this is the case, when that node is visited, the ID
600cdc20ff6SDimitry Andric /// assigned to the subtree is re-assigned to its LHS, and a new ID is given
601cdc20ff6SDimitry Andric /// to its RHS. At the end of the walk, all leaf-level conditions will have a
602cdc20ff6SDimitry Andric /// unique ID -- keep in mind that the final set of IDs may not be in
603cdc20ff6SDimitry Andric /// numerical order from left to right.
604cdc20ff6SDimitry Andric ///
605cdc20ff6SDimitry Andric /// Example: "x = (A && B) || (C && D) || (D && F)"
606cdc20ff6SDimitry Andric ///
607cdc20ff6SDimitry Andric /// Visit Depth1:
608cdc20ff6SDimitry Andric /// (A && B) || (C && D) || (D && F)
609cdc20ff6SDimitry Andric /// ^-------LHS--------^ ^-RHS--^
610cdc20ff6SDimitry Andric /// ID=1 ID=2
611cdc20ff6SDimitry Andric ///
612cdc20ff6SDimitry Andric /// Visit LHS-Depth2:
613cdc20ff6SDimitry Andric /// (A && B) || (C && D)
614cdc20ff6SDimitry Andric /// ^-LHS--^ ^-RHS--^
615cdc20ff6SDimitry Andric /// ID=1 ID=3
616cdc20ff6SDimitry Andric ///
617cdc20ff6SDimitry Andric /// Visit LHS-Depth3:
618cdc20ff6SDimitry Andric /// (A && B)
619cdc20ff6SDimitry Andric /// LHS RHS
620cdc20ff6SDimitry Andric /// ID=1 ID=4
621cdc20ff6SDimitry Andric ///
622cdc20ff6SDimitry Andric /// Visit RHS-Depth3:
623cdc20ff6SDimitry Andric /// (C && D)
624cdc20ff6SDimitry Andric /// LHS RHS
625cdc20ff6SDimitry Andric /// ID=3 ID=5
626cdc20ff6SDimitry Andric ///
627cdc20ff6SDimitry Andric /// Visit RHS-Depth2: (D && F)
628cdc20ff6SDimitry Andric /// LHS RHS
629cdc20ff6SDimitry Andric /// ID=2 ID=6
630cdc20ff6SDimitry Andric ///
631cdc20ff6SDimitry Andric /// Visit Depth1:
632cdc20ff6SDimitry Andric /// (A && B) || (C && D) || (D && F)
633cdc20ff6SDimitry Andric /// ID=1 ID=4 ID=3 ID=5 ID=2 ID=6
634cdc20ff6SDimitry Andric ///
635cdc20ff6SDimitry Andric /// A node ID of '0' always means MC/DC isn't being tracked.
636cdc20ff6SDimitry Andric ///
637a58f00eaSDimitry Andric /// As the AST walk proceeds recursively, the algorithm will also use a stack
638cdc20ff6SDimitry Andric /// to track the IDs of logical-AND and logical-OR operations on the RHS so
639cdc20ff6SDimitry Andric /// that it can be determined which nodes are executed next, depending on how
640cdc20ff6SDimitry Andric /// a LHS or RHS of a logical-AND or logical-OR is evaluated. This
641cdc20ff6SDimitry Andric /// information relies on the assigned IDs and are embedded within the
642cdc20ff6SDimitry Andric /// coverage region IDs of each branch region associated with a leaf-level
643cdc20ff6SDimitry Andric /// condition. This information helps the visualization tool reconstruct all
644a58f00eaSDimitry Andric /// possible test vectors for the purposes of MC/DC analysis. If a "next" node
645cdc20ff6SDimitry Andric /// ID is '0', it means it's the end of the test vector. The following rules
646cdc20ff6SDimitry Andric /// are used:
647cdc20ff6SDimitry Andric ///
648cdc20ff6SDimitry Andric /// For logical-AND ("LHS && RHS"):
649cdc20ff6SDimitry Andric /// - If LHS is TRUE, execution goes to the RHS node.
650cdc20ff6SDimitry Andric /// - If LHS is FALSE, execution goes to the LHS node of the next logical-OR.
651cdc20ff6SDimitry Andric /// If that does not exist, execution exits (ID == 0).
652cdc20ff6SDimitry Andric ///
653cdc20ff6SDimitry Andric /// - If RHS is TRUE, execution goes to LHS node of the next logical-AND.
654cdc20ff6SDimitry Andric /// If that does not exist, execution exits (ID == 0).
655cdc20ff6SDimitry Andric /// - If RHS is FALSE, execution goes to the LHS node of the next logical-OR.
656cdc20ff6SDimitry Andric /// If that does not exist, execution exits (ID == 0).
657cdc20ff6SDimitry Andric ///
658cdc20ff6SDimitry Andric /// For logical-OR ("LHS || RHS"):
659cdc20ff6SDimitry Andric /// - If LHS is TRUE, execution goes to the LHS node of the next logical-AND.
660cdc20ff6SDimitry Andric /// If that does not exist, execution exits (ID == 0).
661cdc20ff6SDimitry Andric /// - If LHS is FALSE, execution goes to the RHS node.
662cdc20ff6SDimitry Andric ///
663cdc20ff6SDimitry Andric /// - If RHS is TRUE, execution goes to LHS node of the next logical-AND.
664cdc20ff6SDimitry Andric /// If that does not exist, execution exits (ID == 0).
665cdc20ff6SDimitry Andric /// - If RHS is FALSE, execution goes to the LHS node of the next logical-OR.
666cdc20ff6SDimitry Andric /// If that does not exist, execution exits (ID == 0).
667cdc20ff6SDimitry Andric ///
668cdc20ff6SDimitry Andric /// Finally, the condition IDs are also used when instrumenting the code to
669cdc20ff6SDimitry Andric /// indicate a unique offset into a temporary bitmap that represents the true
670cdc20ff6SDimitry Andric /// or false evaluation of that particular condition.
671cdc20ff6SDimitry Andric ///
672cdc20ff6SDimitry Andric /// NOTE regarding the use of CodeGenFunction::stripCond(). Even though, for
673cdc20ff6SDimitry Andric /// simplicity, parentheses and unary logical-NOT operators are considered
674cdc20ff6SDimitry Andric /// part of their underlying condition for both MC/DC and branch coverage, the
675cdc20ff6SDimitry Andric /// condition IDs themselves are assigned and tracked using the underlying
676cdc20ff6SDimitry Andric /// condition itself. This is done solely for consistency since parentheses
677cdc20ff6SDimitry Andric /// and logical-NOTs are ignored when checking whether the condition is
678cdc20ff6SDimitry Andric /// actually an instrumentable condition. This can also make debugging a bit
679cdc20ff6SDimitry Andric /// easier.
680cdc20ff6SDimitry Andric
681cdc20ff6SDimitry Andric private:
682cdc20ff6SDimitry Andric CodeGenModule &CGM;
683cdc20ff6SDimitry Andric
684a58f00eaSDimitry Andric llvm::SmallVector<DecisionIDPair> DecisionStack;
685cdc20ff6SDimitry Andric llvm::DenseMap<const Stmt *, MCDCConditionID> &CondIDs;
686cdc20ff6SDimitry Andric llvm::DenseMap<const Stmt *, unsigned> &MCDCBitmapMap;
687cdc20ff6SDimitry Andric MCDCConditionID NextID = 1;
688cdc20ff6SDimitry Andric bool NotMapped = false;
689cdc20ff6SDimitry Andric
690a58f00eaSDimitry Andric /// Represent a sentinel value of [0,0] for the bottom of DecisionStack.
691a58f00eaSDimitry Andric static constexpr DecisionIDPair DecisionStackSentinel{0, 0};
692a58f00eaSDimitry Andric
693cdc20ff6SDimitry Andric /// Is this a logical-AND operation?
isLAnd__anonc2c3cfe60211::MCDCCoverageBuilder694cdc20ff6SDimitry Andric bool isLAnd(const BinaryOperator *E) const {
695cdc20ff6SDimitry Andric return E->getOpcode() == BO_LAnd;
696cdc20ff6SDimitry Andric }
697cdc20ff6SDimitry Andric
698cdc20ff6SDimitry Andric public:
MCDCCoverageBuilder__anonc2c3cfe60211::MCDCCoverageBuilder699cdc20ff6SDimitry Andric MCDCCoverageBuilder(CodeGenModule &CGM,
700cdc20ff6SDimitry Andric llvm::DenseMap<const Stmt *, MCDCConditionID> &CondIDMap,
701cdc20ff6SDimitry Andric llvm::DenseMap<const Stmt *, unsigned> &MCDCBitmapMap)
702a58f00eaSDimitry Andric : CGM(CGM), DecisionStack(1, DecisionStackSentinel), CondIDs(CondIDMap),
703a58f00eaSDimitry Andric MCDCBitmapMap(MCDCBitmapMap) {}
704cdc20ff6SDimitry Andric
705a58f00eaSDimitry Andric /// Return whether the build of the control flow map is at the top-level
706a58f00eaSDimitry Andric /// (root) of a logical operator nest in a boolean expression prior to the
707a58f00eaSDimitry Andric /// assignment of condition IDs.
isIdle__anonc2c3cfe60211::MCDCCoverageBuilder708a58f00eaSDimitry Andric bool isIdle() const { return (NextID == 1 && !NotMapped); }
709cdc20ff6SDimitry Andric
710a58f00eaSDimitry Andric /// Return whether any IDs have been assigned in the build of the control
711a58f00eaSDimitry Andric /// flow map, indicating that the map is being generated for this boolean
712a58f00eaSDimitry Andric /// expression.
isBuilding__anonc2c3cfe60211::MCDCCoverageBuilder713a58f00eaSDimitry Andric bool isBuilding() const { return (NextID > 1); }
714a58f00eaSDimitry Andric
715a58f00eaSDimitry Andric /// Set the given condition's ID.
setCondID__anonc2c3cfe60211::MCDCCoverageBuilder716a58f00eaSDimitry Andric void setCondID(const Expr *Cond, MCDCConditionID ID) {
717a58f00eaSDimitry Andric CondIDs[CodeGenFunction::stripCond(Cond)] = ID;
718cdc20ff6SDimitry Andric }
719cdc20ff6SDimitry Andric
720cdc20ff6SDimitry Andric /// Return the ID of a given condition.
getCondID__anonc2c3cfe60211::MCDCCoverageBuilder721cdc20ff6SDimitry Andric MCDCConditionID getCondID(const Expr *Cond) const {
722cdc20ff6SDimitry Andric auto I = CondIDs.find(CodeGenFunction::stripCond(Cond));
723cdc20ff6SDimitry Andric if (I == CondIDs.end())
724cdc20ff6SDimitry Andric return 0;
725cdc20ff6SDimitry Andric else
726cdc20ff6SDimitry Andric return I->second;
727cdc20ff6SDimitry Andric }
728cdc20ff6SDimitry Andric
729a58f00eaSDimitry Andric /// Return the LHS Decision ([0,0] if not set).
back__anonc2c3cfe60211::MCDCCoverageBuilder730a58f00eaSDimitry Andric const DecisionIDPair &back() const { return DecisionStack.back(); }
731a58f00eaSDimitry Andric
732cdc20ff6SDimitry Andric /// Push the binary operator statement to track the nest level and assign IDs
733cdc20ff6SDimitry Andric /// to the operator's LHS and RHS. The RHS may be a larger subtree that is
734cdc20ff6SDimitry Andric /// broken up on successive levels.
pushAndAssignIDs__anonc2c3cfe60211::MCDCCoverageBuilder735cdc20ff6SDimitry Andric void pushAndAssignIDs(const BinaryOperator *E) {
736cdc20ff6SDimitry Andric if (!CGM.getCodeGenOpts().MCDCCoverage)
737cdc20ff6SDimitry Andric return;
738cdc20ff6SDimitry Andric
739cdc20ff6SDimitry Andric // If binary expression is disqualified, don't do mapping.
740a58f00eaSDimitry Andric if (!isBuilding() && !MCDCBitmapMap.contains(CodeGenFunction::stripCond(E)))
741cdc20ff6SDimitry Andric NotMapped = true;
742cdc20ff6SDimitry Andric
743cdc20ff6SDimitry Andric // Don't go any further if we don't need to map condition IDs.
744cdc20ff6SDimitry Andric if (NotMapped)
745cdc20ff6SDimitry Andric return;
746cdc20ff6SDimitry Andric
747a58f00eaSDimitry Andric const DecisionIDPair &ParentDecision = DecisionStack.back();
748a58f00eaSDimitry Andric
749cdc20ff6SDimitry Andric // If the operator itself has an assigned ID, this means it represents a
750a58f00eaSDimitry Andric // larger subtree. In this case, assign that ID to its LHS node. Its RHS
751a58f00eaSDimitry Andric // will receive a new ID below. Otherwise, assign ID+1 to LHS.
752a58f00eaSDimitry Andric if (CondIDs.contains(CodeGenFunction::stripCond(E)))
753a58f00eaSDimitry Andric setCondID(E->getLHS(), getCondID(E));
754a58f00eaSDimitry Andric else
755a58f00eaSDimitry Andric setCondID(E->getLHS(), NextID++);
756cdc20ff6SDimitry Andric
757a58f00eaSDimitry Andric // Assign a ID+1 for the RHS.
758a58f00eaSDimitry Andric MCDCConditionID RHSid = NextID++;
759a58f00eaSDimitry Andric setCondID(E->getRHS(), RHSid);
760a58f00eaSDimitry Andric
761a58f00eaSDimitry Andric // Push the LHS decision IDs onto the DecisionStack.
762a58f00eaSDimitry Andric if (isLAnd(E))
763a58f00eaSDimitry Andric DecisionStack.push_back({RHSid, ParentDecision.FalseID});
764a58f00eaSDimitry Andric else
765a58f00eaSDimitry Andric DecisionStack.push_back({ParentDecision.TrueID, RHSid});
766cdc20ff6SDimitry Andric }
767cdc20ff6SDimitry Andric
768a58f00eaSDimitry Andric /// Pop and return the LHS Decision ([0,0] if not set).
pop__anonc2c3cfe60211::MCDCCoverageBuilder769a58f00eaSDimitry Andric DecisionIDPair pop() {
770a58f00eaSDimitry Andric if (!CGM.getCodeGenOpts().MCDCCoverage || NotMapped)
771a58f00eaSDimitry Andric return DecisionStack.front();
772cdc20ff6SDimitry Andric
773a58f00eaSDimitry Andric assert(DecisionStack.size() > 1);
774a58f00eaSDimitry Andric DecisionIDPair D = DecisionStack.back();
775a58f00eaSDimitry Andric DecisionStack.pop_back();
776a58f00eaSDimitry Andric return D;
777cdc20ff6SDimitry Andric }
778cdc20ff6SDimitry Andric
779a58f00eaSDimitry Andric /// Return the total number of conditions and reset the state. The number of
780a58f00eaSDimitry Andric /// conditions is zero if the expression isn't mapped.
getTotalConditionsAndReset__anonc2c3cfe60211::MCDCCoverageBuilder781a58f00eaSDimitry Andric unsigned getTotalConditionsAndReset(const BinaryOperator *E) {
782cdc20ff6SDimitry Andric if (!CGM.getCodeGenOpts().MCDCCoverage)
783cdc20ff6SDimitry Andric return 0;
784cdc20ff6SDimitry Andric
785a58f00eaSDimitry Andric assert(!isIdle());
786a58f00eaSDimitry Andric assert(DecisionStack.size() == 1);
787cdc20ff6SDimitry Andric
788cdc20ff6SDimitry Andric // Reset state if not doing mapping.
789a58f00eaSDimitry Andric if (NotMapped) {
790cdc20ff6SDimitry Andric NotMapped = false;
791a58f00eaSDimitry Andric assert(NextID == 1);
792cdc20ff6SDimitry Andric return 0;
793cdc20ff6SDimitry Andric }
794cdc20ff6SDimitry Andric
795a58f00eaSDimitry Andric // Set number of conditions and reset.
796a58f00eaSDimitry Andric unsigned TotalConds = NextID - 1;
797cdc20ff6SDimitry Andric
798cdc20ff6SDimitry Andric // Reset ID back to beginning.
799cdc20ff6SDimitry Andric NextID = 1;
800a58f00eaSDimitry Andric
801cdc20ff6SDimitry Andric return TotalConds;
802cdc20ff6SDimitry Andric }
803cdc20ff6SDimitry Andric };
804cdc20ff6SDimitry Andric
8050b57cec5SDimitry Andric /// A StmtVisitor that creates coverage mapping regions which map
8060b57cec5SDimitry Andric /// from the source code locations to the PGO counters.
8070b57cec5SDimitry Andric struct CounterCoverageMappingBuilder
8080b57cec5SDimitry Andric : public CoverageMappingBuilder,
8090b57cec5SDimitry Andric public ConstStmtVisitor<CounterCoverageMappingBuilder> {
8100b57cec5SDimitry Andric /// The map of statements to count values.
8110b57cec5SDimitry Andric llvm::DenseMap<const Stmt *, unsigned> &CounterMap;
8120b57cec5SDimitry Andric
813cdc20ff6SDimitry Andric /// The map of statements to bitmap coverage object values.
814cdc20ff6SDimitry Andric llvm::DenseMap<const Stmt *, unsigned> &MCDCBitmapMap;
815cdc20ff6SDimitry Andric
8160b57cec5SDimitry Andric /// A stack of currently live regions.
817cdc20ff6SDimitry Andric llvm::SmallVector<SourceMappingRegion> RegionStack;
818cdc20ff6SDimitry Andric
819cdc20ff6SDimitry Andric /// An object to manage MCDC regions.
820cdc20ff6SDimitry Andric MCDCCoverageBuilder MCDCBuilder;
8210b57cec5SDimitry Andric
8220b57cec5SDimitry Andric CounterExpressionBuilder Builder;
8230b57cec5SDimitry Andric
8240b57cec5SDimitry Andric /// A location in the most recently visited file or macro.
8250b57cec5SDimitry Andric ///
8260b57cec5SDimitry Andric /// This is used to adjust the active source regions appropriately when
8270b57cec5SDimitry Andric /// expressions cross file or macro boundaries.
8280b57cec5SDimitry Andric SourceLocation MostRecentLocation;
8290b57cec5SDimitry Andric
830fe6060f1SDimitry Andric /// Whether the visitor at a terminate statement.
831fe6060f1SDimitry Andric bool HasTerminateStmt = false;
832fe6060f1SDimitry Andric
833fe6060f1SDimitry Andric /// Gap region counter after terminate statement.
834fe6060f1SDimitry Andric Counter GapRegionCounter;
8350b57cec5SDimitry Andric
8360b57cec5SDimitry Andric /// Return a counter for the subtraction of \c RHS from \c LHS
subtractCounters__anonc2c3cfe60211::CounterCoverageMappingBuilder83781ad6265SDimitry Andric Counter subtractCounters(Counter LHS, Counter RHS, bool Simplify = true) {
83881ad6265SDimitry Andric return Builder.subtract(LHS, RHS, Simplify);
8390b57cec5SDimitry Andric }
8400b57cec5SDimitry Andric
8410b57cec5SDimitry Andric /// Return a counter for the sum of \c LHS and \c RHS.
addCounters__anonc2c3cfe60211::CounterCoverageMappingBuilder84281ad6265SDimitry Andric Counter addCounters(Counter LHS, Counter RHS, bool Simplify = true) {
84381ad6265SDimitry Andric return Builder.add(LHS, RHS, Simplify);
8440b57cec5SDimitry Andric }
8450b57cec5SDimitry Andric
addCounters__anonc2c3cfe60211::CounterCoverageMappingBuilder84681ad6265SDimitry Andric Counter addCounters(Counter C1, Counter C2, Counter C3,
84781ad6265SDimitry Andric bool Simplify = true) {
84881ad6265SDimitry Andric return addCounters(addCounters(C1, C2, Simplify), C3, Simplify);
8490b57cec5SDimitry Andric }
8500b57cec5SDimitry Andric
8510b57cec5SDimitry Andric /// Return the region counter for the given statement.
8520b57cec5SDimitry Andric ///
8530b57cec5SDimitry Andric /// This should only be called on statements that have a dedicated counter.
getRegionCounter__anonc2c3cfe60211::CounterCoverageMappingBuilder8540b57cec5SDimitry Andric Counter getRegionCounter(const Stmt *S) {
8550b57cec5SDimitry Andric return Counter::getCounter(CounterMap[S]);
8560b57cec5SDimitry Andric }
8570b57cec5SDimitry Andric
getRegionBitmap__anonc2c3cfe60211::CounterCoverageMappingBuilder858cdc20ff6SDimitry Andric unsigned getRegionBitmap(const Stmt *S) { return MCDCBitmapMap[S]; }
859cdc20ff6SDimitry Andric
8600b57cec5SDimitry Andric /// Push a region onto the stack.
8610b57cec5SDimitry Andric ///
8620b57cec5SDimitry Andric /// Returns the index on the stack where the region was pushed. This can be
8630b57cec5SDimitry Andric /// used with popRegions to exit a "scope", ending the region that was pushed.
pushRegion__anonc2c3cfe60211::CounterCoverageMappingBuilder864bdd1243dSDimitry Andric size_t pushRegion(Counter Count,
865bdd1243dSDimitry Andric std::optional<SourceLocation> StartLoc = std::nullopt,
866bdd1243dSDimitry Andric std::optional<SourceLocation> EndLoc = std::nullopt,
867cdc20ff6SDimitry Andric std::optional<Counter> FalseCount = std::nullopt,
868cdc20ff6SDimitry Andric MCDCConditionID ID = 0, MCDCConditionID TrueID = 0,
869cdc20ff6SDimitry Andric MCDCConditionID FalseID = 0) {
870e8d8bef9SDimitry Andric
87181ad6265SDimitry Andric if (StartLoc && !FalseCount) {
8720b57cec5SDimitry Andric MostRecentLocation = *StartLoc;
8730b57cec5SDimitry Andric }
874e8d8bef9SDimitry Andric
875fe013be4SDimitry Andric // If either of these locations is invalid, something elsewhere in the
876fe013be4SDimitry Andric // compiler has broken.
877fe013be4SDimitry Andric assert((!StartLoc || StartLoc->isValid()) && "Start location is not valid");
878fe013be4SDimitry Andric assert((!EndLoc || EndLoc->isValid()) && "End location is not valid");
879fe013be4SDimitry Andric
880fe013be4SDimitry Andric // However, we can still recover without crashing.
881fe013be4SDimitry Andric // If either location is invalid, set it to std::nullopt to avoid
882fe013be4SDimitry Andric // letting users of RegionStack think that region has a valid start/end
883fe013be4SDimitry Andric // location.
884fe013be4SDimitry Andric if (StartLoc && StartLoc->isInvalid())
885fe013be4SDimitry Andric StartLoc = std::nullopt;
886fe013be4SDimitry Andric if (EndLoc && EndLoc->isInvalid())
887fe013be4SDimitry Andric EndLoc = std::nullopt;
888cdc20ff6SDimitry Andric RegionStack.emplace_back(Count, FalseCount,
889cdc20ff6SDimitry Andric MCDCParameters{0, 0, ID, TrueID, FalseID},
890cdc20ff6SDimitry Andric StartLoc, EndLoc);
891cdc20ff6SDimitry Andric
892cdc20ff6SDimitry Andric return RegionStack.size() - 1;
893cdc20ff6SDimitry Andric }
894cdc20ff6SDimitry Andric
pushRegion__anonc2c3cfe60211::CounterCoverageMappingBuilder895cdc20ff6SDimitry Andric size_t pushRegion(unsigned BitmapIdx, unsigned Conditions,
896cdc20ff6SDimitry Andric std::optional<SourceLocation> StartLoc = std::nullopt,
897cdc20ff6SDimitry Andric std::optional<SourceLocation> EndLoc = std::nullopt) {
898cdc20ff6SDimitry Andric
899cdc20ff6SDimitry Andric RegionStack.emplace_back(MCDCParameters{BitmapIdx, Conditions}, StartLoc,
900cdc20ff6SDimitry Andric EndLoc);
9010b57cec5SDimitry Andric
9020b57cec5SDimitry Andric return RegionStack.size() - 1;
9030b57cec5SDimitry Andric }
9040b57cec5SDimitry Andric
locationDepth__anonc2c3cfe60211::CounterCoverageMappingBuilder9050b57cec5SDimitry Andric size_t locationDepth(SourceLocation Loc) {
9060b57cec5SDimitry Andric size_t Depth = 0;
9070b57cec5SDimitry Andric while (Loc.isValid()) {
9080b57cec5SDimitry Andric Loc = getIncludeOrExpansionLoc(Loc);
9090b57cec5SDimitry Andric Depth++;
9100b57cec5SDimitry Andric }
9110b57cec5SDimitry Andric return Depth;
9120b57cec5SDimitry Andric }
9130b57cec5SDimitry Andric
9140b57cec5SDimitry Andric /// Pop regions from the stack into the function's list of regions.
9150b57cec5SDimitry Andric ///
9160b57cec5SDimitry Andric /// Adds all regions from \c ParentIndex to the top of the stack to the
9170b57cec5SDimitry Andric /// function's \c SourceRegions.
popRegions__anonc2c3cfe60211::CounterCoverageMappingBuilder9180b57cec5SDimitry Andric void popRegions(size_t ParentIndex) {
9190b57cec5SDimitry Andric assert(RegionStack.size() >= ParentIndex && "parent not in stack");
9200b57cec5SDimitry Andric while (RegionStack.size() > ParentIndex) {
9210b57cec5SDimitry Andric SourceMappingRegion &Region = RegionStack.back();
922fe013be4SDimitry Andric if (Region.hasStartLoc() &&
923fe013be4SDimitry Andric (Region.hasEndLoc() || RegionStack[ParentIndex].hasEndLoc())) {
9240b57cec5SDimitry Andric SourceLocation StartLoc = Region.getBeginLoc();
9250b57cec5SDimitry Andric SourceLocation EndLoc = Region.hasEndLoc()
9260b57cec5SDimitry Andric ? Region.getEndLoc()
9270b57cec5SDimitry Andric : RegionStack[ParentIndex].getEndLoc();
928e8d8bef9SDimitry Andric bool isBranch = Region.isBranch();
9290b57cec5SDimitry Andric size_t StartDepth = locationDepth(StartLoc);
9300b57cec5SDimitry Andric size_t EndDepth = locationDepth(EndLoc);
9310b57cec5SDimitry Andric while (!SM.isWrittenInSameFile(StartLoc, EndLoc)) {
9320b57cec5SDimitry Andric bool UnnestStart = StartDepth >= EndDepth;
9330b57cec5SDimitry Andric bool UnnestEnd = EndDepth >= StartDepth;
9340b57cec5SDimitry Andric if (UnnestEnd) {
935e8d8bef9SDimitry Andric // The region ends in a nested file or macro expansion. If the
936e8d8bef9SDimitry Andric // region is not a branch region, create a separate region for each
937e8d8bef9SDimitry Andric // expansion, and for all regions, update the EndLoc. Branch
938e8d8bef9SDimitry Andric // regions should not be split in order to keep a straightforward
939e8d8bef9SDimitry Andric // correspondance between the region and its associated branch
940e8d8bef9SDimitry Andric // condition, even if the condition spans multiple depths.
9410b57cec5SDimitry Andric SourceLocation NestedLoc = getStartOfFileOrMacro(EndLoc);
9420b57cec5SDimitry Andric assert(SM.isWrittenInSameFile(NestedLoc, EndLoc));
9430b57cec5SDimitry Andric
944e8d8bef9SDimitry Andric if (!isBranch && !isRegionAlreadyAdded(NestedLoc, EndLoc))
945e8d8bef9SDimitry Andric SourceRegions.emplace_back(Region.getCounter(), NestedLoc,
946e8d8bef9SDimitry Andric EndLoc);
9470b57cec5SDimitry Andric
9480b57cec5SDimitry Andric EndLoc = getPreciseTokenLocEnd(getIncludeOrExpansionLoc(EndLoc));
9490b57cec5SDimitry Andric if (EndLoc.isInvalid())
950e8d8bef9SDimitry Andric llvm::report_fatal_error(
951e8d8bef9SDimitry Andric "File exit not handled before popRegions");
9520b57cec5SDimitry Andric EndDepth--;
9530b57cec5SDimitry Andric }
9540b57cec5SDimitry Andric if (UnnestStart) {
955e8d8bef9SDimitry Andric // The region ends in a nested file or macro expansion. If the
956e8d8bef9SDimitry Andric // region is not a branch region, create a separate region for each
957e8d8bef9SDimitry Andric // expansion, and for all regions, update the StartLoc. Branch
958e8d8bef9SDimitry Andric // regions should not be split in order to keep a straightforward
959e8d8bef9SDimitry Andric // correspondance between the region and its associated branch
960e8d8bef9SDimitry Andric // condition, even if the condition spans multiple depths.
9610b57cec5SDimitry Andric SourceLocation NestedLoc = getEndOfFileOrMacro(StartLoc);
9620b57cec5SDimitry Andric assert(SM.isWrittenInSameFile(StartLoc, NestedLoc));
9630b57cec5SDimitry Andric
964e8d8bef9SDimitry Andric if (!isBranch && !isRegionAlreadyAdded(StartLoc, NestedLoc))
965e8d8bef9SDimitry Andric SourceRegions.emplace_back(Region.getCounter(), StartLoc,
966e8d8bef9SDimitry Andric NestedLoc);
9670b57cec5SDimitry Andric
9680b57cec5SDimitry Andric StartLoc = getIncludeOrExpansionLoc(StartLoc);
9690b57cec5SDimitry Andric if (StartLoc.isInvalid())
970e8d8bef9SDimitry Andric llvm::report_fatal_error(
971e8d8bef9SDimitry Andric "File exit not handled before popRegions");
9720b57cec5SDimitry Andric StartDepth--;
9730b57cec5SDimitry Andric }
9740b57cec5SDimitry Andric }
9750b57cec5SDimitry Andric Region.setStartLoc(StartLoc);
9760b57cec5SDimitry Andric Region.setEndLoc(EndLoc);
9770b57cec5SDimitry Andric
978e8d8bef9SDimitry Andric if (!isBranch) {
9790b57cec5SDimitry Andric MostRecentLocation = EndLoc;
980e8d8bef9SDimitry Andric // If this region happens to span an entire expansion, we need to
981e8d8bef9SDimitry Andric // make sure we don't overlap the parent region with it.
9820b57cec5SDimitry Andric if (StartLoc == getStartOfFileOrMacro(StartLoc) &&
9830b57cec5SDimitry Andric EndLoc == getEndOfFileOrMacro(EndLoc))
9840b57cec5SDimitry Andric MostRecentLocation = getIncludeOrExpansionLoc(EndLoc);
985e8d8bef9SDimitry Andric }
9860b57cec5SDimitry Andric
9870b57cec5SDimitry Andric assert(SM.isWrittenInSameFile(Region.getBeginLoc(), EndLoc));
9880b57cec5SDimitry Andric assert(SpellingRegion(SM, Region).isInSourceOrder());
9890b57cec5SDimitry Andric SourceRegions.push_back(Region);
9900b57cec5SDimitry Andric }
9910b57cec5SDimitry Andric RegionStack.pop_back();
9920b57cec5SDimitry Andric }
9930b57cec5SDimitry Andric }
9940b57cec5SDimitry Andric
9950b57cec5SDimitry Andric /// Return the currently active region.
getRegion__anonc2c3cfe60211::CounterCoverageMappingBuilder9960b57cec5SDimitry Andric SourceMappingRegion &getRegion() {
9970b57cec5SDimitry Andric assert(!RegionStack.empty() && "statement has no region");
9980b57cec5SDimitry Andric return RegionStack.back();
9990b57cec5SDimitry Andric }
10000b57cec5SDimitry Andric
10010b57cec5SDimitry Andric /// Propagate counts through the children of \p S if \p VisitChildren is true.
10020b57cec5SDimitry Andric /// Otherwise, only emit a count for \p S itself.
propagateCounts__anonc2c3cfe60211::CounterCoverageMappingBuilder10030b57cec5SDimitry Andric Counter propagateCounts(Counter TopCount, const Stmt *S,
10040b57cec5SDimitry Andric bool VisitChildren = true) {
10050b57cec5SDimitry Andric SourceLocation StartLoc = getStart(S);
10060b57cec5SDimitry Andric SourceLocation EndLoc = getEnd(S);
10070b57cec5SDimitry Andric size_t Index = pushRegion(TopCount, StartLoc, EndLoc);
10080b57cec5SDimitry Andric if (VisitChildren)
10090b57cec5SDimitry Andric Visit(S);
10100b57cec5SDimitry Andric Counter ExitCount = getRegion().getCounter();
10110b57cec5SDimitry Andric popRegions(Index);
10120b57cec5SDimitry Andric
10130b57cec5SDimitry Andric // The statement may be spanned by an expansion. Make sure we handle a file
10140b57cec5SDimitry Andric // exit out of this expansion before moving to the next statement.
10150b57cec5SDimitry Andric if (SM.isBeforeInTranslationUnit(StartLoc, S->getBeginLoc()))
10160b57cec5SDimitry Andric MostRecentLocation = EndLoc;
10170b57cec5SDimitry Andric
10180b57cec5SDimitry Andric return ExitCount;
10190b57cec5SDimitry Andric }
10200b57cec5SDimitry Andric
1021e8d8bef9SDimitry Andric /// Determine whether the given condition can be constant folded.
ConditionFoldsToBool__anonc2c3cfe60211::CounterCoverageMappingBuilder1022e8d8bef9SDimitry Andric bool ConditionFoldsToBool(const Expr *Cond) {
1023e8d8bef9SDimitry Andric Expr::EvalResult Result;
1024e8d8bef9SDimitry Andric return (Cond->EvaluateAsInt(Result, CVM.getCodeGenModule().getContext()));
1025e8d8bef9SDimitry Andric }
1026e8d8bef9SDimitry Andric
1027a58f00eaSDimitry Andric using MCDCDecisionIDPair = MCDCCoverageBuilder::DecisionIDPair;
1028a58f00eaSDimitry Andric
1029e8d8bef9SDimitry Andric /// Create a Branch Region around an instrumentable condition for coverage
1030e8d8bef9SDimitry Andric /// and add it to the function's SourceRegions. A branch region tracks a
1031e8d8bef9SDimitry Andric /// "True" counter and a "False" counter for boolean expressions that
1032e8d8bef9SDimitry Andric /// result in the generation of a branch.
1033a58f00eaSDimitry Andric void
createBranchRegion__anonc2c3cfe60211::CounterCoverageMappingBuilder1034a58f00eaSDimitry Andric createBranchRegion(const Expr *C, Counter TrueCnt, Counter FalseCnt,
1035a58f00eaSDimitry Andric const MCDCDecisionIDPair &IDPair = MCDCDecisionIDPair()) {
1036e8d8bef9SDimitry Andric // Check for NULL conditions.
1037e8d8bef9SDimitry Andric if (!C)
1038e8d8bef9SDimitry Andric return;
1039e8d8bef9SDimitry Andric
1040e8d8bef9SDimitry Andric // Ensure we are an instrumentable condition (i.e. no "&&" or "||"). Push
1041e8d8bef9SDimitry Andric // region onto RegionStack but immediately pop it (which adds it to the
1042e8d8bef9SDimitry Andric // function's SourceRegions) because it doesn't apply to any other source
1043e8d8bef9SDimitry Andric // code other than the Condition.
1044e8d8bef9SDimitry Andric if (CodeGenFunction::isInstrumentedCondition(C)) {
1045a58f00eaSDimitry Andric MCDCConditionID ID = MCDCBuilder.getCondID(C);
1046a58f00eaSDimitry Andric MCDCConditionID TrueID = IDPair.TrueID;
1047a58f00eaSDimitry Andric MCDCConditionID FalseID = IDPair.FalseID;
1048a58f00eaSDimitry Andric
1049e8d8bef9SDimitry Andric // If a condition can fold to true or false, the corresponding branch
1050e8d8bef9SDimitry Andric // will be removed. Create a region with both counters hard-coded to
1051e8d8bef9SDimitry Andric // zero. This allows us to visualize them in a special way.
1052e8d8bef9SDimitry Andric // Alternatively, we can prevent any optimization done via
1053e8d8bef9SDimitry Andric // constant-folding by ensuring that ConstantFoldsToSimpleInteger() in
1054e8d8bef9SDimitry Andric // CodeGenFunction.c always returns false, but that is very heavy-handed.
1055e8d8bef9SDimitry Andric if (ConditionFoldsToBool(C))
1056e8d8bef9SDimitry Andric popRegions(pushRegion(Counter::getZero(), getStart(C), getEnd(C),
1057cdc20ff6SDimitry Andric Counter::getZero(), ID, TrueID, FalseID));
1058e8d8bef9SDimitry Andric else
1059e8d8bef9SDimitry Andric // Otherwise, create a region with the True counter and False counter.
1060cdc20ff6SDimitry Andric popRegions(pushRegion(TrueCnt, getStart(C), getEnd(C), FalseCnt, ID,
1061cdc20ff6SDimitry Andric TrueID, FalseID));
1062e8d8bef9SDimitry Andric }
1063e8d8bef9SDimitry Andric }
1064e8d8bef9SDimitry Andric
1065cdc20ff6SDimitry Andric /// Create a Decision Region with a BitmapIdx and number of Conditions. This
1066cdc20ff6SDimitry Andric /// type of region "contains" branch regions, one for each of the conditions.
1067cdc20ff6SDimitry Andric /// The visualization tool will group everything together.
createDecisionRegion__anonc2c3cfe60211::CounterCoverageMappingBuilder1068cdc20ff6SDimitry Andric void createDecisionRegion(const Expr *C, unsigned BitmapIdx, unsigned Conds) {
1069cdc20ff6SDimitry Andric popRegions(pushRegion(BitmapIdx, Conds, getStart(C), getEnd(C)));
1070cdc20ff6SDimitry Andric }
1071cdc20ff6SDimitry Andric
1072e8d8bef9SDimitry Andric /// Create a Branch Region around a SwitchCase for code coverage
1073e8d8bef9SDimitry Andric /// and add it to the function's SourceRegions.
createSwitchCaseRegion__anonc2c3cfe60211::CounterCoverageMappingBuilder1074e8d8bef9SDimitry Andric void createSwitchCaseRegion(const SwitchCase *SC, Counter TrueCnt,
1075e8d8bef9SDimitry Andric Counter FalseCnt) {
1076e8d8bef9SDimitry Andric // Push region onto RegionStack but immediately pop it (which adds it to
1077e8d8bef9SDimitry Andric // the function's SourceRegions) because it doesn't apply to any other
1078e8d8bef9SDimitry Andric // source other than the SwitchCase.
1079e8d8bef9SDimitry Andric popRegions(pushRegion(TrueCnt, getStart(SC), SC->getColonLoc(), FalseCnt));
1080e8d8bef9SDimitry Andric }
1081e8d8bef9SDimitry Andric
10820b57cec5SDimitry Andric /// Check whether a region with bounds \c StartLoc and \c EndLoc
10830b57cec5SDimitry Andric /// is already added to \c SourceRegions.
isRegionAlreadyAdded__anonc2c3cfe60211::CounterCoverageMappingBuilder1084e8d8bef9SDimitry Andric bool isRegionAlreadyAdded(SourceLocation StartLoc, SourceLocation EndLoc,
1085e8d8bef9SDimitry Andric bool isBranch = false) {
1086349cc55cSDimitry Andric return llvm::any_of(
1087349cc55cSDimitry Andric llvm::reverse(SourceRegions), [&](const SourceMappingRegion &Region) {
10880b57cec5SDimitry Andric return Region.getBeginLoc() == StartLoc &&
1089349cc55cSDimitry Andric Region.getEndLoc() == EndLoc && Region.isBranch() == isBranch;
10900b57cec5SDimitry Andric });
10910b57cec5SDimitry Andric }
10920b57cec5SDimitry Andric
10930b57cec5SDimitry Andric /// Adjust the most recently visited location to \c EndLoc.
10940b57cec5SDimitry Andric ///
10950b57cec5SDimitry Andric /// This should be used after visiting any statements in non-source order.
adjustForOutOfOrderTraversal__anonc2c3cfe60211::CounterCoverageMappingBuilder10960b57cec5SDimitry Andric void adjustForOutOfOrderTraversal(SourceLocation EndLoc) {
10970b57cec5SDimitry Andric MostRecentLocation = EndLoc;
10980b57cec5SDimitry Andric // The code region for a whole macro is created in handleFileExit() when
10990b57cec5SDimitry Andric // it detects exiting of the virtual file of that macro. If we visited
11000b57cec5SDimitry Andric // statements in non-source order, we might already have such a region
11010b57cec5SDimitry Andric // added, for example, if a body of a loop is divided among multiple
11020b57cec5SDimitry Andric // macros. Avoid adding duplicate regions in such case.
11030b57cec5SDimitry Andric if (getRegion().hasEndLoc() &&
11040b57cec5SDimitry Andric MostRecentLocation == getEndOfFileOrMacro(MostRecentLocation) &&
11050b57cec5SDimitry Andric isRegionAlreadyAdded(getStartOfFileOrMacro(MostRecentLocation),
1106e8d8bef9SDimitry Andric MostRecentLocation, getRegion().isBranch()))
11070b57cec5SDimitry Andric MostRecentLocation = getIncludeOrExpansionLoc(MostRecentLocation);
11080b57cec5SDimitry Andric }
11090b57cec5SDimitry Andric
11100b57cec5SDimitry Andric /// Adjust regions and state when \c NewLoc exits a file.
11110b57cec5SDimitry Andric ///
11120b57cec5SDimitry Andric /// If moving from our most recently tracked location to \c NewLoc exits any
11130b57cec5SDimitry Andric /// files, this adjusts our current region stack and creates the file regions
11140b57cec5SDimitry Andric /// for the exited file.
handleFileExit__anonc2c3cfe60211::CounterCoverageMappingBuilder11150b57cec5SDimitry Andric void handleFileExit(SourceLocation NewLoc) {
11160b57cec5SDimitry Andric if (NewLoc.isInvalid() ||
11170b57cec5SDimitry Andric SM.isWrittenInSameFile(MostRecentLocation, NewLoc))
11180b57cec5SDimitry Andric return;
11190b57cec5SDimitry Andric
11200b57cec5SDimitry Andric // If NewLoc is not in a file that contains MostRecentLocation, walk up to
11210b57cec5SDimitry Andric // find the common ancestor.
11220b57cec5SDimitry Andric SourceLocation LCA = NewLoc;
11230b57cec5SDimitry Andric FileID ParentFile = SM.getFileID(LCA);
11240b57cec5SDimitry Andric while (!isNestedIn(MostRecentLocation, ParentFile)) {
11250b57cec5SDimitry Andric LCA = getIncludeOrExpansionLoc(LCA);
11260b57cec5SDimitry Andric if (LCA.isInvalid() || SM.isWrittenInSameFile(LCA, MostRecentLocation)) {
11270b57cec5SDimitry Andric // Since there isn't a common ancestor, no file was exited. We just need
11280b57cec5SDimitry Andric // to adjust our location to the new file.
11290b57cec5SDimitry Andric MostRecentLocation = NewLoc;
11300b57cec5SDimitry Andric return;
11310b57cec5SDimitry Andric }
11320b57cec5SDimitry Andric ParentFile = SM.getFileID(LCA);
11330b57cec5SDimitry Andric }
11340b57cec5SDimitry Andric
11350b57cec5SDimitry Andric llvm::SmallSet<SourceLocation, 8> StartLocs;
1136bdd1243dSDimitry Andric std::optional<Counter> ParentCounter;
11370b57cec5SDimitry Andric for (SourceMappingRegion &I : llvm::reverse(RegionStack)) {
11380b57cec5SDimitry Andric if (!I.hasStartLoc())
11390b57cec5SDimitry Andric continue;
11400b57cec5SDimitry Andric SourceLocation Loc = I.getBeginLoc();
11410b57cec5SDimitry Andric if (!isNestedIn(Loc, ParentFile)) {
11420b57cec5SDimitry Andric ParentCounter = I.getCounter();
11430b57cec5SDimitry Andric break;
11440b57cec5SDimitry Andric }
11450b57cec5SDimitry Andric
11460b57cec5SDimitry Andric while (!SM.isInFileID(Loc, ParentFile)) {
11470b57cec5SDimitry Andric // The most nested region for each start location is the one with the
11480b57cec5SDimitry Andric // correct count. We avoid creating redundant regions by stopping once
11490b57cec5SDimitry Andric // we've seen this region.
1150e8d8bef9SDimitry Andric if (StartLocs.insert(Loc).second) {
1151e8d8bef9SDimitry Andric if (I.isBranch())
1152cdc20ff6SDimitry Andric SourceRegions.emplace_back(
1153cdc20ff6SDimitry Andric I.getCounter(), I.getFalseCounter(),
1154cdc20ff6SDimitry Andric MCDCParameters{0, 0, I.getMCDCParams().ID,
1155cdc20ff6SDimitry Andric I.getMCDCParams().TrueID,
1156cdc20ff6SDimitry Andric I.getMCDCParams().FalseID},
1157cdc20ff6SDimitry Andric Loc, getEndOfFileOrMacro(Loc), I.isBranch());
1158e8d8bef9SDimitry Andric else
11590b57cec5SDimitry Andric SourceRegions.emplace_back(I.getCounter(), Loc,
11600b57cec5SDimitry Andric getEndOfFileOrMacro(Loc));
1161e8d8bef9SDimitry Andric }
11620b57cec5SDimitry Andric Loc = getIncludeOrExpansionLoc(Loc);
11630b57cec5SDimitry Andric }
11640b57cec5SDimitry Andric I.setStartLoc(getPreciseTokenLocEnd(Loc));
11650b57cec5SDimitry Andric }
11660b57cec5SDimitry Andric
11670b57cec5SDimitry Andric if (ParentCounter) {
11680b57cec5SDimitry Andric // If the file is contained completely by another region and doesn't
11690b57cec5SDimitry Andric // immediately start its own region, the whole file gets a region
11700b57cec5SDimitry Andric // corresponding to the parent.
11710b57cec5SDimitry Andric SourceLocation Loc = MostRecentLocation;
11720b57cec5SDimitry Andric while (isNestedIn(Loc, ParentFile)) {
11730b57cec5SDimitry Andric SourceLocation FileStart = getStartOfFileOrMacro(Loc);
11740b57cec5SDimitry Andric if (StartLocs.insert(FileStart).second) {
11750b57cec5SDimitry Andric SourceRegions.emplace_back(*ParentCounter, FileStart,
11760b57cec5SDimitry Andric getEndOfFileOrMacro(Loc));
11770b57cec5SDimitry Andric assert(SpellingRegion(SM, SourceRegions.back()).isInSourceOrder());
11780b57cec5SDimitry Andric }
11790b57cec5SDimitry Andric Loc = getIncludeOrExpansionLoc(Loc);
11800b57cec5SDimitry Andric }
11810b57cec5SDimitry Andric }
11820b57cec5SDimitry Andric
11830b57cec5SDimitry Andric MostRecentLocation = NewLoc;
11840b57cec5SDimitry Andric }
11850b57cec5SDimitry Andric
11860b57cec5SDimitry Andric /// Ensure that \c S is included in the current region.
extendRegion__anonc2c3cfe60211::CounterCoverageMappingBuilder11870b57cec5SDimitry Andric void extendRegion(const Stmt *S) {
11880b57cec5SDimitry Andric SourceMappingRegion &Region = getRegion();
11890b57cec5SDimitry Andric SourceLocation StartLoc = getStart(S);
11900b57cec5SDimitry Andric
11910b57cec5SDimitry Andric handleFileExit(StartLoc);
11920b57cec5SDimitry Andric if (!Region.hasStartLoc())
11930b57cec5SDimitry Andric Region.setStartLoc(StartLoc);
11940b57cec5SDimitry Andric }
11950b57cec5SDimitry Andric
11960b57cec5SDimitry Andric /// Mark \c S as a terminator, starting a zero region.
terminateRegion__anonc2c3cfe60211::CounterCoverageMappingBuilder11970b57cec5SDimitry Andric void terminateRegion(const Stmt *S) {
11980b57cec5SDimitry Andric extendRegion(S);
11990b57cec5SDimitry Andric SourceMappingRegion &Region = getRegion();
12000b57cec5SDimitry Andric SourceLocation EndLoc = getEnd(S);
12010b57cec5SDimitry Andric if (!Region.hasEndLoc())
12020b57cec5SDimitry Andric Region.setEndLoc(EndLoc);
12030b57cec5SDimitry Andric pushRegion(Counter::getZero());
1204fe6060f1SDimitry Andric HasTerminateStmt = true;
12050b57cec5SDimitry Andric }
12060b57cec5SDimitry Andric
12070b57cec5SDimitry Andric /// Find a valid gap range between \p AfterLoc and \p BeforeLoc.
findGapAreaBetween__anonc2c3cfe60211::CounterCoverageMappingBuilder1208bdd1243dSDimitry Andric std::optional<SourceRange> findGapAreaBetween(SourceLocation AfterLoc,
12090b57cec5SDimitry Andric SourceLocation BeforeLoc) {
1210*fe72d8ecSDimitry Andric // Some statements (like AttributedStmt and ImplicitValueInitExpr) don't
1211*fe72d8ecSDimitry Andric // have valid source locations. Do not emit a gap region if this is the case
1212*fe72d8ecSDimitry Andric // in either AfterLoc end or BeforeLoc end.
1213*fe72d8ecSDimitry Andric if (AfterLoc.isInvalid() || BeforeLoc.isInvalid())
1214*fe72d8ecSDimitry Andric return std::nullopt;
1215*fe72d8ecSDimitry Andric
1216fe6060f1SDimitry Andric // If AfterLoc is in function-like macro, use the right parenthesis
1217fe6060f1SDimitry Andric // location.
1218fe6060f1SDimitry Andric if (AfterLoc.isMacroID()) {
1219fe6060f1SDimitry Andric FileID FID = SM.getFileID(AfterLoc);
1220fe6060f1SDimitry Andric const SrcMgr::ExpansionInfo *EI = &SM.getSLocEntry(FID).getExpansion();
1221fe6060f1SDimitry Andric if (EI->isFunctionMacroExpansion())
1222fe6060f1SDimitry Andric AfterLoc = EI->getExpansionLocEnd();
1223fe6060f1SDimitry Andric }
1224fe6060f1SDimitry Andric
1225fe6060f1SDimitry Andric size_t StartDepth = locationDepth(AfterLoc);
1226fe6060f1SDimitry Andric size_t EndDepth = locationDepth(BeforeLoc);
1227fe6060f1SDimitry Andric while (!SM.isWrittenInSameFile(AfterLoc, BeforeLoc)) {
1228fe6060f1SDimitry Andric bool UnnestStart = StartDepth >= EndDepth;
1229fe6060f1SDimitry Andric bool UnnestEnd = EndDepth >= StartDepth;
1230fe6060f1SDimitry Andric if (UnnestEnd) {
1231fe6060f1SDimitry Andric assert(SM.isWrittenInSameFile(getStartOfFileOrMacro(BeforeLoc),
1232fe6060f1SDimitry Andric BeforeLoc));
1233fe6060f1SDimitry Andric
1234fe6060f1SDimitry Andric BeforeLoc = getIncludeOrExpansionLoc(BeforeLoc);
1235fe6060f1SDimitry Andric assert(BeforeLoc.isValid());
1236fe6060f1SDimitry Andric EndDepth--;
1237fe6060f1SDimitry Andric }
1238fe6060f1SDimitry Andric if (UnnestStart) {
1239fe6060f1SDimitry Andric assert(SM.isWrittenInSameFile(AfterLoc,
1240fe6060f1SDimitry Andric getEndOfFileOrMacro(AfterLoc)));
1241fe6060f1SDimitry Andric
1242fe6060f1SDimitry Andric AfterLoc = getIncludeOrExpansionLoc(AfterLoc);
1243fe6060f1SDimitry Andric assert(AfterLoc.isValid());
1244fe6060f1SDimitry Andric AfterLoc = getPreciseTokenLocEnd(AfterLoc);
1245fe6060f1SDimitry Andric assert(AfterLoc.isValid());
1246fe6060f1SDimitry Andric StartDepth--;
1247fe6060f1SDimitry Andric }
1248fe6060f1SDimitry Andric }
1249fe6060f1SDimitry Andric AfterLoc = getPreciseTokenLocEnd(AfterLoc);
12500b57cec5SDimitry Andric // If the start and end locations of the gap are both within the same macro
12510b57cec5SDimitry Andric // file, the range may not be in source order.
12520b57cec5SDimitry Andric if (AfterLoc.isMacroID() || BeforeLoc.isMacroID())
1253bdd1243dSDimitry Andric return std::nullopt;
1254fe6060f1SDimitry Andric if (!SM.isWrittenInSameFile(AfterLoc, BeforeLoc) ||
1255fe6060f1SDimitry Andric !SpellingRegion(SM, AfterLoc, BeforeLoc).isInSourceOrder())
1256bdd1243dSDimitry Andric return std::nullopt;
12570b57cec5SDimitry Andric return {{AfterLoc, BeforeLoc}};
12580b57cec5SDimitry Andric }
12590b57cec5SDimitry Andric
12600b57cec5SDimitry Andric /// Emit a gap region between \p StartLoc and \p EndLoc with the given count.
fillGapAreaWithCount__anonc2c3cfe60211::CounterCoverageMappingBuilder12610b57cec5SDimitry Andric void fillGapAreaWithCount(SourceLocation StartLoc, SourceLocation EndLoc,
12620b57cec5SDimitry Andric Counter Count) {
12630b57cec5SDimitry Andric if (StartLoc == EndLoc)
12640b57cec5SDimitry Andric return;
12650b57cec5SDimitry Andric assert(SpellingRegion(SM, StartLoc, EndLoc).isInSourceOrder());
12660b57cec5SDimitry Andric handleFileExit(StartLoc);
12670b57cec5SDimitry Andric size_t Index = pushRegion(Count, StartLoc, EndLoc);
12680b57cec5SDimitry Andric getRegion().setGap(true);
12690b57cec5SDimitry Andric handleFileExit(EndLoc);
12700b57cec5SDimitry Andric popRegions(Index);
12710b57cec5SDimitry Andric }
12720b57cec5SDimitry Andric
1273a58f00eaSDimitry Andric /// Find a valid range starting with \p StartingLoc and ending before \p
1274a58f00eaSDimitry Andric /// BeforeLoc.
findAreaStartingFromTo__anonc2c3cfe60211::CounterCoverageMappingBuilder1275a58f00eaSDimitry Andric std::optional<SourceRange> findAreaStartingFromTo(SourceLocation StartingLoc,
1276a58f00eaSDimitry Andric SourceLocation BeforeLoc) {
1277a58f00eaSDimitry Andric // If StartingLoc is in function-like macro, use its start location.
1278a58f00eaSDimitry Andric if (StartingLoc.isMacroID()) {
1279a58f00eaSDimitry Andric FileID FID = SM.getFileID(StartingLoc);
1280a58f00eaSDimitry Andric const SrcMgr::ExpansionInfo *EI = &SM.getSLocEntry(FID).getExpansion();
1281a58f00eaSDimitry Andric if (EI->isFunctionMacroExpansion())
1282a58f00eaSDimitry Andric StartingLoc = EI->getExpansionLocStart();
1283a58f00eaSDimitry Andric }
1284a58f00eaSDimitry Andric
1285a58f00eaSDimitry Andric size_t StartDepth = locationDepth(StartingLoc);
1286a58f00eaSDimitry Andric size_t EndDepth = locationDepth(BeforeLoc);
1287a58f00eaSDimitry Andric while (!SM.isWrittenInSameFile(StartingLoc, BeforeLoc)) {
1288a58f00eaSDimitry Andric bool UnnestStart = StartDepth >= EndDepth;
1289a58f00eaSDimitry Andric bool UnnestEnd = EndDepth >= StartDepth;
1290a58f00eaSDimitry Andric if (UnnestEnd) {
1291a58f00eaSDimitry Andric assert(SM.isWrittenInSameFile(getStartOfFileOrMacro(BeforeLoc),
1292a58f00eaSDimitry Andric BeforeLoc));
1293a58f00eaSDimitry Andric
1294a58f00eaSDimitry Andric BeforeLoc = getIncludeOrExpansionLoc(BeforeLoc);
1295a58f00eaSDimitry Andric assert(BeforeLoc.isValid());
1296a58f00eaSDimitry Andric EndDepth--;
1297a58f00eaSDimitry Andric }
1298a58f00eaSDimitry Andric if (UnnestStart) {
1299a58f00eaSDimitry Andric assert(SM.isWrittenInSameFile(StartingLoc,
1300a58f00eaSDimitry Andric getStartOfFileOrMacro(StartingLoc)));
1301a58f00eaSDimitry Andric
1302a58f00eaSDimitry Andric StartingLoc = getIncludeOrExpansionLoc(StartingLoc);
1303a58f00eaSDimitry Andric assert(StartingLoc.isValid());
1304a58f00eaSDimitry Andric StartDepth--;
1305a58f00eaSDimitry Andric }
1306a58f00eaSDimitry Andric }
1307a58f00eaSDimitry Andric // If the start and end locations of the gap are both within the same macro
1308a58f00eaSDimitry Andric // file, the range may not be in source order.
1309a58f00eaSDimitry Andric if (StartingLoc.isMacroID() || BeforeLoc.isMacroID())
1310a58f00eaSDimitry Andric return std::nullopt;
1311a58f00eaSDimitry Andric if (!SM.isWrittenInSameFile(StartingLoc, BeforeLoc) ||
1312a58f00eaSDimitry Andric !SpellingRegion(SM, StartingLoc, BeforeLoc).isInSourceOrder())
1313a58f00eaSDimitry Andric return std::nullopt;
1314a58f00eaSDimitry Andric return {{StartingLoc, BeforeLoc}};
1315a58f00eaSDimitry Andric }
1316a58f00eaSDimitry Andric
markSkipped__anonc2c3cfe60211::CounterCoverageMappingBuilder1317a58f00eaSDimitry Andric void markSkipped(SourceLocation StartLoc, SourceLocation BeforeLoc) {
1318a58f00eaSDimitry Andric const auto Skipped = findAreaStartingFromTo(StartLoc, BeforeLoc);
1319a58f00eaSDimitry Andric
1320a58f00eaSDimitry Andric if (!Skipped)
1321a58f00eaSDimitry Andric return;
1322a58f00eaSDimitry Andric
1323a58f00eaSDimitry Andric const auto NewStartLoc = Skipped->getBegin();
1324a58f00eaSDimitry Andric const auto EndLoc = Skipped->getEnd();
1325a58f00eaSDimitry Andric
1326a58f00eaSDimitry Andric if (NewStartLoc == EndLoc)
1327a58f00eaSDimitry Andric return;
1328a58f00eaSDimitry Andric assert(SpellingRegion(SM, NewStartLoc, EndLoc).isInSourceOrder());
1329a58f00eaSDimitry Andric handleFileExit(NewStartLoc);
1330a58f00eaSDimitry Andric size_t Index = pushRegion({}, NewStartLoc, EndLoc);
1331a58f00eaSDimitry Andric getRegion().setSkipped(true);
1332a58f00eaSDimitry Andric handleFileExit(EndLoc);
1333a58f00eaSDimitry Andric popRegions(Index);
1334a58f00eaSDimitry Andric }
1335a58f00eaSDimitry Andric
13360b57cec5SDimitry Andric /// Keep counts of breaks and continues inside loops.
13370b57cec5SDimitry Andric struct BreakContinue {
13380b57cec5SDimitry Andric Counter BreakCount;
13390b57cec5SDimitry Andric Counter ContinueCount;
13400b57cec5SDimitry Andric };
13410b57cec5SDimitry Andric SmallVector<BreakContinue, 8> BreakContinueStack;
13420b57cec5SDimitry Andric
CounterCoverageMappingBuilder__anonc2c3cfe60211::CounterCoverageMappingBuilder13430b57cec5SDimitry Andric CounterCoverageMappingBuilder(
13440b57cec5SDimitry Andric CoverageMappingModuleGen &CVM,
1345cdc20ff6SDimitry Andric llvm::DenseMap<const Stmt *, unsigned> &CounterMap,
1346cdc20ff6SDimitry Andric llvm::DenseMap<const Stmt *, unsigned> &MCDCBitmapMap,
1347cdc20ff6SDimitry Andric llvm::DenseMap<const Stmt *, MCDCConditionID> &CondIDMap,
1348cdc20ff6SDimitry Andric SourceManager &SM, const LangOptions &LangOpts)
1349cdc20ff6SDimitry Andric : CoverageMappingBuilder(CVM, SM, LangOpts), CounterMap(CounterMap),
1350cdc20ff6SDimitry Andric MCDCBitmapMap(MCDCBitmapMap),
1351cdc20ff6SDimitry Andric MCDCBuilder(CVM.getCodeGenModule(), CondIDMap, MCDCBitmapMap) {}
13520b57cec5SDimitry Andric
13530b57cec5SDimitry Andric /// Write the mapping data to the output stream
write__anonc2c3cfe60211::CounterCoverageMappingBuilder13540b57cec5SDimitry Andric void write(llvm::raw_ostream &OS) {
13550b57cec5SDimitry Andric llvm::SmallVector<unsigned, 8> VirtualFileMapping;
13560b57cec5SDimitry Andric gatherFileIDs(VirtualFileMapping);
13570b57cec5SDimitry Andric SourceRegionFilter Filter = emitExpansionRegions();
13580b57cec5SDimitry Andric emitSourceRegions(Filter);
13590b57cec5SDimitry Andric gatherSkippedRegions();
13600b57cec5SDimitry Andric
13610b57cec5SDimitry Andric if (MappingRegions.empty())
13620b57cec5SDimitry Andric return;
13630b57cec5SDimitry Andric
13640b57cec5SDimitry Andric CoverageMappingWriter Writer(VirtualFileMapping, Builder.getExpressions(),
13650b57cec5SDimitry Andric MappingRegions);
13660b57cec5SDimitry Andric Writer.write(OS);
13670b57cec5SDimitry Andric }
13680b57cec5SDimitry Andric
VisitStmt__anonc2c3cfe60211::CounterCoverageMappingBuilder13690b57cec5SDimitry Andric void VisitStmt(const Stmt *S) {
13700b57cec5SDimitry Andric if (S->getBeginLoc().isValid())
13710b57cec5SDimitry Andric extendRegion(S);
1372fe6060f1SDimitry Andric const Stmt *LastStmt = nullptr;
1373fe6060f1SDimitry Andric bool SaveTerminateStmt = HasTerminateStmt;
1374fe6060f1SDimitry Andric HasTerminateStmt = false;
1375fe6060f1SDimitry Andric GapRegionCounter = Counter::getZero();
13760b57cec5SDimitry Andric for (const Stmt *Child : S->children())
1377fe6060f1SDimitry Andric if (Child) {
1378fe6060f1SDimitry Andric // If last statement contains terminate statements, add a gap area
1379*fe72d8ecSDimitry Andric // between the two statements.
1380*fe72d8ecSDimitry Andric if (LastStmt && HasTerminateStmt) {
1381fe6060f1SDimitry Andric auto Gap = findGapAreaBetween(getEnd(LastStmt), getStart(Child));
1382fe6060f1SDimitry Andric if (Gap)
1383fe6060f1SDimitry Andric fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(),
1384fe6060f1SDimitry Andric GapRegionCounter);
1385fe6060f1SDimitry Andric SaveTerminateStmt = true;
1386fe6060f1SDimitry Andric HasTerminateStmt = false;
1387fe6060f1SDimitry Andric }
13880b57cec5SDimitry Andric this->Visit(Child);
1389fe6060f1SDimitry Andric LastStmt = Child;
1390fe6060f1SDimitry Andric }
1391fe6060f1SDimitry Andric if (SaveTerminateStmt)
1392fe6060f1SDimitry Andric HasTerminateStmt = true;
13930b57cec5SDimitry Andric handleFileExit(getEnd(S));
13940b57cec5SDimitry Andric }
13950b57cec5SDimitry Andric
VisitDecl__anonc2c3cfe60211::CounterCoverageMappingBuilder13960b57cec5SDimitry Andric void VisitDecl(const Decl *D) {
13970b57cec5SDimitry Andric Stmt *Body = D->getBody();
13980b57cec5SDimitry Andric
1399fe013be4SDimitry Andric // Do not propagate region counts into system headers unless collecting
1400fe013be4SDimitry Andric // coverage from system headers is explicitly enabled.
1401fe013be4SDimitry Andric if (!SystemHeadersCoverage && Body &&
1402fe013be4SDimitry Andric SM.isInSystemHeader(SM.getSpellingLoc(getStart(Body))))
14030b57cec5SDimitry Andric return;
14040b57cec5SDimitry Andric
14050b57cec5SDimitry Andric // Do not visit the artificial children nodes of defaulted methods. The
14060b57cec5SDimitry Andric // lexer may not be able to report back precise token end locations for
14070b57cec5SDimitry Andric // these children nodes (llvm.org/PR39822), and moreover users will not be
14080b57cec5SDimitry Andric // able to see coverage for them.
1409c9157d92SDimitry Andric Counter BodyCounter = getRegionCounter(Body);
14100b57cec5SDimitry Andric bool Defaulted = false;
14110b57cec5SDimitry Andric if (auto *Method = dyn_cast<CXXMethodDecl>(D))
14120b57cec5SDimitry Andric Defaulted = Method->isDefaulted();
1413c9157d92SDimitry Andric if (auto *Ctor = dyn_cast<CXXConstructorDecl>(D)) {
1414c9157d92SDimitry Andric for (auto *Initializer : Ctor->inits()) {
1415c9157d92SDimitry Andric if (Initializer->isWritten()) {
1416c9157d92SDimitry Andric auto *Init = Initializer->getInit();
1417c9157d92SDimitry Andric if (getStart(Init).isValid() && getEnd(Init).isValid())
1418c9157d92SDimitry Andric propagateCounts(BodyCounter, Init);
1419c9157d92SDimitry Andric }
1420c9157d92SDimitry Andric }
1421c9157d92SDimitry Andric }
14220b57cec5SDimitry Andric
1423c9157d92SDimitry Andric propagateCounts(BodyCounter, Body,
14240b57cec5SDimitry Andric /*VisitChildren=*/!Defaulted);
14250b57cec5SDimitry Andric assert(RegionStack.empty() && "Regions entered but never exited");
14260b57cec5SDimitry Andric }
14270b57cec5SDimitry Andric
VisitReturnStmt__anonc2c3cfe60211::CounterCoverageMappingBuilder14280b57cec5SDimitry Andric void VisitReturnStmt(const ReturnStmt *S) {
14290b57cec5SDimitry Andric extendRegion(S);
14300b57cec5SDimitry Andric if (S->getRetValue())
14310b57cec5SDimitry Andric Visit(S->getRetValue());
14320b57cec5SDimitry Andric terminateRegion(S);
14330b57cec5SDimitry Andric }
14340b57cec5SDimitry Andric
VisitCoroutineBodyStmt__anonc2c3cfe60211::CounterCoverageMappingBuilder14355ffd83dbSDimitry Andric void VisitCoroutineBodyStmt(const CoroutineBodyStmt *S) {
14365ffd83dbSDimitry Andric extendRegion(S);
14375ffd83dbSDimitry Andric Visit(S->getBody());
14385ffd83dbSDimitry Andric }
14395ffd83dbSDimitry Andric
VisitCoreturnStmt__anonc2c3cfe60211::CounterCoverageMappingBuilder14405ffd83dbSDimitry Andric void VisitCoreturnStmt(const CoreturnStmt *S) {
14415ffd83dbSDimitry Andric extendRegion(S);
14425ffd83dbSDimitry Andric if (S->getOperand())
14435ffd83dbSDimitry Andric Visit(S->getOperand());
14445ffd83dbSDimitry Andric terminateRegion(S);
14455ffd83dbSDimitry Andric }
14465ffd83dbSDimitry Andric
VisitCXXThrowExpr__anonc2c3cfe60211::CounterCoverageMappingBuilder14470b57cec5SDimitry Andric void VisitCXXThrowExpr(const CXXThrowExpr *E) {
14480b57cec5SDimitry Andric extendRegion(E);
14490b57cec5SDimitry Andric if (E->getSubExpr())
14500b57cec5SDimitry Andric Visit(E->getSubExpr());
14510b57cec5SDimitry Andric terminateRegion(E);
14520b57cec5SDimitry Andric }
14530b57cec5SDimitry Andric
VisitGotoStmt__anonc2c3cfe60211::CounterCoverageMappingBuilder14540b57cec5SDimitry Andric void VisitGotoStmt(const GotoStmt *S) { terminateRegion(S); }
14550b57cec5SDimitry Andric
VisitLabelStmt__anonc2c3cfe60211::CounterCoverageMappingBuilder14560b57cec5SDimitry Andric void VisitLabelStmt(const LabelStmt *S) {
14570b57cec5SDimitry Andric Counter LabelCount = getRegionCounter(S);
14580b57cec5SDimitry Andric SourceLocation Start = getStart(S);
14590b57cec5SDimitry Andric // We can't extendRegion here or we risk overlapping with our new region.
14600b57cec5SDimitry Andric handleFileExit(Start);
14610b57cec5SDimitry Andric pushRegion(LabelCount, Start);
14620b57cec5SDimitry Andric Visit(S->getSubStmt());
14630b57cec5SDimitry Andric }
14640b57cec5SDimitry Andric
VisitBreakStmt__anonc2c3cfe60211::CounterCoverageMappingBuilder14650b57cec5SDimitry Andric void VisitBreakStmt(const BreakStmt *S) {
14660b57cec5SDimitry Andric assert(!BreakContinueStack.empty() && "break not in a loop or switch!");
14670b57cec5SDimitry Andric BreakContinueStack.back().BreakCount = addCounters(
14680b57cec5SDimitry Andric BreakContinueStack.back().BreakCount, getRegion().getCounter());
14690b57cec5SDimitry Andric // FIXME: a break in a switch should terminate regions for all preceding
14700b57cec5SDimitry Andric // case statements, not just the most recent one.
14710b57cec5SDimitry Andric terminateRegion(S);
14720b57cec5SDimitry Andric }
14730b57cec5SDimitry Andric
VisitContinueStmt__anonc2c3cfe60211::CounterCoverageMappingBuilder14740b57cec5SDimitry Andric void VisitContinueStmt(const ContinueStmt *S) {
14750b57cec5SDimitry Andric assert(!BreakContinueStack.empty() && "continue stmt not in a loop!");
14760b57cec5SDimitry Andric BreakContinueStack.back().ContinueCount = addCounters(
14770b57cec5SDimitry Andric BreakContinueStack.back().ContinueCount, getRegion().getCounter());
14780b57cec5SDimitry Andric terminateRegion(S);
14790b57cec5SDimitry Andric }
14800b57cec5SDimitry Andric
VisitCallExpr__anonc2c3cfe60211::CounterCoverageMappingBuilder14810b57cec5SDimitry Andric void VisitCallExpr(const CallExpr *E) {
14820b57cec5SDimitry Andric VisitStmt(E);
14830b57cec5SDimitry Andric
14840b57cec5SDimitry Andric // Terminate the region when we hit a noreturn function.
14850b57cec5SDimitry Andric // (This is helpful dealing with switch statements.)
14860b57cec5SDimitry Andric QualType CalleeType = E->getCallee()->getType();
14870b57cec5SDimitry Andric if (getFunctionExtInfo(*CalleeType).getNoReturn())
14880b57cec5SDimitry Andric terminateRegion(E);
14890b57cec5SDimitry Andric }
14900b57cec5SDimitry Andric
VisitWhileStmt__anonc2c3cfe60211::CounterCoverageMappingBuilder14910b57cec5SDimitry Andric void VisitWhileStmt(const WhileStmt *S) {
14920b57cec5SDimitry Andric extendRegion(S);
14930b57cec5SDimitry Andric
14940b57cec5SDimitry Andric Counter ParentCount = getRegion().getCounter();
14950b57cec5SDimitry Andric Counter BodyCount = getRegionCounter(S);
14960b57cec5SDimitry Andric
14970b57cec5SDimitry Andric // Handle the body first so that we can get the backedge count.
14980b57cec5SDimitry Andric BreakContinueStack.push_back(BreakContinue());
14990b57cec5SDimitry Andric extendRegion(S->getBody());
15000b57cec5SDimitry Andric Counter BackedgeCount = propagateCounts(BodyCount, S->getBody());
15010b57cec5SDimitry Andric BreakContinue BC = BreakContinueStack.pop_back_val();
15020b57cec5SDimitry Andric
1503fe6060f1SDimitry Andric bool BodyHasTerminateStmt = HasTerminateStmt;
1504fe6060f1SDimitry Andric HasTerminateStmt = false;
1505fe6060f1SDimitry Andric
15060b57cec5SDimitry Andric // Go back to handle the condition.
15070b57cec5SDimitry Andric Counter CondCount =
15080b57cec5SDimitry Andric addCounters(ParentCount, BackedgeCount, BC.ContinueCount);
15090b57cec5SDimitry Andric propagateCounts(CondCount, S->getCond());
15100b57cec5SDimitry Andric adjustForOutOfOrderTraversal(getEnd(S));
15110b57cec5SDimitry Andric
15120b57cec5SDimitry Andric // The body count applies to the area immediately after the increment.
1513fe6060f1SDimitry Andric auto Gap = findGapAreaBetween(S->getRParenLoc(), getStart(S->getBody()));
15140b57cec5SDimitry Andric if (Gap)
15150b57cec5SDimitry Andric fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(), BodyCount);
15160b57cec5SDimitry Andric
15170b57cec5SDimitry Andric Counter OutCount =
15180b57cec5SDimitry Andric addCounters(BC.BreakCount, subtractCounters(CondCount, BodyCount));
1519fe6060f1SDimitry Andric if (OutCount != ParentCount) {
15200b57cec5SDimitry Andric pushRegion(OutCount);
1521fe6060f1SDimitry Andric GapRegionCounter = OutCount;
1522fe6060f1SDimitry Andric if (BodyHasTerminateStmt)
1523fe6060f1SDimitry Andric HasTerminateStmt = true;
1524fe6060f1SDimitry Andric }
1525e8d8bef9SDimitry Andric
1526e8d8bef9SDimitry Andric // Create Branch Region around condition.
1527e8d8bef9SDimitry Andric createBranchRegion(S->getCond(), BodyCount,
1528e8d8bef9SDimitry Andric subtractCounters(CondCount, BodyCount));
15290b57cec5SDimitry Andric }
15300b57cec5SDimitry Andric
VisitDoStmt__anonc2c3cfe60211::CounterCoverageMappingBuilder15310b57cec5SDimitry Andric void VisitDoStmt(const DoStmt *S) {
15320b57cec5SDimitry Andric extendRegion(S);
15330b57cec5SDimitry Andric
15340b57cec5SDimitry Andric Counter ParentCount = getRegion().getCounter();
15350b57cec5SDimitry Andric Counter BodyCount = getRegionCounter(S);
15360b57cec5SDimitry Andric
15370b57cec5SDimitry Andric BreakContinueStack.push_back(BreakContinue());
15380b57cec5SDimitry Andric extendRegion(S->getBody());
15390b57cec5SDimitry Andric Counter BackedgeCount =
15400b57cec5SDimitry Andric propagateCounts(addCounters(ParentCount, BodyCount), S->getBody());
15410b57cec5SDimitry Andric BreakContinue BC = BreakContinueStack.pop_back_val();
15420b57cec5SDimitry Andric
1543fe6060f1SDimitry Andric bool BodyHasTerminateStmt = HasTerminateStmt;
1544fe6060f1SDimitry Andric HasTerminateStmt = false;
1545fe6060f1SDimitry Andric
15460b57cec5SDimitry Andric Counter CondCount = addCounters(BackedgeCount, BC.ContinueCount);
15470b57cec5SDimitry Andric propagateCounts(CondCount, S->getCond());
15480b57cec5SDimitry Andric
15490b57cec5SDimitry Andric Counter OutCount =
15500b57cec5SDimitry Andric addCounters(BC.BreakCount, subtractCounters(CondCount, BodyCount));
1551fe6060f1SDimitry Andric if (OutCount != ParentCount) {
15520b57cec5SDimitry Andric pushRegion(OutCount);
1553fe6060f1SDimitry Andric GapRegionCounter = OutCount;
1554fe6060f1SDimitry Andric }
1555e8d8bef9SDimitry Andric
1556e8d8bef9SDimitry Andric // Create Branch Region around condition.
1557e8d8bef9SDimitry Andric createBranchRegion(S->getCond(), BodyCount,
1558e8d8bef9SDimitry Andric subtractCounters(CondCount, BodyCount));
1559fe6060f1SDimitry Andric
1560fe6060f1SDimitry Andric if (BodyHasTerminateStmt)
1561fe6060f1SDimitry Andric HasTerminateStmt = true;
15620b57cec5SDimitry Andric }
15630b57cec5SDimitry Andric
VisitForStmt__anonc2c3cfe60211::CounterCoverageMappingBuilder15640b57cec5SDimitry Andric void VisitForStmt(const ForStmt *S) {
15650b57cec5SDimitry Andric extendRegion(S);
15660b57cec5SDimitry Andric if (S->getInit())
15670b57cec5SDimitry Andric Visit(S->getInit());
15680b57cec5SDimitry Andric
15690b57cec5SDimitry Andric Counter ParentCount = getRegion().getCounter();
15700b57cec5SDimitry Andric Counter BodyCount = getRegionCounter(S);
15710b57cec5SDimitry Andric
15720b57cec5SDimitry Andric // The loop increment may contain a break or continue.
15730b57cec5SDimitry Andric if (S->getInc())
15740b57cec5SDimitry Andric BreakContinueStack.emplace_back();
15750b57cec5SDimitry Andric
15760b57cec5SDimitry Andric // Handle the body first so that we can get the backedge count.
15770b57cec5SDimitry Andric BreakContinueStack.emplace_back();
15780b57cec5SDimitry Andric extendRegion(S->getBody());
15790b57cec5SDimitry Andric Counter BackedgeCount = propagateCounts(BodyCount, S->getBody());
15800b57cec5SDimitry Andric BreakContinue BodyBC = BreakContinueStack.pop_back_val();
15810b57cec5SDimitry Andric
1582fe6060f1SDimitry Andric bool BodyHasTerminateStmt = HasTerminateStmt;
1583fe6060f1SDimitry Andric HasTerminateStmt = false;
1584fe6060f1SDimitry Andric
15850b57cec5SDimitry Andric // The increment is essentially part of the body but it needs to include
15860b57cec5SDimitry Andric // the count for all the continue statements.
15870b57cec5SDimitry Andric BreakContinue IncrementBC;
15880b57cec5SDimitry Andric if (const Stmt *Inc = S->getInc()) {
15890b57cec5SDimitry Andric propagateCounts(addCounters(BackedgeCount, BodyBC.ContinueCount), Inc);
15900b57cec5SDimitry Andric IncrementBC = BreakContinueStack.pop_back_val();
15910b57cec5SDimitry Andric }
15920b57cec5SDimitry Andric
15930b57cec5SDimitry Andric // Go back to handle the condition.
15940b57cec5SDimitry Andric Counter CondCount = addCounters(
15950b57cec5SDimitry Andric addCounters(ParentCount, BackedgeCount, BodyBC.ContinueCount),
15960b57cec5SDimitry Andric IncrementBC.ContinueCount);
15970b57cec5SDimitry Andric if (const Expr *Cond = S->getCond()) {
15980b57cec5SDimitry Andric propagateCounts(CondCount, Cond);
15990b57cec5SDimitry Andric adjustForOutOfOrderTraversal(getEnd(S));
16000b57cec5SDimitry Andric }
16010b57cec5SDimitry Andric
16020b57cec5SDimitry Andric // The body count applies to the area immediately after the increment.
1603fe6060f1SDimitry Andric auto Gap = findGapAreaBetween(S->getRParenLoc(), getStart(S->getBody()));
16040b57cec5SDimitry Andric if (Gap)
16050b57cec5SDimitry Andric fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(), BodyCount);
16060b57cec5SDimitry Andric
16070b57cec5SDimitry Andric Counter OutCount = addCounters(BodyBC.BreakCount, IncrementBC.BreakCount,
16080b57cec5SDimitry Andric subtractCounters(CondCount, BodyCount));
1609fe6060f1SDimitry Andric if (OutCount != ParentCount) {
16100b57cec5SDimitry Andric pushRegion(OutCount);
1611fe6060f1SDimitry Andric GapRegionCounter = OutCount;
1612fe6060f1SDimitry Andric if (BodyHasTerminateStmt)
1613fe6060f1SDimitry Andric HasTerminateStmt = true;
1614fe6060f1SDimitry Andric }
1615e8d8bef9SDimitry Andric
1616e8d8bef9SDimitry Andric // Create Branch Region around condition.
1617e8d8bef9SDimitry Andric createBranchRegion(S->getCond(), BodyCount,
1618e8d8bef9SDimitry Andric subtractCounters(CondCount, BodyCount));
16190b57cec5SDimitry Andric }
16200b57cec5SDimitry Andric
VisitCXXForRangeStmt__anonc2c3cfe60211::CounterCoverageMappingBuilder16210b57cec5SDimitry Andric void VisitCXXForRangeStmt(const CXXForRangeStmt *S) {
16220b57cec5SDimitry Andric extendRegion(S);
16230b57cec5SDimitry Andric if (S->getInit())
16240b57cec5SDimitry Andric Visit(S->getInit());
16250b57cec5SDimitry Andric Visit(S->getLoopVarStmt());
16260b57cec5SDimitry Andric Visit(S->getRangeStmt());
16270b57cec5SDimitry Andric
16280b57cec5SDimitry Andric Counter ParentCount = getRegion().getCounter();
16290b57cec5SDimitry Andric Counter BodyCount = getRegionCounter(S);
16300b57cec5SDimitry Andric
16310b57cec5SDimitry Andric BreakContinueStack.push_back(BreakContinue());
16320b57cec5SDimitry Andric extendRegion(S->getBody());
16330b57cec5SDimitry Andric Counter BackedgeCount = propagateCounts(BodyCount, S->getBody());
16340b57cec5SDimitry Andric BreakContinue BC = BreakContinueStack.pop_back_val();
16350b57cec5SDimitry Andric
1636fe6060f1SDimitry Andric bool BodyHasTerminateStmt = HasTerminateStmt;
1637fe6060f1SDimitry Andric HasTerminateStmt = false;
1638fe6060f1SDimitry Andric
16390b57cec5SDimitry Andric // The body count applies to the area immediately after the range.
1640fe6060f1SDimitry Andric auto Gap = findGapAreaBetween(S->getRParenLoc(), getStart(S->getBody()));
16410b57cec5SDimitry Andric if (Gap)
16420b57cec5SDimitry Andric fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(), BodyCount);
16430b57cec5SDimitry Andric
16440b57cec5SDimitry Andric Counter LoopCount =
16450b57cec5SDimitry Andric addCounters(ParentCount, BackedgeCount, BC.ContinueCount);
16460b57cec5SDimitry Andric Counter OutCount =
16470b57cec5SDimitry Andric addCounters(BC.BreakCount, subtractCounters(LoopCount, BodyCount));
1648fe6060f1SDimitry Andric if (OutCount != ParentCount) {
16490b57cec5SDimitry Andric pushRegion(OutCount);
1650fe6060f1SDimitry Andric GapRegionCounter = OutCount;
1651fe6060f1SDimitry Andric if (BodyHasTerminateStmt)
1652fe6060f1SDimitry Andric HasTerminateStmt = true;
1653fe6060f1SDimitry Andric }
1654e8d8bef9SDimitry Andric
1655e8d8bef9SDimitry Andric // Create Branch Region around condition.
1656e8d8bef9SDimitry Andric createBranchRegion(S->getCond(), BodyCount,
1657e8d8bef9SDimitry Andric subtractCounters(LoopCount, BodyCount));
16580b57cec5SDimitry Andric }
16590b57cec5SDimitry Andric
VisitObjCForCollectionStmt__anonc2c3cfe60211::CounterCoverageMappingBuilder16600b57cec5SDimitry Andric void VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S) {
16610b57cec5SDimitry Andric extendRegion(S);
16620b57cec5SDimitry Andric Visit(S->getElement());
16630b57cec5SDimitry Andric
16640b57cec5SDimitry Andric Counter ParentCount = getRegion().getCounter();
16650b57cec5SDimitry Andric Counter BodyCount = getRegionCounter(S);
16660b57cec5SDimitry Andric
16670b57cec5SDimitry Andric BreakContinueStack.push_back(BreakContinue());
16680b57cec5SDimitry Andric extendRegion(S->getBody());
16690b57cec5SDimitry Andric Counter BackedgeCount = propagateCounts(BodyCount, S->getBody());
16700b57cec5SDimitry Andric BreakContinue BC = BreakContinueStack.pop_back_val();
16710b57cec5SDimitry Andric
16720b57cec5SDimitry Andric // The body count applies to the area immediately after the collection.
1673fe6060f1SDimitry Andric auto Gap = findGapAreaBetween(S->getRParenLoc(), getStart(S->getBody()));
16740b57cec5SDimitry Andric if (Gap)
16750b57cec5SDimitry Andric fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(), BodyCount);
16760b57cec5SDimitry Andric
16770b57cec5SDimitry Andric Counter LoopCount =
16780b57cec5SDimitry Andric addCounters(ParentCount, BackedgeCount, BC.ContinueCount);
16790b57cec5SDimitry Andric Counter OutCount =
16800b57cec5SDimitry Andric addCounters(BC.BreakCount, subtractCounters(LoopCount, BodyCount));
1681fe6060f1SDimitry Andric if (OutCount != ParentCount) {
16820b57cec5SDimitry Andric pushRegion(OutCount);
1683fe6060f1SDimitry Andric GapRegionCounter = OutCount;
1684fe6060f1SDimitry Andric }
16850b57cec5SDimitry Andric }
16860b57cec5SDimitry Andric
VisitSwitchStmt__anonc2c3cfe60211::CounterCoverageMappingBuilder16870b57cec5SDimitry Andric void VisitSwitchStmt(const SwitchStmt *S) {
16880b57cec5SDimitry Andric extendRegion(S);
16890b57cec5SDimitry Andric if (S->getInit())
16900b57cec5SDimitry Andric Visit(S->getInit());
16910b57cec5SDimitry Andric Visit(S->getCond());
16920b57cec5SDimitry Andric
16930b57cec5SDimitry Andric BreakContinueStack.push_back(BreakContinue());
16940b57cec5SDimitry Andric
16950b57cec5SDimitry Andric const Stmt *Body = S->getBody();
16960b57cec5SDimitry Andric extendRegion(Body);
16970b57cec5SDimitry Andric if (const auto *CS = dyn_cast<CompoundStmt>(Body)) {
16980b57cec5SDimitry Andric if (!CS->body_empty()) {
16990b57cec5SDimitry Andric // Make a region for the body of the switch. If the body starts with
17000b57cec5SDimitry Andric // a case, that case will reuse this region; otherwise, this covers
17010b57cec5SDimitry Andric // the unreachable code at the beginning of the switch body.
1702480093f4SDimitry Andric size_t Index = pushRegion(Counter::getZero(), getStart(CS));
1703480093f4SDimitry Andric getRegion().setGap(true);
1704fe6060f1SDimitry Andric Visit(Body);
17050b57cec5SDimitry Andric
17060b57cec5SDimitry Andric // Set the end for the body of the switch, if it isn't already set.
17070b57cec5SDimitry Andric for (size_t i = RegionStack.size(); i != Index; --i) {
17080b57cec5SDimitry Andric if (!RegionStack[i - 1].hasEndLoc())
17090b57cec5SDimitry Andric RegionStack[i - 1].setEndLoc(getEnd(CS->body_back()));
17100b57cec5SDimitry Andric }
17110b57cec5SDimitry Andric
17120b57cec5SDimitry Andric popRegions(Index);
17130b57cec5SDimitry Andric }
17140b57cec5SDimitry Andric } else
17150b57cec5SDimitry Andric propagateCounts(Counter::getZero(), Body);
17160b57cec5SDimitry Andric BreakContinue BC = BreakContinueStack.pop_back_val();
17170b57cec5SDimitry Andric
17180b57cec5SDimitry Andric if (!BreakContinueStack.empty())
17190b57cec5SDimitry Andric BreakContinueStack.back().ContinueCount = addCounters(
17200b57cec5SDimitry Andric BreakContinueStack.back().ContinueCount, BC.ContinueCount);
17210b57cec5SDimitry Andric
1722e8d8bef9SDimitry Andric Counter ParentCount = getRegion().getCounter();
17230b57cec5SDimitry Andric Counter ExitCount = getRegionCounter(S);
17240b57cec5SDimitry Andric SourceLocation ExitLoc = getEnd(S);
17250b57cec5SDimitry Andric pushRegion(ExitCount);
1726fe6060f1SDimitry Andric GapRegionCounter = ExitCount;
17270b57cec5SDimitry Andric
17280b57cec5SDimitry Andric // Ensure that handleFileExit recognizes when the end location is located
17290b57cec5SDimitry Andric // in a different file.
17300b57cec5SDimitry Andric MostRecentLocation = getStart(S);
17310b57cec5SDimitry Andric handleFileExit(ExitLoc);
1732e8d8bef9SDimitry Andric
1733e8d8bef9SDimitry Andric // Create a Branch Region around each Case. Subtract the case's
1734e8d8bef9SDimitry Andric // counter from the Parent counter to track the "False" branch count.
1735e8d8bef9SDimitry Andric Counter CaseCountSum;
1736e8d8bef9SDimitry Andric bool HasDefaultCase = false;
1737e8d8bef9SDimitry Andric const SwitchCase *Case = S->getSwitchCaseList();
1738e8d8bef9SDimitry Andric for (; Case; Case = Case->getNextSwitchCase()) {
1739e8d8bef9SDimitry Andric HasDefaultCase = HasDefaultCase || isa<DefaultStmt>(Case);
174081ad6265SDimitry Andric CaseCountSum =
174181ad6265SDimitry Andric addCounters(CaseCountSum, getRegionCounter(Case), /*Simplify=*/false);
1742e8d8bef9SDimitry Andric createSwitchCaseRegion(
1743e8d8bef9SDimitry Andric Case, getRegionCounter(Case),
1744e8d8bef9SDimitry Andric subtractCounters(ParentCount, getRegionCounter(Case)));
1745e8d8bef9SDimitry Andric }
174681ad6265SDimitry Andric // Simplify is skipped while building the counters above: it can get really
174781ad6265SDimitry Andric // slow on top of switches with thousands of cases. Instead, trigger
174881ad6265SDimitry Andric // simplification by adding zero to the last counter.
174981ad6265SDimitry Andric CaseCountSum = addCounters(CaseCountSum, Counter::getZero());
1750e8d8bef9SDimitry Andric
1751e8d8bef9SDimitry Andric // If no explicit default case exists, create a branch region to represent
1752e8d8bef9SDimitry Andric // the hidden branch, which will be added later by the CodeGen. This region
1753e8d8bef9SDimitry Andric // will be associated with the switch statement's condition.
1754e8d8bef9SDimitry Andric if (!HasDefaultCase) {
1755e8d8bef9SDimitry Andric Counter DefaultTrue = subtractCounters(ParentCount, CaseCountSum);
1756e8d8bef9SDimitry Andric Counter DefaultFalse = subtractCounters(ParentCount, DefaultTrue);
1757e8d8bef9SDimitry Andric createBranchRegion(S->getCond(), DefaultTrue, DefaultFalse);
1758e8d8bef9SDimitry Andric }
17590b57cec5SDimitry Andric }
17600b57cec5SDimitry Andric
VisitSwitchCase__anonc2c3cfe60211::CounterCoverageMappingBuilder17610b57cec5SDimitry Andric void VisitSwitchCase(const SwitchCase *S) {
17620b57cec5SDimitry Andric extendRegion(S);
17630b57cec5SDimitry Andric
17640b57cec5SDimitry Andric SourceMappingRegion &Parent = getRegion();
17650b57cec5SDimitry Andric
17660b57cec5SDimitry Andric Counter Count = addCounters(Parent.getCounter(), getRegionCounter(S));
17670b57cec5SDimitry Andric // Reuse the existing region if it starts at our label. This is typical of
17680b57cec5SDimitry Andric // the first case in a switch.
17690b57cec5SDimitry Andric if (Parent.hasStartLoc() && Parent.getBeginLoc() == getStart(S))
17700b57cec5SDimitry Andric Parent.setCounter(Count);
17710b57cec5SDimitry Andric else
17720b57cec5SDimitry Andric pushRegion(Count, getStart(S));
17730b57cec5SDimitry Andric
1774fe6060f1SDimitry Andric GapRegionCounter = Count;
1775fe6060f1SDimitry Andric
17760b57cec5SDimitry Andric if (const auto *CS = dyn_cast<CaseStmt>(S)) {
17770b57cec5SDimitry Andric Visit(CS->getLHS());
17780b57cec5SDimitry Andric if (const Expr *RHS = CS->getRHS())
17790b57cec5SDimitry Andric Visit(RHS);
17800b57cec5SDimitry Andric }
17810b57cec5SDimitry Andric Visit(S->getSubStmt());
17820b57cec5SDimitry Andric }
17830b57cec5SDimitry Andric
coverIfConsteval__anonc2c3cfe60211::CounterCoverageMappingBuilder1784a58f00eaSDimitry Andric void coverIfConsteval(const IfStmt *S) {
1785a58f00eaSDimitry Andric assert(S->isConsteval());
1786a58f00eaSDimitry Andric
1787a58f00eaSDimitry Andric const auto *Then = S->getThen();
1788a58f00eaSDimitry Andric const auto *Else = S->getElse();
1789a58f00eaSDimitry Andric
1790a58f00eaSDimitry Andric // It's better for llvm-cov to create a new region with same counter
1791a58f00eaSDimitry Andric // so line-coverage can be properly calculated for lines containing
1792a58f00eaSDimitry Andric // a skipped region (without it the line is marked uncovered)
1793a58f00eaSDimitry Andric const Counter ParentCount = getRegion().getCounter();
1794a58f00eaSDimitry Andric
1795a58f00eaSDimitry Andric extendRegion(S);
1796a58f00eaSDimitry Andric
1797a58f00eaSDimitry Andric if (S->isNegatedConsteval()) {
1798a58f00eaSDimitry Andric // ignore 'if consteval'
1799a58f00eaSDimitry Andric markSkipped(S->getIfLoc(), getStart(Then));
1800a58f00eaSDimitry Andric propagateCounts(ParentCount, Then);
1801a58f00eaSDimitry Andric
1802a58f00eaSDimitry Andric if (Else) {
1803a58f00eaSDimitry Andric // ignore 'else <else>'
1804a58f00eaSDimitry Andric markSkipped(getEnd(Then), getEnd(Else));
1805a58f00eaSDimitry Andric }
1806a58f00eaSDimitry Andric } else {
1807a58f00eaSDimitry Andric assert(S->isNonNegatedConsteval());
1808a58f00eaSDimitry Andric // ignore 'if consteval <then> [else]'
1809a58f00eaSDimitry Andric markSkipped(S->getIfLoc(), Else ? getStart(Else) : getEnd(Then));
1810a58f00eaSDimitry Andric
1811a58f00eaSDimitry Andric if (Else)
1812a58f00eaSDimitry Andric propagateCounts(ParentCount, Else);
1813a58f00eaSDimitry Andric }
1814a58f00eaSDimitry Andric }
1815a58f00eaSDimitry Andric
coverIfConstexpr__anonc2c3cfe60211::CounterCoverageMappingBuilder1816a58f00eaSDimitry Andric void coverIfConstexpr(const IfStmt *S) {
1817a58f00eaSDimitry Andric assert(S->isConstexpr());
1818a58f00eaSDimitry Andric
1819a58f00eaSDimitry Andric // evaluate constant condition...
1820b9d9368bSDimitry Andric const bool isTrue =
1821b9d9368bSDimitry Andric S->getCond()
1822b9d9368bSDimitry Andric ->EvaluateKnownConstInt(CVM.getCodeGenModule().getContext())
1823b9d9368bSDimitry Andric .getBoolValue();
1824a58f00eaSDimitry Andric
1825a58f00eaSDimitry Andric extendRegion(S);
1826a58f00eaSDimitry Andric
1827a58f00eaSDimitry Andric // I'm using 'propagateCounts' later as new region is better and allows me
1828a58f00eaSDimitry Andric // to properly calculate line coverage in llvm-cov utility
1829a58f00eaSDimitry Andric const Counter ParentCount = getRegion().getCounter();
1830a58f00eaSDimitry Andric
1831a58f00eaSDimitry Andric // ignore 'if constexpr ('
1832a58f00eaSDimitry Andric SourceLocation startOfSkipped = S->getIfLoc();
1833a58f00eaSDimitry Andric
1834a58f00eaSDimitry Andric if (const auto *Init = S->getInit()) {
1835a58f00eaSDimitry Andric const auto start = getStart(Init);
1836a58f00eaSDimitry Andric const auto end = getEnd(Init);
1837a58f00eaSDimitry Andric
1838a58f00eaSDimitry Andric // this check is to make sure typedef here which doesn't have valid source
1839a58f00eaSDimitry Andric // location won't crash it
1840a58f00eaSDimitry Andric if (start.isValid() && end.isValid()) {
1841a58f00eaSDimitry Andric markSkipped(startOfSkipped, start);
1842a58f00eaSDimitry Andric propagateCounts(ParentCount, Init);
1843a58f00eaSDimitry Andric startOfSkipped = getEnd(Init);
1844a58f00eaSDimitry Andric }
1845a58f00eaSDimitry Andric }
1846a58f00eaSDimitry Andric
1847a58f00eaSDimitry Andric const auto *Then = S->getThen();
1848a58f00eaSDimitry Andric const auto *Else = S->getElse();
1849a58f00eaSDimitry Andric
1850a58f00eaSDimitry Andric if (isTrue) {
1851a58f00eaSDimitry Andric // ignore '<condition>)'
1852a58f00eaSDimitry Andric markSkipped(startOfSkipped, getStart(Then));
1853a58f00eaSDimitry Andric propagateCounts(ParentCount, Then);
1854a58f00eaSDimitry Andric
1855a58f00eaSDimitry Andric if (Else)
1856a58f00eaSDimitry Andric // ignore 'else <else>'
1857a58f00eaSDimitry Andric markSkipped(getEnd(Then), getEnd(Else));
1858a58f00eaSDimitry Andric } else {
1859a58f00eaSDimitry Andric // ignore '<condition>) <then> [else]'
1860a58f00eaSDimitry Andric markSkipped(startOfSkipped, Else ? getStart(Else) : getEnd(Then));
1861a58f00eaSDimitry Andric
1862a58f00eaSDimitry Andric if (Else)
1863a58f00eaSDimitry Andric propagateCounts(ParentCount, Else);
1864a58f00eaSDimitry Andric }
1865a58f00eaSDimitry Andric }
1866a58f00eaSDimitry Andric
VisitIfStmt__anonc2c3cfe60211::CounterCoverageMappingBuilder18670b57cec5SDimitry Andric void VisitIfStmt(const IfStmt *S) {
1868a58f00eaSDimitry Andric // "if constexpr" and "if consteval" are not normal conditional statements,
1869a58f00eaSDimitry Andric // their discarded statement should be skipped
1870a58f00eaSDimitry Andric if (S->isConsteval())
1871a58f00eaSDimitry Andric return coverIfConsteval(S);
1872a58f00eaSDimitry Andric else if (S->isConstexpr())
1873a58f00eaSDimitry Andric return coverIfConstexpr(S);
1874a58f00eaSDimitry Andric
18750b57cec5SDimitry Andric extendRegion(S);
18760b57cec5SDimitry Andric if (S->getInit())
18770b57cec5SDimitry Andric Visit(S->getInit());
18780b57cec5SDimitry Andric
18790b57cec5SDimitry Andric // Extend into the condition before we propagate through it below - this is
18800b57cec5SDimitry Andric // needed to handle macros that generate the "if" but not the condition.
18810b57cec5SDimitry Andric extendRegion(S->getCond());
18820b57cec5SDimitry Andric
18830b57cec5SDimitry Andric Counter ParentCount = getRegion().getCounter();
1884a58f00eaSDimitry Andric Counter ThenCount = getRegionCounter(S);
18856c20abcdSDimitry Andric
18860b57cec5SDimitry Andric // Emitting a counter for the condition makes it easier to interpret the
18870b57cec5SDimitry Andric // counter for the body when looking at the coverage.
18880b57cec5SDimitry Andric propagateCounts(ParentCount, S->getCond());
18890b57cec5SDimitry Andric
18900b57cec5SDimitry Andric // The 'then' count applies to the area immediately after the condition.
1891bdd1243dSDimitry Andric std::optional<SourceRange> Gap =
18926246ae0bSDimitry Andric findGapAreaBetween(S->getRParenLoc(), getStart(S->getThen()));
18930b57cec5SDimitry Andric if (Gap)
18940b57cec5SDimitry Andric fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(), ThenCount);
18950b57cec5SDimitry Andric
18960b57cec5SDimitry Andric extendRegion(S->getThen());
18970b57cec5SDimitry Andric Counter OutCount = propagateCounts(ThenCount, S->getThen());
1898a58f00eaSDimitry Andric Counter ElseCount = subtractCounters(ParentCount, ThenCount);
18996c20abcdSDimitry Andric
19000b57cec5SDimitry Andric if (const Stmt *Else = S->getElse()) {
1901fe6060f1SDimitry Andric bool ThenHasTerminateStmt = HasTerminateStmt;
1902fe6060f1SDimitry Andric HasTerminateStmt = false;
19030b57cec5SDimitry Andric // The 'else' count applies to the area immediately after the 'then'.
1904bdd1243dSDimitry Andric std::optional<SourceRange> Gap =
19056246ae0bSDimitry Andric findGapAreaBetween(getEnd(S->getThen()), getStart(Else));
19060b57cec5SDimitry Andric if (Gap)
19070b57cec5SDimitry Andric fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(), ElseCount);
19080b57cec5SDimitry Andric extendRegion(Else);
19090b57cec5SDimitry Andric OutCount = addCounters(OutCount, propagateCounts(ElseCount, Else));
1910fe6060f1SDimitry Andric
1911fe6060f1SDimitry Andric if (ThenHasTerminateStmt)
1912fe6060f1SDimitry Andric HasTerminateStmt = true;
19130b57cec5SDimitry Andric } else
19140b57cec5SDimitry Andric OutCount = addCounters(OutCount, ElseCount);
19150b57cec5SDimitry Andric
1916fe6060f1SDimitry Andric if (OutCount != ParentCount) {
19170b57cec5SDimitry Andric pushRegion(OutCount);
1918fe6060f1SDimitry Andric GapRegionCounter = OutCount;
1919fe6060f1SDimitry Andric }
1920e8d8bef9SDimitry Andric
1921e8d8bef9SDimitry Andric // Create Branch Region around condition.
1922e8d8bef9SDimitry Andric createBranchRegion(S->getCond(), ThenCount,
1923e8d8bef9SDimitry Andric subtractCounters(ParentCount, ThenCount));
19240b57cec5SDimitry Andric }
19250b57cec5SDimitry Andric
VisitCXXTryStmt__anonc2c3cfe60211::CounterCoverageMappingBuilder19260b57cec5SDimitry Andric void VisitCXXTryStmt(const CXXTryStmt *S) {
19270b57cec5SDimitry Andric extendRegion(S);
19280b57cec5SDimitry Andric // Handle macros that generate the "try" but not the rest.
19290b57cec5SDimitry Andric extendRegion(S->getTryBlock());
19300b57cec5SDimitry Andric
19310b57cec5SDimitry Andric Counter ParentCount = getRegion().getCounter();
19320b57cec5SDimitry Andric propagateCounts(ParentCount, S->getTryBlock());
19330b57cec5SDimitry Andric
19340b57cec5SDimitry Andric for (unsigned I = 0, E = S->getNumHandlers(); I < E; ++I)
19350b57cec5SDimitry Andric Visit(S->getHandler(I));
19360b57cec5SDimitry Andric
19370b57cec5SDimitry Andric Counter ExitCount = getRegionCounter(S);
19380b57cec5SDimitry Andric pushRegion(ExitCount);
19390b57cec5SDimitry Andric }
19400b57cec5SDimitry Andric
VisitCXXCatchStmt__anonc2c3cfe60211::CounterCoverageMappingBuilder19410b57cec5SDimitry Andric void VisitCXXCatchStmt(const CXXCatchStmt *S) {
19420b57cec5SDimitry Andric propagateCounts(getRegionCounter(S), S->getHandlerBlock());
19430b57cec5SDimitry Andric }
19440b57cec5SDimitry Andric
VisitAbstractConditionalOperator__anonc2c3cfe60211::CounterCoverageMappingBuilder19450b57cec5SDimitry Andric void VisitAbstractConditionalOperator(const AbstractConditionalOperator *E) {
19460b57cec5SDimitry Andric extendRegion(E);
19470b57cec5SDimitry Andric
19480b57cec5SDimitry Andric Counter ParentCount = getRegion().getCounter();
19490b57cec5SDimitry Andric Counter TrueCount = getRegionCounter(E);
19500b57cec5SDimitry Andric
1951fe6060f1SDimitry Andric propagateCounts(ParentCount, E->getCond());
1952fe013be4SDimitry Andric Counter OutCount;
19530b57cec5SDimitry Andric
19540b57cec5SDimitry Andric if (!isa<BinaryConditionalOperator>(E)) {
19550b57cec5SDimitry Andric // The 'then' count applies to the area immediately after the condition.
19560b57cec5SDimitry Andric auto Gap =
19570b57cec5SDimitry Andric findGapAreaBetween(E->getQuestionLoc(), getStart(E->getTrueExpr()));
19580b57cec5SDimitry Andric if (Gap)
19590b57cec5SDimitry Andric fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(), TrueCount);
19600b57cec5SDimitry Andric
19610b57cec5SDimitry Andric extendRegion(E->getTrueExpr());
1962fe013be4SDimitry Andric OutCount = propagateCounts(TrueCount, E->getTrueExpr());
19630b57cec5SDimitry Andric }
19640b57cec5SDimitry Andric
19650b57cec5SDimitry Andric extendRegion(E->getFalseExpr());
1966fe013be4SDimitry Andric OutCount = addCounters(
1967fe013be4SDimitry Andric OutCount, propagateCounts(subtractCounters(ParentCount, TrueCount),
1968fe013be4SDimitry Andric E->getFalseExpr()));
1969fe013be4SDimitry Andric
1970fe013be4SDimitry Andric if (OutCount != ParentCount) {
1971fe013be4SDimitry Andric pushRegion(OutCount);
1972fe013be4SDimitry Andric GapRegionCounter = OutCount;
1973fe013be4SDimitry Andric }
1974e8d8bef9SDimitry Andric
1975e8d8bef9SDimitry Andric // Create Branch Region around condition.
1976e8d8bef9SDimitry Andric createBranchRegion(E->getCond(), TrueCount,
1977e8d8bef9SDimitry Andric subtractCounters(ParentCount, TrueCount));
19780b57cec5SDimitry Andric }
19790b57cec5SDimitry Andric
VisitBinLAnd__anonc2c3cfe60211::CounterCoverageMappingBuilder19800b57cec5SDimitry Andric void VisitBinLAnd(const BinaryOperator *E) {
1981a58f00eaSDimitry Andric bool IsRootNode = MCDCBuilder.isIdle();
1982a58f00eaSDimitry Andric
1983a58f00eaSDimitry Andric // Keep track of Binary Operator and assign MCDC condition IDs.
1984cdc20ff6SDimitry Andric MCDCBuilder.pushAndAssignIDs(E);
1985cdc20ff6SDimitry Andric
19860b57cec5SDimitry Andric extendRegion(E->getLHS());
19870b57cec5SDimitry Andric propagateCounts(getRegion().getCounter(), E->getLHS());
19880b57cec5SDimitry Andric handleFileExit(getEnd(E->getLHS()));
19890b57cec5SDimitry Andric
1990a58f00eaSDimitry Andric // Track LHS True/False Decision.
1991a58f00eaSDimitry Andric const auto DecisionLHS = MCDCBuilder.pop();
1992a58f00eaSDimitry Andric
1993e8d8bef9SDimitry Andric // Counter tracks the right hand side of a logical and operator.
19940b57cec5SDimitry Andric extendRegion(E->getRHS());
19950b57cec5SDimitry Andric propagateCounts(getRegionCounter(E), E->getRHS());
1996e8d8bef9SDimitry Andric
1997a58f00eaSDimitry Andric // Track RHS True/False Decision.
1998a58f00eaSDimitry Andric const auto DecisionRHS = MCDCBuilder.back();
1999a58f00eaSDimitry Andric
2000a58f00eaSDimitry Andric // Create MCDC Decision Region if at top-level (root).
2001cdc20ff6SDimitry Andric unsigned NumConds = 0;
2002a58f00eaSDimitry Andric if (IsRootNode && (NumConds = MCDCBuilder.getTotalConditionsAndReset(E)))
2003cdc20ff6SDimitry Andric createDecisionRegion(E, getRegionBitmap(E), NumConds);
2004cdc20ff6SDimitry Andric
2005e8d8bef9SDimitry Andric // Extract the RHS's Execution Counter.
2006e8d8bef9SDimitry Andric Counter RHSExecCnt = getRegionCounter(E);
2007e8d8bef9SDimitry Andric
2008e8d8bef9SDimitry Andric // Extract the RHS's "True" Instance Counter.
2009e8d8bef9SDimitry Andric Counter RHSTrueCnt = getRegionCounter(E->getRHS());
2010e8d8bef9SDimitry Andric
2011e8d8bef9SDimitry Andric // Extract the Parent Region Counter.
2012e8d8bef9SDimitry Andric Counter ParentCnt = getRegion().getCounter();
2013e8d8bef9SDimitry Andric
2014e8d8bef9SDimitry Andric // Create Branch Region around LHS condition.
2015e8d8bef9SDimitry Andric createBranchRegion(E->getLHS(), RHSExecCnt,
2016a58f00eaSDimitry Andric subtractCounters(ParentCnt, RHSExecCnt), DecisionLHS);
2017e8d8bef9SDimitry Andric
2018e8d8bef9SDimitry Andric // Create Branch Region around RHS condition.
2019e8d8bef9SDimitry Andric createBranchRegion(E->getRHS(), RHSTrueCnt,
2020a58f00eaSDimitry Andric subtractCounters(RHSExecCnt, RHSTrueCnt), DecisionRHS);
20210b57cec5SDimitry Andric }
20220b57cec5SDimitry Andric
2023fe013be4SDimitry Andric // Determine whether the right side of OR operation need to be visited.
shouldVisitRHS__anonc2c3cfe60211::CounterCoverageMappingBuilder2024fe013be4SDimitry Andric bool shouldVisitRHS(const Expr *LHS) {
2025fe013be4SDimitry Andric bool LHSIsTrue = false;
2026fe013be4SDimitry Andric bool LHSIsConst = false;
2027fe013be4SDimitry Andric if (!LHS->isValueDependent())
2028fe013be4SDimitry Andric LHSIsConst = LHS->EvaluateAsBooleanCondition(
2029fe013be4SDimitry Andric LHSIsTrue, CVM.getCodeGenModule().getContext());
2030fe013be4SDimitry Andric return !LHSIsConst || (LHSIsConst && !LHSIsTrue);
2031fe013be4SDimitry Andric }
2032fe013be4SDimitry Andric
VisitBinLOr__anonc2c3cfe60211::CounterCoverageMappingBuilder20330b57cec5SDimitry Andric void VisitBinLOr(const BinaryOperator *E) {
2034a58f00eaSDimitry Andric bool IsRootNode = MCDCBuilder.isIdle();
2035a58f00eaSDimitry Andric
2036a58f00eaSDimitry Andric // Keep track of Binary Operator and assign MCDC condition IDs.
2037cdc20ff6SDimitry Andric MCDCBuilder.pushAndAssignIDs(E);
2038cdc20ff6SDimitry Andric
20390b57cec5SDimitry Andric extendRegion(E->getLHS());
2040fe013be4SDimitry Andric Counter OutCount = propagateCounts(getRegion().getCounter(), E->getLHS());
20410b57cec5SDimitry Andric handleFileExit(getEnd(E->getLHS()));
20420b57cec5SDimitry Andric
2043a58f00eaSDimitry Andric // Track LHS True/False Decision.
2044a58f00eaSDimitry Andric const auto DecisionLHS = MCDCBuilder.pop();
2045a58f00eaSDimitry Andric
2046e8d8bef9SDimitry Andric // Counter tracks the right hand side of a logical or operator.
20470b57cec5SDimitry Andric extendRegion(E->getRHS());
20480b57cec5SDimitry Andric propagateCounts(getRegionCounter(E), E->getRHS());
2049e8d8bef9SDimitry Andric
2050a58f00eaSDimitry Andric // Track RHS True/False Decision.
2051a58f00eaSDimitry Andric const auto DecisionRHS = MCDCBuilder.back();
2052a58f00eaSDimitry Andric
2053a58f00eaSDimitry Andric // Create MCDC Decision Region if at top-level (root).
2054cdc20ff6SDimitry Andric unsigned NumConds = 0;
2055a58f00eaSDimitry Andric if (IsRootNode && (NumConds = MCDCBuilder.getTotalConditionsAndReset(E)))
2056cdc20ff6SDimitry Andric createDecisionRegion(E, getRegionBitmap(E), NumConds);
2057cdc20ff6SDimitry Andric
2058e8d8bef9SDimitry Andric // Extract the RHS's Execution Counter.
2059e8d8bef9SDimitry Andric Counter RHSExecCnt = getRegionCounter(E);
2060e8d8bef9SDimitry Andric
2061e8d8bef9SDimitry Andric // Extract the RHS's "False" Instance Counter.
2062e8d8bef9SDimitry Andric Counter RHSFalseCnt = getRegionCounter(E->getRHS());
2063e8d8bef9SDimitry Andric
2064fe013be4SDimitry Andric if (!shouldVisitRHS(E->getLHS())) {
2065fe013be4SDimitry Andric GapRegionCounter = OutCount;
2066fe013be4SDimitry Andric }
2067fe013be4SDimitry Andric
2068e8d8bef9SDimitry Andric // Extract the Parent Region Counter.
2069e8d8bef9SDimitry Andric Counter ParentCnt = getRegion().getCounter();
2070e8d8bef9SDimitry Andric
2071e8d8bef9SDimitry Andric // Create Branch Region around LHS condition.
2072e8d8bef9SDimitry Andric createBranchRegion(E->getLHS(), subtractCounters(ParentCnt, RHSExecCnt),
2073a58f00eaSDimitry Andric RHSExecCnt, DecisionLHS);
2074e8d8bef9SDimitry Andric
2075e8d8bef9SDimitry Andric // Create Branch Region around RHS condition.
2076e8d8bef9SDimitry Andric createBranchRegion(E->getRHS(), subtractCounters(RHSExecCnt, RHSFalseCnt),
2077a58f00eaSDimitry Andric RHSFalseCnt, DecisionRHS);
20780b57cec5SDimitry Andric }
20790b57cec5SDimitry Andric
VisitLambdaExpr__anonc2c3cfe60211::CounterCoverageMappingBuilder20800b57cec5SDimitry Andric void VisitLambdaExpr(const LambdaExpr *LE) {
20810b57cec5SDimitry Andric // Lambdas are treated as their own functions for now, so we shouldn't
20820b57cec5SDimitry Andric // propagate counts into them.
20830b57cec5SDimitry Andric }
2084fe013be4SDimitry Andric
VisitPseudoObjectExpr__anonc2c3cfe60211::CounterCoverageMappingBuilder2085fe013be4SDimitry Andric void VisitPseudoObjectExpr(const PseudoObjectExpr *POE) {
2086fe013be4SDimitry Andric // Just visit syntatic expression as this is what users actually write.
2087fe013be4SDimitry Andric VisitStmt(POE->getSyntacticForm());
2088fe013be4SDimitry Andric }
2089fe013be4SDimitry Andric
VisitOpaqueValueExpr__anonc2c3cfe60211::CounterCoverageMappingBuilder2090fe013be4SDimitry Andric void VisitOpaqueValueExpr(const OpaqueValueExpr* OVE) {
2091fe013be4SDimitry Andric Visit(OVE->getSourceExpr());
2092fe013be4SDimitry Andric }
20930b57cec5SDimitry Andric };
20940b57cec5SDimitry Andric
20950b57cec5SDimitry Andric } // end anonymous namespace
20960b57cec5SDimitry Andric
dump(llvm::raw_ostream & OS,StringRef FunctionName,ArrayRef<CounterExpression> Expressions,ArrayRef<CounterMappingRegion> Regions)20970b57cec5SDimitry Andric static void dump(llvm::raw_ostream &OS, StringRef FunctionName,
20980b57cec5SDimitry Andric ArrayRef<CounterExpression> Expressions,
20990b57cec5SDimitry Andric ArrayRef<CounterMappingRegion> Regions) {
21000b57cec5SDimitry Andric OS << FunctionName << ":\n";
21010b57cec5SDimitry Andric CounterMappingContext Ctx(Expressions);
21020b57cec5SDimitry Andric for (const auto &R : Regions) {
21030b57cec5SDimitry Andric OS.indent(2);
21040b57cec5SDimitry Andric switch (R.Kind) {
21050b57cec5SDimitry Andric case CounterMappingRegion::CodeRegion:
21060b57cec5SDimitry Andric break;
21070b57cec5SDimitry Andric case CounterMappingRegion::ExpansionRegion:
21080b57cec5SDimitry Andric OS << "Expansion,";
21090b57cec5SDimitry Andric break;
21100b57cec5SDimitry Andric case CounterMappingRegion::SkippedRegion:
21110b57cec5SDimitry Andric OS << "Skipped,";
21120b57cec5SDimitry Andric break;
21130b57cec5SDimitry Andric case CounterMappingRegion::GapRegion:
21140b57cec5SDimitry Andric OS << "Gap,";
21150b57cec5SDimitry Andric break;
2116e8d8bef9SDimitry Andric case CounterMappingRegion::BranchRegion:
2117c9157d92SDimitry Andric case CounterMappingRegion::MCDCBranchRegion:
2118e8d8bef9SDimitry Andric OS << "Branch,";
2119e8d8bef9SDimitry Andric break;
2120c9157d92SDimitry Andric case CounterMappingRegion::MCDCDecisionRegion:
2121c9157d92SDimitry Andric OS << "Decision,";
2122c9157d92SDimitry Andric break;
21230b57cec5SDimitry Andric }
21240b57cec5SDimitry Andric
21250b57cec5SDimitry Andric OS << "File " << R.FileID << ", " << R.LineStart << ":" << R.ColumnStart
21260b57cec5SDimitry Andric << " -> " << R.LineEnd << ":" << R.ColumnEnd << " = ";
2127cdc20ff6SDimitry Andric
2128cdc20ff6SDimitry Andric if (R.Kind == CounterMappingRegion::MCDCDecisionRegion) {
2129cdc20ff6SDimitry Andric OS << "M:" << R.MCDCParams.BitmapIdx;
2130cdc20ff6SDimitry Andric OS << ", C:" << R.MCDCParams.NumConditions;
2131cdc20ff6SDimitry Andric } else {
21320b57cec5SDimitry Andric Ctx.dump(R.Count, OS);
2133e8d8bef9SDimitry Andric
2134cdc20ff6SDimitry Andric if (R.Kind == CounterMappingRegion::BranchRegion ||
2135cdc20ff6SDimitry Andric R.Kind == CounterMappingRegion::MCDCBranchRegion) {
2136e8d8bef9SDimitry Andric OS << ", ";
2137e8d8bef9SDimitry Andric Ctx.dump(R.FalseCount, OS);
2138e8d8bef9SDimitry Andric }
2139cdc20ff6SDimitry Andric }
2140cdc20ff6SDimitry Andric
2141cdc20ff6SDimitry Andric if (R.Kind == CounterMappingRegion::MCDCBranchRegion) {
2142cdc20ff6SDimitry Andric OS << " [" << R.MCDCParams.ID << "," << R.MCDCParams.TrueID;
2143cdc20ff6SDimitry Andric OS << "," << R.MCDCParams.FalseID << "] ";
2144cdc20ff6SDimitry Andric }
2145e8d8bef9SDimitry Andric
21460b57cec5SDimitry Andric if (R.Kind == CounterMappingRegion::ExpansionRegion)
21470b57cec5SDimitry Andric OS << " (Expanded file = " << R.ExpandedFileID << ")";
21480b57cec5SDimitry Andric OS << "\n";
21490b57cec5SDimitry Andric }
21500b57cec5SDimitry Andric }
21510b57cec5SDimitry Andric
CoverageMappingModuleGen(CodeGenModule & CGM,CoverageSourceInfo & SourceInfo)2152e8d8bef9SDimitry Andric CoverageMappingModuleGen::CoverageMappingModuleGen(
2153e8d8bef9SDimitry Andric CodeGenModule &CGM, CoverageSourceInfo &SourceInfo)
2154fe013be4SDimitry Andric : CGM(CGM), SourceInfo(SourceInfo) {}
2155fe6060f1SDimitry Andric
getCurrentDirname()2156fe6060f1SDimitry Andric std::string CoverageMappingModuleGen::getCurrentDirname() {
2157fe6060f1SDimitry Andric if (!CGM.getCodeGenOpts().CoverageCompilationDir.empty())
2158fe6060f1SDimitry Andric return CGM.getCodeGenOpts().CoverageCompilationDir;
2159fe6060f1SDimitry Andric
2160fe6060f1SDimitry Andric SmallString<256> CWD;
2161fe6060f1SDimitry Andric llvm::sys::fs::current_path(CWD);
2162fe6060f1SDimitry Andric return CWD.str().str();
2163e8d8bef9SDimitry Andric }
2164e8d8bef9SDimitry Andric
normalizeFilename(StringRef Filename)2165e8d8bef9SDimitry Andric std::string CoverageMappingModuleGen::normalizeFilename(StringRef Filename) {
2166e8d8bef9SDimitry Andric llvm::SmallString<256> Path(Filename);
2167e8d8bef9SDimitry Andric llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true);
2168fe013be4SDimitry Andric
2169fe013be4SDimitry Andric /// Traverse coverage prefix map in reverse order because prefix replacements
2170fe013be4SDimitry Andric /// are applied in reverse order starting from the last one when multiple
2171fe013be4SDimitry Andric /// prefix replacement options are provided.
2172fe013be4SDimitry Andric for (const auto &[From, To] :
2173fe013be4SDimitry Andric llvm::reverse(CGM.getCodeGenOpts().CoveragePrefixMap)) {
2174fe013be4SDimitry Andric if (llvm::sys::path::replace_path_prefix(Path, From, To))
2175e8d8bef9SDimitry Andric break;
2176e8d8bef9SDimitry Andric }
2177e8d8bef9SDimitry Andric return Path.str().str();
2178e8d8bef9SDimitry Andric }
2179e8d8bef9SDimitry Andric
getInstrProfSection(const CodeGenModule & CGM,llvm::InstrProfSectKind SK)21805ffd83dbSDimitry Andric static std::string getInstrProfSection(const CodeGenModule &CGM,
21815ffd83dbSDimitry Andric llvm::InstrProfSectKind SK) {
21825ffd83dbSDimitry Andric return llvm::getInstrProfSectionName(
21835ffd83dbSDimitry Andric SK, CGM.getContext().getTargetInfo().getTriple().getObjectFormat());
21845ffd83dbSDimitry Andric }
21855ffd83dbSDimitry Andric
emitFunctionMappingRecord(const FunctionInfo & Info,uint64_t FilenamesRef)21865ffd83dbSDimitry Andric void CoverageMappingModuleGen::emitFunctionMappingRecord(
21875ffd83dbSDimitry Andric const FunctionInfo &Info, uint64_t FilenamesRef) {
21880b57cec5SDimitry Andric llvm::LLVMContext &Ctx = CGM.getLLVMContext();
21895ffd83dbSDimitry Andric
21905ffd83dbSDimitry Andric // Assign a name to the function record. This is used to merge duplicates.
21915ffd83dbSDimitry Andric std::string FuncRecordName = "__covrec_" + llvm::utohexstr(Info.NameHash);
21925ffd83dbSDimitry Andric
21935ffd83dbSDimitry Andric // A dummy description for a function included-but-not-used in a TU can be
21945ffd83dbSDimitry Andric // replaced by full description provided by a different TU. The two kinds of
21955ffd83dbSDimitry Andric // descriptions play distinct roles: therefore, assign them different names
21965ffd83dbSDimitry Andric // to prevent `linkonce_odr` merging.
21975ffd83dbSDimitry Andric if (Info.IsUsed)
21985ffd83dbSDimitry Andric FuncRecordName += "u";
21995ffd83dbSDimitry Andric
22005ffd83dbSDimitry Andric // Create the function record type.
22015ffd83dbSDimitry Andric const uint64_t NameHash = Info.NameHash;
22025ffd83dbSDimitry Andric const uint64_t FuncHash = Info.FuncHash;
22035ffd83dbSDimitry Andric const std::string &CoverageMapping = Info.CoverageMapping;
22040b57cec5SDimitry Andric #define COVMAP_FUNC_RECORD(Type, LLVMType, Name, Init) LLVMType,
22050b57cec5SDimitry Andric llvm::Type *FunctionRecordTypes[] = {
22060b57cec5SDimitry Andric #include "llvm/ProfileData/InstrProfData.inc"
22070b57cec5SDimitry Andric };
22085ffd83dbSDimitry Andric auto *FunctionRecordTy =
2209bdd1243dSDimitry Andric llvm::StructType::get(Ctx, ArrayRef(FunctionRecordTypes),
22100b57cec5SDimitry Andric /*isPacked=*/true);
22110b57cec5SDimitry Andric
22125ffd83dbSDimitry Andric // Create the function record constant.
22130b57cec5SDimitry Andric #define COVMAP_FUNC_RECORD(Type, LLVMType, Name, Init) Init,
22140b57cec5SDimitry Andric llvm::Constant *FunctionRecordVals[] = {
22150b57cec5SDimitry Andric #include "llvm/ProfileData/InstrProfData.inc"
22160b57cec5SDimitry Andric };
2217bdd1243dSDimitry Andric auto *FuncRecordConstant =
2218bdd1243dSDimitry Andric llvm::ConstantStruct::get(FunctionRecordTy, ArrayRef(FunctionRecordVals));
22195ffd83dbSDimitry Andric
22205ffd83dbSDimitry Andric // Create the function record global.
22215ffd83dbSDimitry Andric auto *FuncRecord = new llvm::GlobalVariable(
22225ffd83dbSDimitry Andric CGM.getModule(), FunctionRecordTy, /*isConstant=*/true,
22235ffd83dbSDimitry Andric llvm::GlobalValue::LinkOnceODRLinkage, FuncRecordConstant,
22245ffd83dbSDimitry Andric FuncRecordName);
22255ffd83dbSDimitry Andric FuncRecord->setVisibility(llvm::GlobalValue::HiddenVisibility);
22265ffd83dbSDimitry Andric FuncRecord->setSection(getInstrProfSection(CGM, llvm::IPSK_covfun));
22275ffd83dbSDimitry Andric FuncRecord->setAlignment(llvm::Align(8));
22285ffd83dbSDimitry Andric if (CGM.supportsCOMDAT())
22295ffd83dbSDimitry Andric FuncRecord->setComdat(CGM.getModule().getOrInsertComdat(FuncRecordName));
22305ffd83dbSDimitry Andric
22315ffd83dbSDimitry Andric // Make sure the data doesn't get deleted.
22325ffd83dbSDimitry Andric CGM.addUsedGlobal(FuncRecord);
22335ffd83dbSDimitry Andric }
22345ffd83dbSDimitry Andric
addFunctionMappingRecord(llvm::GlobalVariable * NamePtr,StringRef NameValue,uint64_t FuncHash,const std::string & CoverageMapping,bool IsUsed)22355ffd83dbSDimitry Andric void CoverageMappingModuleGen::addFunctionMappingRecord(
22365ffd83dbSDimitry Andric llvm::GlobalVariable *NamePtr, StringRef NameValue, uint64_t FuncHash,
22375ffd83dbSDimitry Andric const std::string &CoverageMapping, bool IsUsed) {
22385ffd83dbSDimitry Andric const uint64_t NameHash = llvm::IndexedInstrProf::ComputeHash(NameValue);
22395ffd83dbSDimitry Andric FunctionRecords.push_back({NameHash, FuncHash, CoverageMapping, IsUsed});
22405ffd83dbSDimitry Andric
22410b57cec5SDimitry Andric if (!IsUsed)
2242c9157d92SDimitry Andric FunctionNames.push_back(NamePtr);
22430b57cec5SDimitry Andric
22440b57cec5SDimitry Andric if (CGM.getCodeGenOpts().DumpCoverageMapping) {
22450b57cec5SDimitry Andric // Dump the coverage mapping data for this function by decoding the
22460b57cec5SDimitry Andric // encoded data. This allows us to dump the mapping regions which were
22470b57cec5SDimitry Andric // also processed by the CoverageMappingWriter which performs
22480b57cec5SDimitry Andric // additional minimization operations such as reducing the number of
22490b57cec5SDimitry Andric // expressions.
2250fe6060f1SDimitry Andric llvm::SmallVector<std::string, 16> FilenameStrs;
22510b57cec5SDimitry Andric std::vector<StringRef> Filenames;
22520b57cec5SDimitry Andric std::vector<CounterExpression> Expressions;
22530b57cec5SDimitry Andric std::vector<CounterMappingRegion> Regions;
2254fe6060f1SDimitry Andric FilenameStrs.resize(FileEntries.size() + 1);
2255fe6060f1SDimitry Andric FilenameStrs[0] = normalizeFilename(getCurrentDirname());
22560b57cec5SDimitry Andric for (const auto &Entry : FileEntries) {
22570b57cec5SDimitry Andric auto I = Entry.second;
2258c9157d92SDimitry Andric FilenameStrs[I] = normalizeFilename(Entry.first.getName());
22590b57cec5SDimitry Andric }
2260bdd1243dSDimitry Andric ArrayRef<std::string> FilenameRefs = llvm::ArrayRef(FilenameStrs);
22610b57cec5SDimitry Andric RawCoverageMappingReader Reader(CoverageMapping, FilenameRefs, Filenames,
22620b57cec5SDimitry Andric Expressions, Regions);
22630b57cec5SDimitry Andric if (Reader.read())
22640b57cec5SDimitry Andric return;
22650b57cec5SDimitry Andric dump(llvm::outs(), NameValue, Expressions, Regions);
22660b57cec5SDimitry Andric }
22670b57cec5SDimitry Andric }
22680b57cec5SDimitry Andric
emit()22690b57cec5SDimitry Andric void CoverageMappingModuleGen::emit() {
22700b57cec5SDimitry Andric if (FunctionRecords.empty())
22710b57cec5SDimitry Andric return;
22720b57cec5SDimitry Andric llvm::LLVMContext &Ctx = CGM.getLLVMContext();
22730b57cec5SDimitry Andric auto *Int32Ty = llvm::Type::getInt32Ty(Ctx);
22740b57cec5SDimitry Andric
22750b57cec5SDimitry Andric // Create the filenames and merge them with coverage mappings
22760b57cec5SDimitry Andric llvm::SmallVector<std::string, 16> FilenameStrs;
2277fe6060f1SDimitry Andric FilenameStrs.resize(FileEntries.size() + 1);
2278fe6060f1SDimitry Andric // The first filename is the current working directory.
2279fe6060f1SDimitry Andric FilenameStrs[0] = normalizeFilename(getCurrentDirname());
22800b57cec5SDimitry Andric for (const auto &Entry : FileEntries) {
22810b57cec5SDimitry Andric auto I = Entry.second;
2282c9157d92SDimitry Andric FilenameStrs[I] = normalizeFilename(Entry.first.getName());
22830b57cec5SDimitry Andric }
22840b57cec5SDimitry Andric
22855ffd83dbSDimitry Andric std::string Filenames;
22865ffd83dbSDimitry Andric {
22875ffd83dbSDimitry Andric llvm::raw_string_ostream OS(Filenames);
2288fe6060f1SDimitry Andric CoverageFilenamesSectionWriter(FilenameStrs).write(OS);
22890b57cec5SDimitry Andric }
22905ffd83dbSDimitry Andric auto *FilenamesVal =
22915ffd83dbSDimitry Andric llvm::ConstantDataArray::getString(Ctx, Filenames, false);
22925ffd83dbSDimitry Andric const int64_t FilenamesRef = llvm::IndexedInstrProf::ComputeHash(Filenames);
22930b57cec5SDimitry Andric
22945ffd83dbSDimitry Andric // Emit the function records.
22955ffd83dbSDimitry Andric for (const FunctionInfo &Info : FunctionRecords)
22965ffd83dbSDimitry Andric emitFunctionMappingRecord(Info, FilenamesRef);
22970b57cec5SDimitry Andric
22985ffd83dbSDimitry Andric const unsigned NRecords = 0;
22995ffd83dbSDimitry Andric const size_t FilenamesSize = Filenames.size();
23005ffd83dbSDimitry Andric const unsigned CoverageMappingSize = 0;
23010b57cec5SDimitry Andric llvm::Type *CovDataHeaderTypes[] = {
23020b57cec5SDimitry Andric #define COVMAP_HEADER(Type, LLVMType, Name, Init) LLVMType,
23030b57cec5SDimitry Andric #include "llvm/ProfileData/InstrProfData.inc"
23040b57cec5SDimitry Andric };
23050b57cec5SDimitry Andric auto CovDataHeaderTy =
2306bdd1243dSDimitry Andric llvm::StructType::get(Ctx, ArrayRef(CovDataHeaderTypes));
23070b57cec5SDimitry Andric llvm::Constant *CovDataHeaderVals[] = {
23080b57cec5SDimitry Andric #define COVMAP_HEADER(Type, LLVMType, Name, Init) Init,
23090b57cec5SDimitry Andric #include "llvm/ProfileData/InstrProfData.inc"
23100b57cec5SDimitry Andric };
2311bdd1243dSDimitry Andric auto CovDataHeaderVal =
2312bdd1243dSDimitry Andric llvm::ConstantStruct::get(CovDataHeaderTy, ArrayRef(CovDataHeaderVals));
23130b57cec5SDimitry Andric
23140b57cec5SDimitry Andric // Create the coverage data record
23155ffd83dbSDimitry Andric llvm::Type *CovDataTypes[] = {CovDataHeaderTy, FilenamesVal->getType()};
2316bdd1243dSDimitry Andric auto CovDataTy = llvm::StructType::get(Ctx, ArrayRef(CovDataTypes));
23175ffd83dbSDimitry Andric llvm::Constant *TUDataVals[] = {CovDataHeaderVal, FilenamesVal};
2318bdd1243dSDimitry Andric auto CovDataVal = llvm::ConstantStruct::get(CovDataTy, ArrayRef(TUDataVals));
23190b57cec5SDimitry Andric auto CovData = new llvm::GlobalVariable(
23205ffd83dbSDimitry Andric CGM.getModule(), CovDataTy, true, llvm::GlobalValue::PrivateLinkage,
23210b57cec5SDimitry Andric CovDataVal, llvm::getCoverageMappingVarName());
23220b57cec5SDimitry Andric
23235ffd83dbSDimitry Andric CovData->setSection(getInstrProfSection(CGM, llvm::IPSK_covmap));
2324a7dea167SDimitry Andric CovData->setAlignment(llvm::Align(8));
23250b57cec5SDimitry Andric
23260b57cec5SDimitry Andric // Make sure the data doesn't get deleted.
23270b57cec5SDimitry Andric CGM.addUsedGlobal(CovData);
23280b57cec5SDimitry Andric // Create the deferred function records array
23290b57cec5SDimitry Andric if (!FunctionNames.empty()) {
2330c9157d92SDimitry Andric auto NamesArrTy = llvm::ArrayType::get(llvm::PointerType::getUnqual(Ctx),
23310b57cec5SDimitry Andric FunctionNames.size());
23320b57cec5SDimitry Andric auto NamesArrVal = llvm::ConstantArray::get(NamesArrTy, FunctionNames);
23330b57cec5SDimitry Andric // This variable will *NOT* be emitted to the object file. It is used
23340b57cec5SDimitry Andric // to pass the list of names referenced to codegen.
23350b57cec5SDimitry Andric new llvm::GlobalVariable(CGM.getModule(), NamesArrTy, true,
23360b57cec5SDimitry Andric llvm::GlobalValue::InternalLinkage, NamesArrVal,
23370b57cec5SDimitry Andric llvm::getCoverageUnusedNamesVarName());
23380b57cec5SDimitry Andric }
23390b57cec5SDimitry Andric }
23400b57cec5SDimitry Andric
getFileID(FileEntryRef File)2341c9157d92SDimitry Andric unsigned CoverageMappingModuleGen::getFileID(FileEntryRef File) {
23420b57cec5SDimitry Andric auto It = FileEntries.find(File);
23430b57cec5SDimitry Andric if (It != FileEntries.end())
23440b57cec5SDimitry Andric return It->second;
2345fe6060f1SDimitry Andric unsigned FileID = FileEntries.size() + 1;
23460b57cec5SDimitry Andric FileEntries.insert(std::make_pair(File, FileID));
23470b57cec5SDimitry Andric return FileID;
23480b57cec5SDimitry Andric }
23490b57cec5SDimitry Andric
emitCounterMapping(const Decl * D,llvm::raw_ostream & OS)23500b57cec5SDimitry Andric void CoverageMappingGen::emitCounterMapping(const Decl *D,
23510b57cec5SDimitry Andric llvm::raw_ostream &OS) {
2352cdc20ff6SDimitry Andric assert(CounterMap && MCDCBitmapMap);
2353cdc20ff6SDimitry Andric CounterCoverageMappingBuilder Walker(CVM, *CounterMap, *MCDCBitmapMap,
2354cdc20ff6SDimitry Andric *CondIDMap, SM, LangOpts);
23550b57cec5SDimitry Andric Walker.VisitDecl(D);
23560b57cec5SDimitry Andric Walker.write(OS);
23570b57cec5SDimitry Andric }
23580b57cec5SDimitry Andric
emitEmptyMapping(const Decl * D,llvm::raw_ostream & OS)23590b57cec5SDimitry Andric void CoverageMappingGen::emitEmptyMapping(const Decl *D,
23600b57cec5SDimitry Andric llvm::raw_ostream &OS) {
23610b57cec5SDimitry Andric EmptyCoverageMappingBuilder Walker(CVM, SM, LangOpts);
23620b57cec5SDimitry Andric Walker.VisitDecl(D);
23630b57cec5SDimitry Andric Walker.write(OS);
23640b57cec5SDimitry Andric }
2365