llvm::cl::desc(
"Enable single byte coverage"),
37llvm::cl::Hidden, llvm::cl::init(
false));
41 "emptyline-comment-coverage",
42llvm::cl::desc(
"Emit emptylines and comment lines as skipped regions (only " 43 "disable it on test)"),
44llvm::cl::init(
true), llvm::cl::Hidden);
48 "system-headers-coverage",
49cl::desc(
"Enable collecting coverage from system headers"), cl::init(
false),
53using namespace clang;
54using namespaceCodeGen;
69 if(Tok.
getKind() != clang::tok::eod)
79PrevTokLoc == SkippedRanges.back().PrevTokLoc &&
80SourceMgr.isWrittenInSameFile(SkippedRanges.back().Range.getEnd(),
82SkippedRanges.back().Range.setEnd(
Range.
getEnd());
84SkippedRanges.push_back({
Range, RangeKind, PrevTokLoc});
101 if(!SkippedRanges.empty() && SkippedRanges.back().NextTokLoc.isInvalid())
102SkippedRanges.back().NextTokLoc =
Loc;
107classSourceMappingRegion {
112std::optional<Counter> FalseCount;
115mcdc::Parameters MCDCParams;
118std::optional<SourceLocation> LocStart;
121std::optional<SourceLocation> LocEnd;
132SourceMappingRegion(Counter Count, std::optional<SourceLocation> LocStart,
133std::optional<SourceLocation> LocEnd,
134 boolGapRegion =
false)
135: Count(Count), LocStart(LocStart), LocEnd(LocEnd), GapRegion(GapRegion),
136SkippedRegion(
false) {}
138SourceMappingRegion(Counter Count, std::optional<Counter> FalseCount,
139mcdc::Parameters MCDCParams,
140std::optional<SourceLocation> LocStart,
141std::optional<SourceLocation> LocEnd,
142 boolGapRegion =
false)
143: Count(Count), FalseCount(FalseCount), MCDCParams(MCDCParams),
144LocStart(LocStart), LocEnd(LocEnd), GapRegion(GapRegion),
145SkippedRegion(
false) {}
147SourceMappingRegion(mcdc::Parameters MCDCParams,
148std::optional<SourceLocation> LocStart,
149std::optional<SourceLocation> LocEnd)
150: MCDCParams(MCDCParams), LocStart(LocStart), LocEnd(LocEnd),
153 constCounter &getCounter()
const{
returnCount; }
155 constCounter &getFalseCounter()
const{
156assert(FalseCount &&
"Region has no alternate counter");
160 voidsetCounter(Counter
C) { Count =
C; }
162 boolhasStartLoc()
const{
returnLocStart.has_value(); }
167assert(LocStart &&
"Region has no start location");
171 boolhasEndLoc()
const{
returnLocEnd.has_value(); }
174assert(
Loc.
isValid() &&
"Setting an invalid end location");
179assert(LocEnd &&
"Region has no end location");
183 boolisGap()
const{
returnGapRegion; }
185 voidsetGap(
boolGap) { GapRegion = Gap; }
187 boolisSkipped()
const{
returnSkippedRegion; }
189 voidsetSkipped(
boolSkipped) { SkippedRegion = Skipped; }
191 boolisBranch()
const{
returnFalseCount.has_value(); }
193 boolisMCDCBranch()
const{
194 returnstd::holds_alternative<mcdc::BranchParameters>(MCDCParams);
197 const auto&getMCDCBranchParams()
const{
198 returnmcdc::getParams<const mcdc::BranchParameters>(MCDCParams);
201 boolisMCDCDecision()
const{
202 returnstd::holds_alternative<mcdc::DecisionParameters>(MCDCParams);
205 const auto&getMCDCDecisionParams()
const{
206 returnmcdc::getParams<const mcdc::DecisionParameters>(MCDCParams);
209 constmcdc::Parameters &getMCDCParams()
const{
returnMCDCParams; }
211 voidresetMCDCParams() { MCDCParams = mcdc::Parameters(); }
215structSpellingRegion {
220 unsignedColumnStart;
230LineStart =
SM.getSpellingLineNumber(LocStart);
231ColumnStart =
SM.getSpellingColumnNumber(LocStart);
232LineEnd =
SM.getSpellingLineNumber(LocEnd);
233ColumnEnd =
SM.getSpellingColumnNumber(LocEnd);
237: SpellingRegion(
SM, R.getBeginLoc(), R.getEndLoc()) {}
241 boolisInSourceOrder()
const{
242 return(LineStart < LineEnd) ||
243(LineStart == LineEnd && ColumnStart <= ColumnEnd);
249classCoverageMappingBuilder {
257llvm::SmallDenseMap<FileID, std::pair<unsigned, SourceLocation>, 8>
264std::vector<SourceMappingRegion> SourceRegions;
271 typedefllvm::SmallSet<std::pair<SourceLocation, SourceLocation>, 8>
276: CVM(CVM),
SM(
SM), LangOpts(LangOpts) {}
291 return SM.getLocForStartOfFile(
SM.getFileID(
Loc));
298 SM.getFileOffset(
Loc));
299 return SM.getLocForEndOfFile(
SM.getFileID(
Loc));
309std::pair<SourceLocation, std::optional<SourceLocation>>
311std::optional<SourceLocation> EndLoc = std::nullopt;
313 SM.isWrittenInScratchSpace(
SM.getSpellingLoc(
Loc))) {
314 autoExpansionRange =
SM.getImmediateExpansionRange(
Loc);
315 Loc= ExpansionRange.getBegin();
316EndLoc = ExpansionRange.getEnd();
318 returnstd::make_pair(
Loc, EndLoc);
325 boolAcceptScratch =
true) {
327 return SM.getIncludeLoc(
SM.getFileID(
Loc));
328 Loc=
SM.getImmediateExpansionRange(
Loc).getBegin();
331 returngetNonScratchExpansionLoc(
Loc).first;
336 return SM.getBufferName(
SM.getSpellingLoc(
Loc)) ==
"<built-in>";
342 Loc= getIncludeOrExpansionLoc(
Loc);
352 while(
SM.isMacroArgExpansion(
Loc) || isInBuiltin(
Loc))
353 Loc=
SM.getImmediateExpansionRange(
Loc).getBegin();
360 while(
SM.isMacroArgExpansion(
Loc) || isInBuiltin(
Loc))
361 Loc=
SM.getImmediateExpansionRange(
Loc).getBegin();
362 returngetPreciseTokenLocEnd(
Loc);
371FileIDMapping.clear();
373llvm::SmallSet<FileID, 8>
Visited;
375 for(
auto&Region : SourceRegions) {
379 autoNonScratchExpansionLoc = getNonScratchExpansionLoc(
Loc);
380 autoEndLoc = NonScratchExpansionLoc.second;
381 if(EndLoc.has_value()) {
382 Loc= NonScratchExpansionLoc.first;
383Region.setStartLoc(
Loc);
384Region.setEndLoc(EndLoc.value());
389 autoBeginLoc =
SM.getSpellingLoc(
Loc);
390 autoEndLoc =
SM.getSpellingLoc(Region.getEndLoc());
391 if(
SM.isWrittenInSameFile(BeginLoc, EndLoc)) {
393Region.setStartLoc(
Loc);
394Region.setEndLoc(
SM.getFileLoc(Region.getEndLoc()));
403!
SM.isInSystemHeader(
SM.getSpellingLoc(
Loc)));
409FileLocs.push_back(std::make_pair(
Loc, Depth));
411llvm::stable_sort(FileLocs, llvm::less_second());
413 for(
const auto&FL : FileLocs) {
415 FileIDSpellingFile =
SM.getDecomposedSpellingLoc(
Loc).first;
416 autoEntry =
SM.getFileEntryRefForID(SpellingFile);
420FileIDMapping[
SM.getFileID(
Loc)] = std::make_pair(Mapping.size(),
Loc);
421Mapping.push_back(CVM.
getFileID(*Entry));
429 autoMapping = FileIDMapping.find(
SM.getFileID(
Loc));
430 if(Mapping != FileIDMapping.end())
431 returnMapping->second.first;
445SpellingRegion SR{
SM, LocStart, LocEnd};
447 if(PrevTokLoc.
isValid() &&
SM.isWrittenInSameFile(LocStart, PrevTokLoc) &&
448SR.LineStart ==
SM.getSpellingLineNumber(PrevTokLoc))
450 if(NextTokLoc.
isValid() &&
SM.isWrittenInSameFile(LocEnd, NextTokLoc) &&
451SR.LineEnd ==
SM.getSpellingLineNumber(NextTokLoc)) {
455 if(SR.isInSourceOrder())
462 voidgatherSkippedRegions() {
466FileLineRanges.resize(
467FileIDMapping.size(),
468std::make_pair(std::numeric_limits<unsigned>::max(), 0));
469 for(
const auto&R : MappingRegions) {
470FileLineRanges[R.FileID].first =
471std::min(FileLineRanges[R.FileID].first, R.LineStart);
472FileLineRanges[R.FileID].second =
473std::max(FileLineRanges[R.FileID].second, R.LineEnd);
477 for(
auto&I : SkippedRanges) {
481assert(
SM.isWrittenInSameFile(LocStart, LocEnd) &&
482 "region spans multiple files");
484 autoCovFileID = getCoverageFileID(LocStart);
487std::optional<SpellingRegion> SR;
489SR = adjustSkippedRange(
SM, LocStart, LocEnd, I.PrevTokLoc,
491 else if(I.isPPIfElse() || I.isEmptyLine())
492SR = {
SM, LocStart, LocEnd};
496 autoRegion = CounterMappingRegion::makeSkipped(
497*CovFileID, SR->LineStart, SR->ColumnStart, SR->LineEnd,
501 if(Region.LineStart >= FileLineRanges[*CovFileID].first &&
502Region.LineEnd <= FileLineRanges[*CovFileID].second)
503MappingRegions.push_back(Region);
509 voidemitSourceRegions(
constSourceRegionFilter &Filter) {
510 for(
const auto&Region : SourceRegions) {
511assert(Region.hasEndLoc() &&
"incomplete region");
514assert(
SM.getFileID(LocStart).isValid() &&
"region in invalid file");
519 SM.isInSystemHeader(
SM.getSpellingLoc(LocStart))) {
520assert(!Region.isMCDCBranch() && !Region.isMCDCDecision() &&
521 "Don't suppress the condition in system headers");
525 autoCovFileID = getCoverageFileID(LocStart);
528assert(!Region.isMCDCBranch() && !Region.isMCDCDecision() &&
529 "Don't suppress the condition in non-file regions");
534assert(
SM.isWrittenInSameFile(LocStart, LocEnd) &&
535 "region spans multiple files");
541 if(
Filter.count(std::make_pair(LocStart, LocEnd))) {
542assert(!Region.isMCDCBranch() && !Region.isMCDCDecision() &&
543 "Don't suppress the condition");
548SpellingRegion SR{
SM, LocStart, LocEnd};
549assert(SR.isInSourceOrder() &&
"region start and end out of order");
551 if(Region.isGap()) {
552MappingRegions.push_back(CounterMappingRegion::makeGapRegion(
553Region.getCounter(), *CovFileID, SR.LineStart, SR.ColumnStart,
554SR.LineEnd, SR.ColumnEnd));
555}
else if(Region.isSkipped()) {
556MappingRegions.push_back(CounterMappingRegion::makeSkipped(
557*CovFileID, SR.LineStart, SR.ColumnStart, SR.LineEnd,
559}
else if(Region.isBranch()) {
560MappingRegions.push_back(CounterMappingRegion::makeBranchRegion(
561Region.getCounter(), Region.getFalseCounter(), *CovFileID,
562SR.LineStart, SR.ColumnStart, SR.LineEnd, SR.ColumnEnd,
563Region.getMCDCParams()));
564}
else if(Region.isMCDCDecision()) {
565MappingRegions.push_back(CounterMappingRegion::makeDecisionRegion(
566Region.getMCDCDecisionParams(), *CovFileID, SR.LineStart,
567SR.ColumnStart, SR.LineEnd, SR.ColumnEnd));
569MappingRegions.push_back(CounterMappingRegion::makeRegion(
570Region.getCounter(), *CovFileID, SR.LineStart, SR.ColumnStart,
571SR.LineEnd, SR.ColumnEnd));
577SourceRegionFilter emitExpansionRegions() {
578SourceRegionFilter
Filter;
579 for(
const auto&FM : FileIDMapping) {
581 SourceLocationParentLoc = getIncludeOrExpansionLoc(ExpandedLoc,
false);
585 autoParentFileID = getCoverageFileID(ParentLoc);
588 autoExpandedFileID = getCoverageFileID(ExpandedLoc);
589assert(ExpandedFileID &&
"expansion in uncovered file");
592assert(
SM.isWrittenInSameFile(ParentLoc, LocEnd) &&
593 "region spans multiple files");
594 Filter.insert(std::make_pair(ParentLoc, LocEnd));
596SpellingRegion SR{
SM, ParentLoc, LocEnd};
597assert(SR.isInSourceOrder() &&
"region start and end out of order");
598MappingRegions.push_back(CounterMappingRegion::makeExpansion(
599*ParentFileID, *ExpandedFileID, SR.LineStart, SR.ColumnStart,
600SR.LineEnd, SR.ColumnEnd));
608structEmptyCoverageMappingBuilder :
publicCoverageMappingBuilder {
611: CoverageMappingBuilder(CVM,
SM, LangOpts) {}
613 voidVisitDecl(
const Decl*
D) {
619 if(!
SM.isWrittenInSameFile(Start, End)) {
622 FileIDStartFileID =
SM.getFileID(Start);
623 FileIDEndFileID =
SM.getFileID(End);
624 while(StartFileID != EndFileID && !isNestedIn(End, StartFileID)) {
625Start = getIncludeOrExpansionLoc(Start);
627 "Declaration start location not nested within a known region");
628StartFileID =
SM.getFileID(Start);
630 while(StartFileID != EndFileID) {
631End = getPreciseTokenLocEnd(getIncludeOrExpansionLoc(End));
632assert(End.isValid() &&
633 "Declaration end location not nested within a known region");
634EndFileID =
SM.getFileID(End);
637SourceRegions.emplace_back(Counter(), Start, End);
641 voidwrite(llvm::raw_ostream &OS) {
643gatherFileIDs(FileIDMapping);
644emitSourceRegions(SourceRegionFilter());
646 if(MappingRegions.empty())
649CoverageMappingWriter Writer(FileIDMapping, {}, MappingRegions);
662structMCDCCoverageBuilder {
756 const Stmt*DecisionStmt =
nullptr;
757mcdc::ConditionID NextID = 0;
758 boolNotMapped =
false;
762 static constexprmcdc::ConditionIDs DecisionStackSentinel{-1, -1};
766 return E->getOpcode() == BO_LAnd;
771: CGM(CGM), DecisionStack(1, DecisionStackSentinel),
772MCDCState(MCDCState) {}
777 boolisIdle()
const{
return(NextID == 0 && !NotMapped); }
782 boolisBuilding()
const{
return(NextID > 0); }
785 voidsetCondID(
const Expr*Cond, mcdc::ConditionID
ID) {
786MCDCState.
BranchByStmt[CodeGenFunction::stripCond(Cond)] = {
ID,
791mcdc::ConditionID getCondID(
const Expr*Cond)
const{
792 autoI = MCDCState.
BranchByStmt.find(CodeGenFunction::stripCond(Cond));
800 constmcdc::ConditionIDs &back()
const{
returnDecisionStack.back(); }
823 constmcdc::ConditionIDs &ParentDecision = DecisionStack.back();
828 if(MCDCState.
BranchByStmt.contains(CodeGenFunction::stripCond(
E)))
829setCondID(
E->getLHS(), getCondID(
E));
831setCondID(
E->getLHS(), NextID++);
834mcdc::ConditionID RHSid = NextID++;
835setCondID(
E->getRHS(), RHSid);
839DecisionStack.push_back({ParentDecision[
false], RHSid});
841DecisionStack.push_back({RHSid, ParentDecision[
true]});
845mcdc::ConditionIDs pop() {
847 returnDecisionStackSentinel;
849assert(DecisionStack.size() > 1);
850 returnDecisionStack.pop_back_val();
860assert(DecisionStack.size() == 1);
870 unsignedTotalConds = NextID;
881structCounterCoverageMappingBuilder
882:
publicCoverageMappingBuilder,
885llvm::DenseMap<const Stmt *, CounterPair> &CounterMap;
894llvm::DenseSet<const Stmt *> LeafExprSet;
897MCDCCoverageBuilder MCDCBuilder;
899CounterExpressionBuilder Builder;
908 boolHasTerminateStmt =
false;
911Counter GapRegionCounter;
914Counter subtractCounters(Counter LHS, Counter RHS,
boolSimplify =
true) {
916 "cannot add counters when single byte coverage mode is enabled");
917 returnBuilder.subtract(LHS, RHS, Simplify);
921Counter addCounters(Counter LHS, Counter RHS,
boolSimplify =
true) {
922 returnBuilder.add(LHS, RHS, Simplify);
925Counter addCounters(Counter C1, Counter C2, Counter C3,
926 boolSimplify =
true) {
927 returnaddCounters(addCounters(C1, C2, Simplify), C3, Simplify);
933Counter getRegionCounter(
const Stmt*S) {
934 returnCounter::getCounter(CounterMap[S].Executed);
937 structBranchCounterPair {
959getBranchCounterPair(
const Stmt*S, Counter ParentCnt,
960std::optional<Counter> SkipCntForOld = std::nullopt) {
961Counter ExecCnt = getRegionCounter(S);
966assert(SkipCntForOld &&
967 "SingleByte must provide SkipCntForOld as a fake Skipped count.");
968 return{ExecCnt, *SkipCntForOld};
971 return{ExecCnt, Builder.subtract(ParentCnt, ExecCnt)};
974 boolIsCounterEqual(Counter OutCount, Counter ParentCount) {
975 if(OutCount == ParentCount)
985 size_tpushRegion(Counter Count,
986std::optional<SourceLocation> StartLoc = std::nullopt,
987std::optional<SourceLocation> EndLoc = std::nullopt,
988std::optional<Counter> FalseCount = std::nullopt,
989 constmcdc::Parameters &BranchParams = std::monostate()) {
991 if(StartLoc && !FalseCount) {
992MostRecentLocation = *StartLoc;
997assert((!StartLoc || StartLoc->isValid()) &&
"Start location is not valid");
998assert((!EndLoc || EndLoc->isValid()) &&
"End location is not valid");
1004 if(StartLoc && StartLoc->isInvalid())
1005StartLoc = std::nullopt;
1006 if(EndLoc && EndLoc->isInvalid())
1007EndLoc = std::nullopt;
1008RegionStack.emplace_back(Count, FalseCount, BranchParams, StartLoc, EndLoc);
1010 returnRegionStack.size() - 1;
1013 size_tpushRegion(
constmcdc::DecisionParameters &DecisionParams,
1014std::optional<SourceLocation> StartLoc = std::nullopt,
1015std::optional<SourceLocation> EndLoc = std::nullopt) {
1017RegionStack.emplace_back(DecisionParams, StartLoc, EndLoc);
1019 returnRegionStack.size() - 1;
1025 Loc= getIncludeOrExpansionLoc(
Loc);
1035 voidpopRegions(
size_tParentIndex) {
1036assert(RegionStack.size() >= ParentIndex &&
"parent not in stack");
1037 while(RegionStack.size() > ParentIndex) {
1038SourceMappingRegion &Region = RegionStack.back();
1039 if(Region.hasStartLoc() &&
1040(Region.hasEndLoc() || RegionStack[ParentIndex].hasEndLoc())) {
1043? Region.getEndLoc()
1044: RegionStack[ParentIndex].getEndLoc();
1045 boolisBranch = Region.isBranch();
1046 size_tStartDepth = locationDepth(StartLoc);
1047 size_tEndDepth = locationDepth(EndLoc);
1048 while(!
SM.isWrittenInSameFile(StartLoc, EndLoc)) {
1049 boolUnnestStart = StartDepth >= EndDepth;
1050 boolUnnestEnd = EndDepth >= StartDepth;
1059assert(
SM.isWrittenInSameFile(NestedLoc, EndLoc));
1061 if(!isBranch && !isRegionAlreadyAdded(NestedLoc, EndLoc))
1062SourceRegions.emplace_back(Region.getCounter(), NestedLoc,
1065EndLoc = getPreciseTokenLocEnd(getIncludeOrExpansionLoc(EndLoc));
1067llvm::report_fatal_error(
1068 "File exit not handled before popRegions");
1079assert(
SM.isWrittenInSameFile(StartLoc, NestedLoc));
1081 if(!isBranch && !isRegionAlreadyAdded(StartLoc, NestedLoc))
1082SourceRegions.emplace_back(Region.getCounter(), StartLoc,
1085StartLoc = getIncludeOrExpansionLoc(StartLoc);
1087llvm::report_fatal_error(
1088 "File exit not handled before popRegions");
1092Region.setStartLoc(StartLoc);
1093Region.setEndLoc(EndLoc);
1096MostRecentLocation = EndLoc;
1099 if(StartLoc == getStartOfFileOrMacro(StartLoc) &&
1100EndLoc == getEndOfFileOrMacro(EndLoc))
1101MostRecentLocation = getIncludeOrExpansionLoc(EndLoc);
1104assert(
SM.isWrittenInSameFile(Region.getBeginLoc(), EndLoc));
1105assert(SpellingRegion(
SM, Region).isInSourceOrder());
1106SourceRegions.push_back(Region);
1108RegionStack.pop_back();
1114assert(!RegionStack.empty() &&
"statement has no region");
1115 returnRegionStack.back();
1120Counter propagateCounts(Counter TopCount,
const Stmt*S,
1121 boolVisitChildren =
true) {
1124 size_tIndex = pushRegion(TopCount, StartLoc, EndLoc);
1127Counter ExitCount =
getRegion().getCounter();
1132 if(
SM.isBeforeInTranslationUnit(StartLoc, S->getBeginLoc()))
1133MostRecentLocation = EndLoc;
1142 voidcreateBranchRegion(
const Expr*
C, Counter TrueCnt, Counter FalseCnt,
1143 constmcdc::ConditionIDs &Conds = {}) {
1154 if(CodeGenFunction::isInstrumentedCondition(
C) ||
1155LeafExprSet.count(CodeGenFunction::stripCond(
C))) {
1156mcdc::Parameters BranchParams;
1157mcdc::ConditionID
ID= MCDCBuilder.getCondID(
C);
1159BranchParams = mcdc::BranchParameters{
ID, Conds};
1169 if(Result.Val.getInt().getBoolValue())
1170FalseCnt = Counter::getZero();
1172TrueCnt = Counter::getZero();
1175pushRegion(TrueCnt, getStart(
C), getEnd(
C), FalseCnt, BranchParams));
1182 voidcreateDecisionRegion(
const Expr*
C,
1183 constmcdc::DecisionParameters &DecisionParams) {
1184popRegions(pushRegion(DecisionParams, getStart(
C), getEnd(
C)));
1190Counter createSwitchCaseRegion(
const SwitchCase*SC, Counter ParentCount) {
1194Counter TrueCnt = getRegionCounter(SC);
1195popRegions(pushRegion(TrueCnt, getStart(SC), SC->
getColonLoc(),
1196subtractCounters(ParentCount, TrueCnt)));
1203 boolisBranch =
false) {
1204 returnllvm::any_of(
1205llvm::reverse(SourceRegions), [&](
constSourceMappingRegion &Region) {
1206 returnRegion.getBeginLoc() == StartLoc &&
1207Region.getEndLoc() == EndLoc && Region.isBranch() == isBranch;
1215MostRecentLocation = EndLoc;
1222MostRecentLocation == getEndOfFileOrMacro(MostRecentLocation) &&
1223isRegionAlreadyAdded(getStartOfFileOrMacro(MostRecentLocation),
1224MostRecentLocation,
getRegion().isBranch()))
1225MostRecentLocation = getIncludeOrExpansionLoc(MostRecentLocation);
1235 SM.isWrittenInSameFile(MostRecentLocation, NewLoc))
1241 FileIDParentFile =
SM.getFileID(LCA);
1242 while(!isNestedIn(MostRecentLocation, ParentFile)) {
1243LCA = getIncludeOrExpansionLoc(LCA);
1244 if(LCA.
isInvalid() ||
SM.isWrittenInSameFile(LCA, MostRecentLocation)) {
1247MostRecentLocation = NewLoc;
1250ParentFile =
SM.getFileID(LCA);
1253llvm::SmallSet<SourceLocation, 8> StartLocs;
1254std::optional<Counter> ParentCounter;
1255 for(SourceMappingRegion &I : llvm::reverse(RegionStack)) {
1256 if(!I.hasStartLoc())
1259 if(!isNestedIn(
Loc, ParentFile)) {
1260ParentCounter = I.getCounter();
1264 while(!
SM.isInFileID(
Loc, ParentFile)) {
1268 if(StartLocs.insert(
Loc).second) {
1270SourceRegions.emplace_back(I.getCounter(), I.getFalseCounter(),
1271I.getMCDCParams(),
Loc,
1272getEndOfFileOrMacro(
Loc), I.isBranch());
1274SourceRegions.emplace_back(I.getCounter(),
Loc,
1275getEndOfFileOrMacro(
Loc));
1277 Loc= getIncludeOrExpansionLoc(
Loc);
1279I.setStartLoc(getPreciseTokenLocEnd(
Loc));
1282 if(ParentCounter) {
1287 while(isNestedIn(
Loc, ParentFile)) {
1289 if(StartLocs.insert(FileStart).second) {
1290SourceRegions.emplace_back(*ParentCounter, FileStart,
1291getEndOfFileOrMacro(
Loc));
1292assert(SpellingRegion(
SM, SourceRegions.back()).isInSourceOrder());
1294 Loc= getIncludeOrExpansionLoc(
Loc);
1298MostRecentLocation = NewLoc;
1302 voidextendRegion(
const Stmt*S) {
1303SourceMappingRegion &Region =
getRegion();
1306handleFileExit(StartLoc);
1307 if(!Region.hasStartLoc())
1308Region.setStartLoc(StartLoc);
1312 voidterminateRegion(
const Stmt*S) {
1314SourceMappingRegion &Region =
getRegion();
1316 if(!Region.hasEndLoc())
1317Region.setEndLoc(EndLoc);
1318pushRegion(Counter::getZero());
1319HasTerminateStmt =
true;
1323std::optional<SourceRange> findGapAreaBetween(
SourceLocationAfterLoc,
1329 returnstd::nullopt;
1334 FileIDFID =
SM.getFileID(AfterLoc);
1340 size_tStartDepth = locationDepth(AfterLoc);
1341 size_tEndDepth = locationDepth(BeforeLoc);
1342 while(!
SM.isWrittenInSameFile(AfterLoc, BeforeLoc)) {
1343 boolUnnestStart = StartDepth >= EndDepth;
1344 boolUnnestEnd = EndDepth >= StartDepth;
1346assert(
SM.isWrittenInSameFile(getStartOfFileOrMacro(BeforeLoc),
1349BeforeLoc = getIncludeOrExpansionLoc(BeforeLoc);
1350assert(BeforeLoc.
isValid());
1354assert(
SM.isWrittenInSameFile(AfterLoc,
1355getEndOfFileOrMacro(AfterLoc)));
1357AfterLoc = getIncludeOrExpansionLoc(AfterLoc);
1359AfterLoc = getPreciseTokenLocEnd(AfterLoc);
1364AfterLoc = getPreciseTokenLocEnd(AfterLoc);
1368 returnstd::nullopt;
1369 if(!
SM.isWrittenInSameFile(AfterLoc, BeforeLoc) ||
1370!SpellingRegion(
SM, AfterLoc, BeforeLoc).isInSourceOrder())
1371 returnstd::nullopt;
1372 return{{AfterLoc, BeforeLoc}};
1378 if(StartLoc == EndLoc)
1380assert(SpellingRegion(
SM, StartLoc, EndLoc).isInSourceOrder());
1381handleFileExit(StartLoc);
1382 size_tIndex = pushRegion(Count, StartLoc, EndLoc);
1384handleFileExit(EndLoc);
1390std::optional<SourceRange> findAreaStartingFromTo(
SourceLocationStartingLoc,
1394 FileIDFID =
SM.getFileID(StartingLoc);
1400 size_tStartDepth = locationDepth(StartingLoc);
1401 size_tEndDepth = locationDepth(BeforeLoc);
1402 while(!
SM.isWrittenInSameFile(StartingLoc, BeforeLoc)) {
1403 boolUnnestStart = StartDepth >= EndDepth;
1404 boolUnnestEnd = EndDepth >= StartDepth;
1406assert(
SM.isWrittenInSameFile(getStartOfFileOrMacro(BeforeLoc),
1409BeforeLoc = getIncludeOrExpansionLoc(BeforeLoc);
1410assert(BeforeLoc.
isValid());
1414assert(
SM.isWrittenInSameFile(StartingLoc,
1415getStartOfFileOrMacro(StartingLoc)));
1417StartingLoc = getIncludeOrExpansionLoc(StartingLoc);
1418assert(StartingLoc.
isValid());
1425 returnstd::nullopt;
1426 if(!
SM.isWrittenInSameFile(StartingLoc, BeforeLoc) ||
1427!SpellingRegion(
SM, StartingLoc, BeforeLoc).isInSourceOrder())
1428 returnstd::nullopt;
1429 return{{StartingLoc, BeforeLoc}};
1433 const autoSkipped = findAreaStartingFromTo(StartLoc, BeforeLoc);
1438 const autoNewStartLoc = Skipped->getBegin();
1439 const autoEndLoc = Skipped->getEnd();
1441 if(NewStartLoc == EndLoc)
1443assert(SpellingRegion(
SM, NewStartLoc, EndLoc).isInSourceOrder());
1444handleFileExit(NewStartLoc);
1445 size_tIndex = pushRegion(Counter{}, NewStartLoc, EndLoc);
1447handleFileExit(EndLoc);
1452 structBreakContinue {
1454Counter ContinueCount;
1458CounterCoverageMappingBuilder(
1460llvm::DenseMap<const Stmt *, CounterPair> &CounterMap,
1462: CoverageMappingBuilder(CVM,
SM, LangOpts), CounterMap(CounterMap),
1463MCDCState(MCDCState), MCDCBuilder(CVM.getCodeGenModule(), MCDCState) {}
1466 voidwrite(llvm::raw_ostream &OS) {
1468gatherFileIDs(VirtualFileMapping);
1469SourceRegionFilter
Filter= emitExpansionRegions();
1470emitSourceRegions(Filter);
1471gatherSkippedRegions();
1473 if(MappingRegions.empty())
1476CoverageMappingWriter Writer(VirtualFileMapping, Builder.getExpressions(),
1481 voidVisitStmt(
const Stmt*S) {
1482 if(S->getBeginLoc().isValid())
1484 const Stmt*LastStmt =
nullptr;
1485 boolSaveTerminateStmt = HasTerminateStmt;
1486HasTerminateStmt =
false;
1487GapRegionCounter = Counter::getZero();
1488 for(
const Stmt*Child : S->children())
1492 if(LastStmt && HasTerminateStmt) {
1493 autoGap = findGapAreaBetween(getEnd(LastStmt), getStart(Child));
1495fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(),
1497SaveTerminateStmt =
true;
1498HasTerminateStmt =
false;
1503 if(SaveTerminateStmt)
1504HasTerminateStmt =
true;
1505handleFileExit(getEnd(S));
1508 voidVisitDecl(
const Decl*
D) {
1514 SM.isInSystemHeader(
SM.getSpellingLoc(getStart(Body))))
1521Counter BodyCounter = getRegionCounter(Body);
1523 if(
auto*Method = dyn_cast<CXXMethodDecl>(
D))
1525 if(
auto*Ctor = dyn_cast<CXXConstructorDecl>(
D)) {
1529 if(getStart(
Init).isValid() && getEnd(
Init).isValid())
1530propagateCounts(BodyCounter,
Init);
1535propagateCounts(BodyCounter, Body,
1537assert(RegionStack.empty() &&
"Regions entered but never exited");
1542 if(S->getRetValue())
1543Visit(S->getRetValue());
1549Visit(S->getBody());
1554 if(S->getOperand())
1555Visit(S->getOperand());
1560Visit(
E->getOperand());
1565 if(
E->getSubExpr())
1566Visit(
E->getSubExpr());
1567terminateRegion(
E);
1570 voidVisitGotoStmt(
const GotoStmt*S) { terminateRegion(S); }
1572 voidVisitLabelStmt(
const LabelStmt*S) {
1573Counter LabelCount = getRegionCounter(S);
1576handleFileExit(Start);
1577pushRegion(LabelCount, Start);
1578Visit(S->getSubStmt());
1581 voidVisitBreakStmt(
const BreakStmt*S) {
1582assert(!BreakContinueStack.empty() &&
"break not in a loop or switch!");
1584BreakContinueStack.back().BreakCount = addCounters(
1585BreakContinueStack.back().BreakCount,
getRegion().getCounter());
1592assert(!BreakContinueStack.empty() &&
"continue stmt not in a loop!");
1594BreakContinueStack.back().ContinueCount = addCounters(
1595BreakContinueStack.back().ContinueCount,
getRegion().getCounter());
1599 voidVisitCallExpr(
const CallExpr*
E) {
1606terminateRegion(
E);
1609 voidVisitWhileStmt(
const WhileStmt*S) {
1612Counter ParentCount =
getRegion().getCounter();
1614? getRegionCounter(S->getBody())
1615: getRegionCounter(S);
1618BreakContinueStack.push_back(BreakContinue());
1619extendRegion(S->getBody());
1620Counter BackedgeCount = propagateCounts(BodyCount, S->getBody());
1621BreakContinue BC = BreakContinueStack.pop_back_val();
1623 boolBodyHasTerminateStmt = HasTerminateStmt;
1624HasTerminateStmt =
false;
1629? getRegionCounter(S->getCond())
1630: addCounters(ParentCount, BackedgeCount, BC.ContinueCount);
1631 autoBranchCount = getBranchCounterPair(S, CondCount, getRegionCounter(S));
1632assert(BranchCount.Executed.isZero() || BranchCount.Executed == BodyCount ||
1635propagateCounts(CondCount, S->getCond());
1636adjustForOutOfOrderTraversal(getEnd(S));
1639 autoGap = findGapAreaBetween(S->getRParenLoc(), getStart(S->getBody()));
1641fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(), BodyCount);
1645(BC.BreakCount.isZero() && BranchCount.Skipped == getRegionCounter(S)));
1646Counter OutCount = addCounters(BC.BreakCount, BranchCount.Skipped);
1647 if(!IsCounterEqual(OutCount, ParentCount)) {
1648pushRegion(OutCount);
1649GapRegionCounter = OutCount;
1650 if(BodyHasTerminateStmt)
1651HasTerminateStmt =
true;
1656createBranchRegion(S->getCond(), BodyCount, BranchCount.Skipped);
1659 voidVisitDoStmt(
const DoStmt*S) {
1662Counter ParentCount =
getRegion().getCounter();
1664? getRegionCounter(S->getBody())
1665: getRegionCounter(S);
1667BreakContinueStack.push_back(BreakContinue());
1668extendRegion(S->getBody());
1670Counter BackedgeCount;
1672propagateCounts(BodyCount, S->getBody());
1675propagateCounts(addCounters(ParentCount, BodyCount), S->getBody());
1677BreakContinue BC = BreakContinueStack.pop_back_val();
1679 boolBodyHasTerminateStmt = HasTerminateStmt;
1680HasTerminateStmt =
false;
1683? getRegionCounter(S->getCond())
1684: addCounters(BackedgeCount, BC.ContinueCount);
1685 autoBranchCount = getBranchCounterPair(S, CondCount, getRegionCounter(S));
1686assert(BranchCount.Executed.isZero() || BranchCount.Executed == BodyCount ||
1689propagateCounts(CondCount, S->getCond());
1693(BC.BreakCount.isZero() && BranchCount.Skipped == getRegionCounter(S)));
1694Counter OutCount = addCounters(BC.BreakCount, BranchCount.Skipped);
1695 if(!IsCounterEqual(OutCount, ParentCount)) {
1696pushRegion(OutCount);
1697GapRegionCounter = OutCount;
1702createBranchRegion(S->getCond(), BodyCount, BranchCount.Skipped);
1704 if(BodyHasTerminateStmt)
1705HasTerminateStmt =
true;
1708 voidVisitForStmt(
const ForStmt*S) {
1711Visit(S->getInit());
1713Counter ParentCount =
getRegion().getCounter();
1715? getRegionCounter(S->getBody())
1716: getRegionCounter(S);
1720BreakContinueStack.emplace_back();
1723BreakContinueStack.emplace_back();
1724extendRegion(S->getBody());
1725Counter BackedgeCount = propagateCounts(BodyCount, S->getBody());
1726BreakContinue BodyBC = BreakContinueStack.pop_back_val();
1728 boolBodyHasTerminateStmt = HasTerminateStmt;
1729HasTerminateStmt =
false;
1733BreakContinue IncrementBC;
1734 if(
const Stmt*Inc = S->getInc()) {
1737IncCount = getRegionCounter(S->getInc());
1739IncCount = addCounters(BackedgeCount, BodyBC.ContinueCount);
1740propagateCounts(IncCount, Inc);
1741IncrementBC = BreakContinueStack.pop_back_val();
1747? getRegionCounter(S->getCond())
1749addCounters(ParentCount, BackedgeCount, BodyBC.ContinueCount),
1750IncrementBC.ContinueCount);
1751 autoBranchCount = getBranchCounterPair(S, CondCount, getRegionCounter(S));
1752assert(BranchCount.Executed.isZero() || BranchCount.Executed == BodyCount ||
1755 if(
const Expr*Cond = S->getCond()) {
1756propagateCounts(CondCount, Cond);
1757adjustForOutOfOrderTraversal(getEnd(S));
1761 autoGap = findGapAreaBetween(S->getRParenLoc(), getStart(S->getBody()));
1763fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(), BodyCount);
1766(BodyBC.BreakCount.isZero() && IncrementBC.BreakCount.isZero()));
1767Counter OutCount = addCounters(BodyBC.BreakCount, IncrementBC.BreakCount,
1768BranchCount.Skipped);
1769 if(!IsCounterEqual(OutCount, ParentCount)) {
1770pushRegion(OutCount);
1771GapRegionCounter = OutCount;
1772 if(BodyHasTerminateStmt)
1773HasTerminateStmt =
true;
1778createBranchRegion(S->getCond(), BodyCount, BranchCount.Skipped);
1784Visit(S->getInit());
1785Visit(S->getLoopVarStmt());
1786Visit(S->getRangeStmt());
1788Counter ParentCount =
getRegion().getCounter();
1790? getRegionCounter(S->getBody())
1791: getRegionCounter(S);
1793BreakContinueStack.push_back(BreakContinue());
1794extendRegion(S->getBody());
1795Counter BackedgeCount = propagateCounts(BodyCount, S->getBody());
1796BreakContinue BC = BreakContinueStack.pop_back_val();
1798 boolBodyHasTerminateStmt = HasTerminateStmt;
1799HasTerminateStmt =
false;
1802 autoGap = findGapAreaBetween(S->getRParenLoc(), getStart(S->getBody()));
1804fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(), BodyCount);
1807addCounters(ParentCount, BackedgeCount, BC.ContinueCount);
1808 autoBranchCount = getBranchCounterPair(S, LoopCount, getRegionCounter(S));
1809assert(BranchCount.Executed.isZero() || BranchCount.Executed == BodyCount ||
1813(BC.BreakCount.isZero() && BranchCount.Skipped == getRegionCounter(S)));
1815Counter OutCount = addCounters(BC.BreakCount, BranchCount.Skipped);
1816 if(!IsCounterEqual(OutCount, ParentCount)) {
1817pushRegion(OutCount);
1818GapRegionCounter = OutCount;
1819 if(BodyHasTerminateStmt)
1820HasTerminateStmt =
true;
1825createBranchRegion(S->getCond(), BodyCount, BranchCount.Skipped);
1830Visit(S->getElement());
1832Counter ParentCount =
getRegion().getCounter();
1833Counter BodyCount = getRegionCounter(S);
1835BreakContinueStack.push_back(BreakContinue());
1836extendRegion(S->getBody());
1837Counter BackedgeCount = propagateCounts(BodyCount, S->getBody());
1838BreakContinue BC = BreakContinueStack.pop_back_val();
1841 autoGap = findGapAreaBetween(S->getRParenLoc(), getStart(S->getBody()));
1843fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(), BodyCount);
1846addCounters(ParentCount, BackedgeCount, BC.ContinueCount);
1847 autoBranchCount = getBranchCounterPair(S, LoopCount);
1848assert(BranchCount.Executed.isZero() || BranchCount.Executed == BodyCount);
1849Counter OutCount = addCounters(BC.BreakCount, BranchCount.Skipped);
1850 if(!IsCounterEqual(OutCount, ParentCount)) {
1851pushRegion(OutCount);
1852GapRegionCounter = OutCount;
1859Visit(S->getInit());
1860Visit(S->getCond());
1862BreakContinueStack.push_back(BreakContinue());
1864 const Stmt*Body = S->getBody();
1866 if(
const auto*CS = dyn_cast<CompoundStmt>(Body)) {
1867 if(!CS->body_empty()) {
1871 size_tIndex = pushRegion(Counter::getZero(), getStart(CS));
1876 for(
size_ti = RegionStack.size(); i != Index; --i) {
1877 if(!RegionStack[i - 1].hasEndLoc())
1878RegionStack[i - 1].setEndLoc(getEnd(CS->body_back()));
1884propagateCounts(Counter::getZero(), Body);
1885BreakContinue BC = BreakContinueStack.pop_back_val();
1888BreakContinueStack.back().ContinueCount = addCounters(
1889BreakContinueStack.back().ContinueCount, BC.ContinueCount);
1891Counter ParentCount =
getRegion().getCounter();
1892Counter ExitCount = getRegionCounter(S);
1894pushRegion(ExitCount);
1895GapRegionCounter = ExitCount;
1899MostRecentLocation = getStart(S);
1900handleFileExit(ExitLoc);
1909Counter CaseCountSum;
1910 boolHasDefaultCase =
false;
1911 const SwitchCase*Case = S->getSwitchCaseList();
1913HasDefaultCase = HasDefaultCase || isa<DefaultStmt>(Case);
1914 autoCaseCount = createSwitchCaseRegion(Case, ParentCount);
1915CaseCountSum = addCounters(CaseCountSum, CaseCount,
false);
1920 if(!HasDefaultCase) {
1925addCounters(CaseCountSum, Counter::getZero(),
true);
1928Counter SwitchFalse = subtractCounters(ParentCount, CaseCountSum);
1929createBranchRegion(S->getCond(), CaseCountSum, SwitchFalse);
1938? getRegionCounter(S)
1939: addCounters(
Parent.getCounter(), getRegionCounter(S));
1943 if(
Parent.hasStartLoc() &&
Parent.getBeginLoc() == getStart(S))
1944 Parent.setCounter(Count);
1946pushRegion(Count, getStart(S));
1948GapRegionCounter = Count;
1950 if(
const auto*CS = dyn_cast<CaseStmt>(S)) {
1951Visit(CS->getLHS());
1952 if(
const Expr*RHS = CS->getRHS())
1955Visit(S->getSubStmt());
1958 voidcoverIfConsteval(
const IfStmt*S) {
1959assert(S->isConsteval());
1961 const auto*Then = S->getThen();
1962 const auto*Else = S->getElse();
1967 constCounter ParentCount =
getRegion().getCounter();
1971 if(S->isNegatedConsteval()) {
1973markSkipped(S->getIfLoc(), getStart(Then));
1974propagateCounts(ParentCount, Then);
1978markSkipped(getEnd(Then), getEnd(Else));
1981assert(S->isNonNegatedConsteval());
1983markSkipped(S->getIfLoc(), Else ? getStart(Else) : getEnd(Then));
1986propagateCounts(ParentCount, Else);
1990 voidcoverIfConstexpr(
const IfStmt*S) {
1991assert(S->isConstexpr());
2003 constCounter ParentCount =
getRegion().getCounter();
2008 if(
const auto*
Init= S->getInit()) {
2009 const autostart = getStart(
Init);
2010 const autoend = getEnd(
Init);
2014 if(start.isValid() && end.isValid()) {
2015markSkipped(startOfSkipped, start);
2016propagateCounts(ParentCount,
Init);
2017startOfSkipped = getEnd(
Init);
2021 const auto*Then = S->getThen();
2022 const auto*Else = S->getElse();
2026markSkipped(startOfSkipped, getStart(Then));
2027propagateCounts(ParentCount, Then);
2031markSkipped(getEnd(Then), getEnd(Else));
2034markSkipped(startOfSkipped, Else ? getStart(Else) : getEnd(Then));
2037propagateCounts(ParentCount, Else);
2041 voidVisitIfStmt(
const IfStmt*S) {
2044 if(S->isConsteval())
2045 returncoverIfConsteval(S);
2046 else if(S->isConstexpr())
2047 returncoverIfConstexpr(S);
2051Visit(S->getInit());
2055extendRegion(S->getCond());
2057Counter ParentCount =
getRegion().getCounter();
2058 auto[ThenCount, ElseCount] =
2060? BranchCounterPair{getRegionCounter(S->getThen()),
2061(S->getElse() ? getRegionCounter(S->getElse())
2062: Counter::getZero())}
2063: getBranchCounterPair(S, ParentCount));
2067propagateCounts(ParentCount, S->getCond());
2070std::optional<SourceRange> Gap =
2071findGapAreaBetween(S->getRParenLoc(), getStart(S->getThen()));
2073fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(), ThenCount);
2075extendRegion(S->getThen());
2076Counter OutCount = propagateCounts(ThenCount, S->getThen());
2078 if(
const Stmt*Else = S->getElse()) {
2079 boolThenHasTerminateStmt = HasTerminateStmt;
2080HasTerminateStmt =
false;
2082std::optional<SourceRange> Gap =
2083findGapAreaBetween(getEnd(S->getThen()), getStart(Else));
2085fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(), ElseCount);
2088Counter ElseOutCount = propagateCounts(ElseCount, Else);
2090OutCount = addCounters(OutCount, ElseOutCount);
2092 if(ThenHasTerminateStmt)
2093HasTerminateStmt =
true;
2095OutCount = addCounters(OutCount, ElseCount);
2098OutCount = getRegionCounter(S);
2100 if(!IsCounterEqual(OutCount, ParentCount)) {
2101pushRegion(OutCount);
2102GapRegionCounter = OutCount;
2107createBranchRegion(S->getCond(), ThenCount, ElseCount);
2113extendRegion(S->getTryBlock());
2115Counter ParentCount =
getRegion().getCounter();
2116propagateCounts(ParentCount, S->getTryBlock());
2118 for(
unsignedI = 0,
E= S->getNumHandlers(); I <
E; ++I)
2119Visit(S->getHandler(I));
2121Counter ExitCount = getRegionCounter(S);
2122pushRegion(ExitCount);
2126propagateCounts(getRegionCounter(S), S->getHandlerBlock());
2132Counter ParentCount =
getRegion().getCounter();
2133 auto[TrueCount, FalseCount] =
2135? BranchCounterPair{getRegionCounter(
E->getTrueExpr()),
2136getRegionCounter(
E->getFalseExpr())}
2137: getBranchCounterPair(
E, ParentCount));
2140 if(
const auto*BCO = dyn_cast<BinaryConditionalOperator>(
E)) {
2141propagateCounts(ParentCount, BCO->getCommon());
2142OutCount = TrueCount;
2144propagateCounts(ParentCount,
E->getCond());
2147findGapAreaBetween(
E->getQuestionLoc(), getStart(
E->getTrueExpr()));
2149fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(), TrueCount);
2151extendRegion(
E->getTrueExpr());
2152OutCount = propagateCounts(TrueCount,
E->getTrueExpr());
2155extendRegion(
E->getFalseExpr());
2156Counter FalseOutCount = propagateCounts(FalseCount,
E->getFalseExpr());
2158OutCount = getRegionCounter(
E);
2160OutCount = addCounters(OutCount, FalseOutCount);
2162 if(!IsCounterEqual(OutCount, ParentCount)) {
2163pushRegion(OutCount);
2164GapRegionCounter = OutCount;
2169createBranchRegion(
E->getCond(), TrueCount, FalseCount);
2172 voidcreateOrCancelDecision(
const BinaryOperator*
E,
unsignedSince) {
2173 unsignedNumConds = MCDCBuilder.getTotalConditionsAndReset(
E);
2179 for(
const auto&SR :
ArrayRef(SourceRegions).slice(Since)) {
2180 if(SR.isMCDCBranch()) {
2181 auto[
ID, Conds] = SR.getMCDCBranchParams();
2182CondIDs[
ID] = Conds;
2187mcdc::TVIdxBuilder Builder(CondIDs);
2188 unsignedNumTVs = Builder.NumTestVectors;
2190assert(MaxTVs < mcdc::TVIdxBuilder::HardMaxTVs);
2192 if(NumTVs > MaxTVs) {
2194cancelDecision(
E, Since, NumTVs, MaxTVs);
2202std::move(Builder.Indices),
2205 autoDecisionParams = mcdc::DecisionParameters{
2211createDecisionRegion(
E, DecisionParams);
2215 voidcancelDecision(
const BinaryOperator*
E,
unsignedSince,
intNumTVs,
2220 "unsupported MC/DC boolean expression; " 2221 "number of test vectors (%0) exceeds max (%1). " 2222 "Expression will not be covered");
2227assert(!SR.isMCDCDecision() &&
"Decision shouldn't be seen here");
2228 if(SR.isMCDCBranch())
2229SR.resetMCDCParams();
2239 SM.isInSystemHeader(
SM.getSpellingLoc(
E->getOperatorLoc())) &&
2245 if(isExprInSystemHeader(
E)) {
2246LeafExprSet.insert(
E);
2250 boolIsRootNode = MCDCBuilder.isIdle();
2252 unsignedSourceRegionsSince = SourceRegions.size();
2255MCDCBuilder.pushAndAssignIDs(
E);
2257extendRegion(
E->getLHS());
2258propagateCounts(
getRegion().getCounter(),
E->getLHS());
2259handleFileExit(getEnd(
E->getLHS()));
2262 const autoDecisionLHS = MCDCBuilder.pop();
2265extendRegion(
E->getRHS());
2266propagateCounts(getRegionCounter(
E),
E->getRHS());
2272 const autoDecisionRHS = MCDCBuilder.back();
2275Counter ParentCnt =
getRegion().getCounter();
2278 auto[RHSExecCnt, LHSExitCnt] = getBranchCounterPair(
E, ParentCnt);
2281 auto[RHSTrueCnt, RHSExitCnt] =
2282getBranchCounterPair(
E->getRHS(), RHSExecCnt);
2285createBranchRegion(
E->getLHS(), RHSExecCnt, LHSExitCnt, DecisionLHS);
2288createBranchRegion(
E->getRHS(), RHSTrueCnt, RHSExitCnt, DecisionRHS);
2292createOrCancelDecision(
E, SourceRegionsSince);
2296 boolshouldVisitRHS(
const Expr*LHS) {
2297 boolLHSIsTrue =
false;
2298 boolLHSIsConst =
false;
2302 return!LHSIsConst || (LHSIsConst && !LHSIsTrue);
2306 if(isExprInSystemHeader(
E)) {
2307LeafExprSet.insert(
E);
2311 boolIsRootNode = MCDCBuilder.isIdle();
2313 unsignedSourceRegionsSince = SourceRegions.size();
2316MCDCBuilder.pushAndAssignIDs(
E);
2318extendRegion(
E->getLHS());
2319Counter OutCount = propagateCounts(
getRegion().getCounter(),
E->getLHS());
2320handleFileExit(getEnd(
E->getLHS()));
2323 const autoDecisionLHS = MCDCBuilder.pop();
2326extendRegion(
E->getRHS());
2327propagateCounts(getRegionCounter(
E),
E->getRHS());
2333 const autoDecisionRHS = MCDCBuilder.back();
2336Counter ParentCnt =
getRegion().getCounter();
2339 auto[RHSExecCnt, LHSExitCnt] = getBranchCounterPair(
E, ParentCnt);
2342 auto[RHSFalseCnt, RHSExitCnt] =
2343getBranchCounterPair(
E->getRHS(), RHSExecCnt);
2345 if(!shouldVisitRHS(
E->getLHS())) {
2346GapRegionCounter = OutCount;
2350createBranchRegion(
E->getLHS(), LHSExitCnt, RHSExecCnt, DecisionLHS);
2353createBranchRegion(
E->getRHS(), RHSExitCnt, RHSFalseCnt, DecisionRHS);
2357createOrCancelDecision(
E, SourceRegionsSince);
2360 voidVisitLambdaExpr(
const LambdaExpr*LE) {
2382static void dump(llvm::raw_ostream &OS, StringRef FunctionName,
2385OS << FunctionName <<
":\n";
2386CounterMappingContext Ctx(Expressions);
2387 for(
const auto&R : Regions) {
2390 caseCounterMappingRegion::CodeRegion:
2392 caseCounterMappingRegion::ExpansionRegion:
2393OS <<
"Expansion,";
2395 caseCounterMappingRegion::SkippedRegion:
2398 caseCounterMappingRegion::GapRegion:
2401 caseCounterMappingRegion::BranchRegion:
2402 caseCounterMappingRegion::MCDCBranchRegion:
2405 caseCounterMappingRegion::MCDCDecisionRegion:
2410OS <<
"File "<< R.FileID <<
", "<< R.LineStart <<
":"<< R.ColumnStart
2411<<
" -> "<< R.LineEnd <<
":"<< R.ColumnEnd <<
" = ";
2413 if(
const auto*DecisionParams =
2414std::get_if<mcdc::DecisionParameters>(&R.MCDCParams)) {
2415OS <<
"M:"<< DecisionParams->BitmapIdx;
2416OS <<
", C:"<< DecisionParams->NumConditions;
2418Ctx.dump(R.Count, OS);
2422Ctx.dump(R.FalseCount, OS);
2426 if(
const auto*BranchParams =
2427std::get_if<mcdc::BranchParameters>(&R.MCDCParams)) {
2428OS <<
" ["<< BranchParams->ID + 1 <<
"," 2429<< BranchParams->Conds[
true] + 1;
2430OS <<
","<< BranchParams->Conds[
false] + 1 <<
"] ";
2433 if(R.Kind == CounterMappingRegion::ExpansionRegion)
2434OS <<
" (Expanded file = "<< R.ExpandedFileID <<
")";
2441: CGM(CGM), SourceInfo(SourceInfo) {}
2443std::string CoverageMappingModuleGen::getCurrentDirname() {
2448llvm::sys::fs::current_path(CWD);
2449 returnCWD.str().str();
2452std::string CoverageMappingModuleGen::normalizeFilename(StringRef
Filename) {
2454llvm::sys::path::remove_dots(
Path,
true);
2459 for(
const auto&[From, To] :
2461 if(llvm::sys::path::replace_path_prefix(
Path, From, To))
2464 return Path.str().str();
2468llvm::InstrProfSectKind SK) {
2469 returnllvm::getInstrProfSectionName(
2473voidCoverageMappingModuleGen::emitFunctionMappingRecord(
2474 constFunctionInfo &Info, uint64_t FilenamesRef) {
2478std::string FuncRecordName =
"__covrec_"+ llvm::utohexstr(Info.NameHash);
2485FuncRecordName +=
"u";
2488 const uint64_tNameHash = Info.NameHash;
2489 const uint64_tFuncHash = Info.FuncHash;
2490 conststd::string &CoverageMapping = Info.CoverageMapping;
2491#define COVMAP_FUNC_RECORD(Type, LLVMType, Name, Init) LLVMType, 2492llvm::Type *FunctionRecordTypes[] = {
2493#include "llvm/ProfileData/InstrProfData.inc" 2495 auto*FunctionRecordTy =
2496llvm::StructType::get(Ctx,
ArrayRef(FunctionRecordTypes),
2500#define COVMAP_FUNC_RECORD(Type, LLVMType, Name, Init) Init, 2501llvm::Constant *FunctionRecordVals[] = {
2502 #include "llvm/ProfileData/InstrProfData.inc" 2504 auto*FuncRecordConstant =
2505llvm::ConstantStruct::get(FunctionRecordTy,
ArrayRef(FunctionRecordVals));
2508 auto*FuncRecord =
newllvm::GlobalVariable(
2509CGM.
getModule(), FunctionRecordTy,
true,
2510llvm::GlobalValue::LinkOnceODRLinkage, FuncRecordConstant,
2512FuncRecord->setVisibility(llvm::GlobalValue::HiddenVisibility);
2514FuncRecord->setAlignment(llvm::Align(8));
2516FuncRecord->setComdat(CGM.
getModule().getOrInsertComdat(FuncRecordName));
2523llvm::GlobalVariable *NamePtr, StringRef NameValue, uint64_t FuncHash,
2524 conststd::string &CoverageMapping,
boolIsUsed) {
2525 constuint64_t NameHash = llvm::IndexedInstrProf::ComputeHash(NameValue);
2526FunctionRecords.push_back({NameHash, FuncHash, CoverageMapping, IsUsed});
2529FunctionNames.push_back(NamePtr);
2538std::vector<StringRef> Filenames;
2539std::vector<CounterExpression> Expressions;
2540std::vector<CounterMappingRegion> Regions;
2541FilenameStrs.resize(FileEntries.size() + 1);
2542FilenameStrs[0] = normalizeFilename(getCurrentDirname());
2543 for(
const auto&Entry : FileEntries) {
2544 autoI = Entry.second;
2545FilenameStrs[I] = normalizeFilename(Entry.first.getName());
2548RawCoverageMappingReader Reader(CoverageMapping, FilenameRefs, Filenames,
2549Expressions, Regions);
2552 dump(llvm::outs(), NameValue, Expressions, Regions);
2557 if(FunctionRecords.empty())
2560 auto*Int32Ty = llvm::Type::getInt32Ty(Ctx);
2564FilenameStrs.resize(FileEntries.size() + 1);
2566FilenameStrs[0] = normalizeFilename(getCurrentDirname());
2567 for(
const auto&Entry : FileEntries) {
2568 autoI = Entry.second;
2569FilenameStrs[I] = normalizeFilename(Entry.first.getName());
2572std::string Filenames;
2574llvm::raw_string_ostream OS(Filenames);
2575CoverageFilenamesSectionWriter(FilenameStrs).write(OS);
2577 auto*FilenamesVal =
2578llvm::ConstantDataArray::getString(Ctx, Filenames,
false);
2579 constint64_t FilenamesRef = llvm::IndexedInstrProf::ComputeHash(Filenames);
2582 for(
constFunctionInfo &Info : FunctionRecords)
2583emitFunctionMappingRecord(Info, FilenamesRef);
2585 const unsignedNRecords = 0;
2586 const size_tFilenamesSize = Filenames.size();
2587 const unsignedCoverageMappingSize = 0;
2588llvm::Type *CovDataHeaderTypes[] = {
2589#define COVMAP_HEADER(Type, LLVMType, Name, Init) LLVMType, 2590#include "llvm/ProfileData/InstrProfData.inc" 2592 autoCovDataHeaderTy =
2593llvm::StructType::get(Ctx,
ArrayRef(CovDataHeaderTypes));
2594llvm::Constant *CovDataHeaderVals[] = {
2595#define COVMAP_HEADER(Type, LLVMType, Name, Init) Init, 2596#include "llvm/ProfileData/InstrProfData.inc" 2598 autoCovDataHeaderVal =
2599llvm::ConstantStruct::get(CovDataHeaderTy,
ArrayRef(CovDataHeaderVals));
2602llvm::Type *CovDataTypes[] = {CovDataHeaderTy, FilenamesVal->getType()};
2603 autoCovDataTy = llvm::StructType::get(Ctx,
ArrayRef(CovDataTypes));
2604llvm::Constant *TUDataVals[] = {CovDataHeaderVal, FilenamesVal};
2605 autoCovDataVal = llvm::ConstantStruct::get(CovDataTy,
ArrayRef(TUDataVals));
2606 autoCovData =
newllvm::GlobalVariable(
2607CGM.
getModule(), CovDataTy,
true, llvm::GlobalValue::PrivateLinkage,
2608CovDataVal, llvm::getCoverageMappingVarName());
2611CovData->setAlignment(llvm::Align(8));
2616 if(!FunctionNames.empty()) {
2617 autoNamesArrTy = llvm::ArrayType::get(llvm::PointerType::getUnqual(Ctx),
2618FunctionNames.size());
2619 autoNamesArrVal = llvm::ConstantArray::get(NamesArrTy, FunctionNames);
2622 newllvm::GlobalVariable(CGM.
getModule(), NamesArrTy,
true,
2623llvm::GlobalValue::InternalLinkage, NamesArrVal,
2624llvm::getCoverageUnusedNamesVarName());
2629 returnFileEntries.try_emplace(
File, FileEntries.size() + 1).first->second;
2633llvm::raw_ostream &OS) {
2634assert(CounterMap && MCDCState);
2635CounterCoverageMappingBuilder Walker(CVM, *CounterMap, *MCDCState,
SM,
2637Walker.VisitDecl(
D);
2642llvm::raw_ostream &OS) {
2643EmptyCoverageMappingBuilder Walker(CVM,
SM, LangOpts);
2644Walker.VisitDecl(
D);
Defines the Diagnostic-related interfaces.
static const MemRegion * getRegion(const CallEvent &Call, const MutexDescriptor &Descriptor, bool IsLock)
static std::string getInstrProfSection(const CodeGenModule &CGM, llvm::InstrProfSectKind SK)
static void dump(llvm::raw_ostream &OS, StringRef FunctionName, ArrayRef< CounterExpression > Expressions, ArrayRef< CounterMappingRegion > Regions)
static llvm::cl::opt< bool > EmptyLineCommentCoverage("emptyline-comment-coverage", llvm::cl::desc("Emit emptylines and comment lines as skipped regions (only " "disable it on test)"), llvm::cl::init(true), llvm::cl::Hidden)
llvm::DenseSet< const void * > Visited
static DiagnosticBuilder Diag(DiagnosticsEngine *Diags, const LangOptions &Features, FullSourceLoc TokLoc, const char *TokBegin, const char *TokRangeBegin, const char *TokRangeEnd, unsigned DiagID)
Produce a diagnostic highlighting some portion of a literal.
const TargetInfo & getTargetInfo() const
AbstractConditionalOperator - An abstract base class for ConditionalOperator and BinaryConditionalOpe...
Represents a loop initializing the elements of an array.
OpaqueValueExpr * getCommonExpr() const
Get the common subexpression shared by all initializations (the source array).
A builtin binary operation expression such as "x + y" or "x <= y".
BreakStmt - This represents a break.
CXXCatchStmt - This represents a C++ catch block.
CXXForRangeStmt - This represents C++0x [stmt.ranged]'s ranged for statement, represented as 'for (ra...
A C++ throw-expression (C++ [except.throw]).
CXXTryStmt - A C++ try block, including all handlers.
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
llvm::SmallVector< std::pair< std::string, std::string >, 0 > CoveragePrefixMap
Prefix replacement map for source-based code coverage to remap source file paths in coverage mapping.
std::string CoverageCompilationDir
The string to embed in coverage mapping as the current working directory.
This class organizes the cross-function state that is used while generating LLVM code.
llvm::Module & getModule() const
DiagnosticsEngine & getDiags() const
void addUsedGlobal(llvm::GlobalValue *GV)
Add a global to a list to be added to the llvm.used metadata.
ASTContext & getContext() const
bool supportsCOMDAT() const
const CodeGenOptions & getCodeGenOpts() const
llvm::LLVMContext & getLLVMContext()
void emitEmptyMapping(const Decl *D, llvm::raw_ostream &OS)
Emit the coverage mapping data for an unused function.
void emitCounterMapping(const Decl *D, llvm::raw_ostream &OS)
Emit the coverage mapping data which maps the regions of code to counters that will be used to find t...
Organizes the cross-function state that is used while generating code coverage mapping data.
void addFunctionMappingRecord(llvm::GlobalVariable *FunctionName, StringRef FunctionNameValue, uint64_t FunctionHash, const std::string &CoverageMapping, bool IsUsed=true)
Add a function's coverage mapping record to the collection of the function mapping records.
CoverageSourceInfo & getSourceInfo() const
static CoverageSourceInfo * setUpCoverageCallbacks(Preprocessor &PP)
CoverageMappingModuleGen(CodeGenModule &CGM, CoverageSourceInfo &SourceInfo)
void emit()
Emit the coverage mapping data for a translation unit.
CodeGenModule & getCodeGenModule()
Return an interface into CodeGenModule.
unsigned getFileID(FileEntryRef File)
Return the coverage mapping translation unit file id for the given file.
ConstStmtVisitor - This class implements a simple visitor for Stmt subclasses.
ContinueStmt - This represents a continue.
Represents a 'co_return' statement in the C++ Coroutines TS.
Represents the body of a coroutine.
Represents an expression that might suspend coroutine execution; either a co_await or co_yield expres...
Stores additional source code information like skipped ranges which is required by the coverage mappi...
void SourceRangeSkipped(SourceRange Range, SourceLocation EndifLoc) override
Hook called when a source range is skipped.
void updateNextTokLoc(SourceLocation Loc)
void AddSkippedRange(SourceRange Range, SkippedRange::Kind RangeKind)
std::vector< SkippedRange > & getSkippedRanges()
bool HandleComment(Preprocessor &PP, SourceRange Range) override
SourceLocation PrevTokLoc
void HandleEmptyline(SourceRange Range) override
Decl - This represents one declaration (or definition), e.g.
virtual Stmt * getBody() const
getBody - If this Decl represents a declaration for a body of code, such as a function or method defi...
virtual bool hasBody() const
Returns true if this Decl represents a declaration for a body of code, such as a function or method d...
DoStmt - This represents a 'do/while' stmt.
This represents one expression.
bool isValueDependent() const
Determines whether the value of this expression depends on.
bool EvaluateAsBooleanCondition(bool &Result, const ASTContext &Ctx, bool InConstantContext=false) const
EvaluateAsBooleanCondition - Return true if this is a constant which we can fold and convert to a boo...
A reference to a FileEntry that includes the name of the file as it was accessed by the FileManager's...
An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...
ForStmt - This represents a 'for (init;cond;inc)' stmt.
GotoStmt - This represents a direct goto.
IfStmt - This represents an if/then/else.
LabelStmt - Represents a label, which has a substatement.
A C++ lambda expression, which produces a function object (of unspecified type) that can be invoked l...
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
static unsigned MeasureTokenLength(SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts)
MeasureTokenLength - Relex the token at the specified location and return its length in bytes in the ...
Represents Objective-C's collection statement.
OpaqueValueExpr - An expression referring to an opaque object of a fixed type and value class.
Expr * getSourceExpr() const
The source expression of an opaque value expression is the expression which originally generated the ...
Engages in a tight little dance with the lexer to efficiently preprocess tokens.
void addCommentHandler(CommentHandler *Handler)
Add the specified comment handler to the preprocessor.
void addPPCallbacks(std::unique_ptr< PPCallbacks > C)
SourceManager & getSourceManager() const
void setPreprocessToken(bool Preprocess)
void setTokenWatcher(llvm::unique_function< void(const clang::Token &)> F)
Register a function that would be called on each token in the final expanded token stream.
void setEmptylineHandler(EmptylineHandler *Handler)
Set empty line handler.
PseudoObjectExpr - An expression which accesses a pseudo-object l-value.
Expr * getSyntacticForm()
Return the syntactic form of this expression, i.e.
A (possibly-)qualified type.
ReturnStmt - This represents a return, optionally of an expression: return; return 4;.
Encodes a location in the source.
bool isValid() const
Return true if this is a valid SourceLocation object.
SourceLocation getLocWithOffset(IntTy Offset) const
Return a source location with the specified offset from this SourceLocation.
This class handles loading and caching of source files into memory.
A trivial tuple used to represent a source range.
SourceLocation getEnd() const
SourceLocation getBegin() const
Each ExpansionInfo encodes the expansion location - where the token was ultimately expanded,...
SourceLocation getExpansionLocStart() const
bool isFunctionMacroExpansion() const
SourceLocation getExpansionLocEnd() const
Stmt - This represents one statement.
SourceLocation getEndLoc() const LLVM_READONLY
SourceLocation getBeginLoc() const LLVM_READONLY
SourceLocation getColonLoc() const
const SwitchCase * getNextSwitchCase() const
SwitchStmt - This represents a 'switch' stmt.
const llvm::Triple & getTriple() const
Returns the target triple of the primary target.
Token - This structure provides full information about a lexed token.
SourceLocation getLocation() const
Return a source location identifier for the specified offset in the current file.
tok::TokenKind getKind() const
WhileStmt - This represents a 'while' stmt.
The JSON file list parser is used to communicate input to InstallAPI.
FunctionType::ExtInfo getFunctionExtInfo(const Type &t)
cl::opt< bool > SystemHeadersCoverage
Diagnostic wrappers for TextAPI types for error reporting.
cl::opt< bool > EnableSingleByteCoverage
Per-Function MC/DC state.
llvm::DenseMap< const Stmt *, Branch > BranchByStmt
llvm::DenseMap< const Stmt *, Decision > DecisionByStmt
EvalResult is a struct with detailed info about an evaluated expression.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4