clang: lib/CodeGen/CoverageMappingGen.cpp Source File (original) (raw)
1
2
3
4
5
6
7
8
9
10
11
12
19#include "llvm/ADT/DenseSet.h"
20#include "llvm/ADT/SmallSet.h"
21#include "llvm/ADT/StringExtras.h"
22#include "llvm/ProfileData/Coverage/CoverageMapping.h"
23#include "llvm/ProfileData/Coverage/CoverageMappingReader.h"
24#include "llvm/ProfileData/Coverage/CoverageMappingWriter.h"
25#include "llvm/Support/FileSystem.h"
26#include "llvm/Support/Path.h"
27#include
28
29
30
31#define COVMAP_V3
32
33namespace llvm {
34cl::opt
36 llvm:🆑:ZeroOrMore,
37 llvm:🆑:desc("Enable single byte coverage"),
38 llvm:🆑:Hidden, llvm:🆑:init(false));
39}
40
42 "emptyline-comment-coverage",
43 llvm:🆑:desc("Emit emptylines and comment lines as skipped regions (only "
44 "disable it on test)"),
45 llvm:🆑:init(true), llvm:🆑:Hidden);
46
49 "system-headers-coverage",
50 cl::desc("Enable collecting coverage from system headers"), cl::init(false),
51 cl::Hidden);
52}
53
54using namespace clang;
57
62 PP.addPPCallbacks(std::unique_ptr(CoverageInfo));
68
70 if (Tok.getKind() != clang::tok::eod)
72 });
73 }
74 return CoverageInfo;
75}
76
80 PrevTokLoc == SkippedRanges.back().PrevTokLoc &&
81 SourceMgr.isWrittenInSameFile(SkippedRanges.back().Range.getEnd(),
82 Range.getBegin()))
83 SkippedRanges.back().Range.setEnd(Range.getEnd());
84 else
85 SkippedRanges.push_back({Range, RangeKind, PrevTokLoc});
86}
87
91
95
100
102 if (!SkippedRanges.empty() && SkippedRanges.back().NextTokLoc.isInvalid())
103 SkippedRanges.back().NextTokLoc = Loc;
104}
105
106namespace {
107
108class SourceMappingRegion {
109
110 Counter Count;
111
112
113 std::optional FalseCount;
114
115
116 mcdc::Parameters MCDCParams;
117
118
119 std::optional LocStart;
120
121
122 std::optional LocEnd;
123
124
125
126 bool GapRegion;
127
128
129
130 bool SkippedRegion;
131
132public:
133 SourceMappingRegion(Counter Count, std::optional LocStart,
134 std::optional LocEnd,
135 bool GapRegion = false)
136 : Count(Count), LocStart(LocStart), LocEnd(LocEnd), GapRegion(GapRegion),
137 SkippedRegion(false) {}
138
139 SourceMappingRegion(Counter Count, std::optional FalseCount,
140 mcdc::Parameters MCDCParams,
141 std::optional LocStart,
142 std::optional LocEnd,
143 bool GapRegion = false)
144 : Count(Count), FalseCount(FalseCount), MCDCParams(MCDCParams),
145 LocStart(LocStart), LocEnd(LocEnd), GapRegion(GapRegion),
146 SkippedRegion(false) {}
147
148 SourceMappingRegion(mcdc::Parameters MCDCParams,
149 std::optional LocStart,
150 std::optional LocEnd)
151 : MCDCParams(MCDCParams), LocStart(LocStart), LocEnd(LocEnd),
152 GapRegion(false), SkippedRegion(false) {}
153
154 const Counter &getCounter() const { return Count; }
155
156 const Counter &getFalseCounter() const {
157 assert(FalseCount && "Region has no alternate counter");
158 return *FalseCount;
159 }
160
161 void setCounter(Counter C) { Count = C; }
162
163 bool hasStartLoc() const { return LocStart.has_value(); }
164
165 void setStartLoc(SourceLocation Loc) { LocStart = Loc; }
166
167 SourceLocation getBeginLoc() const {
168 assert(LocStart && "Region has no start location");
169 return *LocStart;
170 }
171
172 bool hasEndLoc() const { return LocEnd.has_value(); }
173
174 void setEndLoc(SourceLocation Loc) {
175 assert(Loc.isValid() && "Setting an invalid end location");
176 LocEnd = Loc;
177 }
178
179 SourceLocation getEndLoc() const {
180 assert(LocEnd && "Region has no end location");
181 return *LocEnd;
182 }
183
184 bool isGap() const { return GapRegion; }
185
186 void setGap(bool Gap) { GapRegion = Gap; }
187
188 bool isSkipped() const { return SkippedRegion; }
189
190 void setSkipped(bool Skipped) { SkippedRegion = Skipped; }
191
192 bool isBranch() const { return FalseCount.has_value(); }
193
194 bool isMCDCBranch() const {
195 return std::holds_alternativemcdc::BranchParameters(MCDCParams);
196 }
197
198 const auto &getMCDCBranchParams() const {
199 return mcdc::getParams(MCDCParams);
200 }
201
202 bool isMCDCDecision() const {
203 return std::holds_alternativemcdc::DecisionParameters(MCDCParams);
204 }
205
206 const auto &getMCDCDecisionParams() const {
207 return mcdc::getParams(MCDCParams);
208 }
209
210 const mcdc::Parameters &getMCDCParams() const { return MCDCParams; }
211
212 void resetMCDCParams() { MCDCParams = mcdc::Parameters(); }
213};
214
215
216struct SpellingRegion {
217
218 unsigned LineStart;
219
220
221 unsigned ColumnStart;
222
223
224 unsigned LineEnd;
225
226
227 unsigned ColumnEnd;
228
229 SpellingRegion(SourceManager &SM, SourceLocation LocStart,
230 SourceLocation LocEnd) {
231 LineStart = SM.getSpellingLineNumber(LocStart);
232 ColumnStart = SM.getSpellingColumnNumber(LocStart);
233 LineEnd = SM.getSpellingLineNumber(LocEnd);
234 ColumnEnd = SM.getSpellingColumnNumber(LocEnd);
235 }
236
237 SpellingRegion(SourceManager &SM, SourceMappingRegion &R)
238 : SpellingRegion(SM, R.getBeginLoc(), R.getEndLoc()) {}
239
240
241
242 bool isInSourceOrder() const {
243 return (LineStart < LineEnd) ||
244 (LineStart == LineEnd && ColumnStart <= ColumnEnd);
245 }
246};
247
248
249
250class CoverageMappingBuilder {
251public:
252 CoverageMappingModuleGen &CVM;
253 SourceManager &SM;
254 const LangOptions &LangOpts;
255
256private:
257
258 llvm::SmallDenseMap<FileID, std::pair<unsigned, SourceLocation>, 8>
259 FileIDMapping;
260
261public:
262
263 llvm::SmallVector<CounterMappingRegion, 32> MappingRegions;
264
265 std::vector SourceRegions;
266
267
268
269
270
271
272 typedef llvm::SmallSet<std::pair<SourceLocation, SourceLocation>, 8>
273 SourceRegionFilter;
274
275 CoverageMappingBuilder(CoverageMappingModuleGen &CVM, SourceManager &SM,
276 const LangOptions &LangOpts)
277 : CVM(CVM), SM(SM), LangOpts(LangOpts) {}
278
279
280 SourceLocation getPreciseTokenLocEnd(SourceLocation Loc) {
281
282
283 unsigned TokLen =
286 }
287
288
289 SourceLocation getStartOfFileOrMacro(SourceLocation Loc) {
292 return SM.getLocForStartOfFile(SM.getFileID(Loc));
293 }
294
295
296 SourceLocation getEndOfFileOrMacro(SourceLocation Loc) {
299 SM.getFileOffset(Loc));
300 return SM.getLocForEndOfFile(SM.getFileID(Loc));
301 }
302
303
304
305
306
307
308
309
310 std::pair<SourceLocation, std::optional>
311 getNonScratchExpansionLoc(SourceLocation Loc) {
312 std::optional EndLoc = std::nullopt;
314 SM.isWrittenInScratchSpace(SM.getSpellingLoc(Loc))) {
315 auto ExpansionRange = SM.getImmediateExpansionRange(Loc);
316 Loc = ExpansionRange.getBegin();
317 EndLoc = ExpansionRange.getEnd();
318 }
319 return std::make_pair(Loc, EndLoc);
320 }
321
322
323
324
325 SourceLocation getIncludeOrExpansionLoc(SourceLocation Loc,
326 bool AcceptScratch = true) {
328 return SM.getIncludeLoc(SM.getFileID(Loc));
329 Loc = SM.getImmediateExpansionRange(Loc).getBegin();
330 if (AcceptScratch)
331 return Loc;
332 return getNonScratchExpansionLoc(Loc).first;
333 }
334
335
336 bool isInBuiltin(SourceLocation Loc) {
337 return SM.getBufferName(SM.getSpellingLoc(Loc)) == "";
338 }
339
340
341 bool isNestedIn(SourceLocation Loc, FileID Parent) {
342 do {
343 Loc = getIncludeOrExpansionLoc(Loc);
345 return false;
346 } while (.isInFileID(Loc, Parent));
347 return true;
348 }
349
350
351 SourceLocation getStart(const Stmt *S) {
353 while (SM.isMacroArgExpansion(Loc) || isInBuiltin(Loc))
354 Loc = SM.getImmediateExpansionRange(Loc).getBegin();
355 return Loc;
356 }
357
358
359 SourceLocation getEnd(const Stmt *S) {
360 SourceLocation Loc = S->getEndLoc();
361 while (SM.isMacroArgExpansion(Loc) || isInBuiltin(Loc))
362 Loc = SM.getImmediateExpansionRange(Loc).getBegin();
363 return getPreciseTokenLocEnd(Loc);
364 }
365
366
367
368
369
370
371 void gatherFileIDs(SmallVectorImpl &Mapping) {
372 FileIDMapping.clear();
373
374 llvm::SmallSet<FileID, 8> Visited;
375 SmallVector<std::pair<SourceLocation, unsigned>, 8> FileLocs;
376 for (auto &Region : SourceRegions) {
377 SourceLocation Loc = Region.getBeginLoc();
378
379
380 auto NonScratchExpansionLoc = getNonScratchExpansionLoc(Loc);
381 auto EndLoc = NonScratchExpansionLoc.second;
382 if (EndLoc.has_value()) {
383 Loc = NonScratchExpansionLoc.first;
384 Region.setStartLoc(Loc);
385 Region.setEndLoc(EndLoc.value());
386 }
387
388
390 auto BeginLoc = SM.getSpellingLoc(Loc);
391 auto EndLoc = SM.getSpellingLoc(Region.getEndLoc());
392 if (SM.isWrittenInSameFile(BeginLoc, EndLoc)) {
393 Loc = SM.getFileLoc(Loc);
394 Region.setStartLoc(Loc);
395 Region.setEndLoc(SM.getFileLoc(Region.getEndLoc()));
396 }
397 }
398
399 FileID File = SM.getFileID(Loc);
400 if (!Visited.insert(File).second)
401 continue;
402
404 .isInSystemHeader(SM.getSpellingLoc(Loc)));
405
406 unsigned Depth = 0;
407 for (SourceLocation Parent = getIncludeOrExpansionLoc(Loc);
408 Parent.isValid(); Parent = getIncludeOrExpansionLoc(Parent))
409 ++Depth;
410 FileLocs.push_back(std::make_pair(Loc, Depth));
411 }
412 llvm::stable_sort(FileLocs, llvm::less_second());
413
414 for (const auto &FL : FileLocs) {
415 SourceLocation Loc = FL.first;
416 FileID SpellingFile = SM.getDecomposedSpellingLoc(Loc).first;
417 auto Entry = SM.getFileEntryRefForID(SpellingFile);
418 if (!Entry)
419 continue;
420
421 FileIDMapping[SM.getFileID(Loc)] = std::make_pair(Mapping.size(), Loc);
422 Mapping.push_back(CVM.getFileID(*Entry));
423 }
424 }
425
426
427
428
429 std::optional getCoverageFileID(SourceLocation Loc) {
430 auto Mapping = FileIDMapping.find(SM.getFileID(Loc));
431 if (Mapping != FileIDMapping.end())
432 return Mapping->second.first;
433 return std::nullopt;
434 }
435
436
437
438
439
440
441 std::optional adjustSkippedRange(SourceManager &SM,
442 SourceLocation LocStart,
443 SourceLocation LocEnd,
444 SourceLocation PrevTokLoc,
445 SourceLocation NextTokLoc) {
446 SpellingRegion SR{SM, LocStart, LocEnd};
447 SR.ColumnStart = 1;
448 if (PrevTokLoc.isValid() && SM.isWrittenInSameFile(LocStart, PrevTokLoc) &&
449 SR.LineStart == SM.getSpellingLineNumber(PrevTokLoc))
450 SR.LineStart++;
451 if (NextTokLoc.isValid() && SM.isWrittenInSameFile(LocEnd, NextTokLoc) &&
452 SR.LineEnd == SM.getSpellingLineNumber(NextTokLoc)) {
453 SR.LineEnd--;
454 SR.ColumnEnd++;
455 }
456 if (SR.isInSourceOrder())
457 return SR;
458 return std::nullopt;
459 }
460
461
462
463 void gatherSkippedRegions() {
464
465
466 llvm::SmallVector<std::pair<unsigned, unsigned>, 8> FileLineRanges;
467 FileLineRanges.resize(
468 FileIDMapping.size(),
469 std::make_pair(std::numeric_limits::max(), 0));
470 for (const auto &R : MappingRegions) {
471 FileLineRanges[R.FileID].first =
472 std::min(FileLineRanges[R.FileID].first, R.LineStart);
473 FileLineRanges[R.FileID].second =
474 std::max(FileLineRanges[R.FileID].second, R.LineEnd);
475 }
476
478 for (auto &I : SkippedRanges) {
479 SourceRange Range = I.Range;
480 auto LocStart = Range.getBegin();
481 auto LocEnd = Range.getEnd();
482 assert(SM.isWrittenInSameFile(LocStart, LocEnd) &&
483 "region spans multiple files");
484
485 auto CovFileID = getCoverageFileID(LocStart);
486 if (!CovFileID)
487 continue;
488 std::optional SR;
489 if (I.isComment())
490 SR = adjustSkippedRange(SM, LocStart, LocEnd, I.PrevTokLoc,
491 I.NextTokLoc);
492 else if (I.isPPIfElse() || I.isEmptyLine())
493 SR = {SM, LocStart, LocEnd};
494
495 if (!SR)
496 continue;
497 auto Region = CounterMappingRegion::makeSkipped(
498 *CovFileID, SR->LineStart, SR->ColumnStart, SR->LineEnd,
499 SR->ColumnEnd);
500
501
502 if (Region.LineStart >= FileLineRanges[*CovFileID].first &&
503 Region.LineEnd <= FileLineRanges[*CovFileID].second)
504 MappingRegions.push_back(Region);
505 }
506 }
507
508
509
510 void emitSourceRegions(const SourceRegionFilter &Filter) {
511 for (const auto &Region : SourceRegions) {
512 assert(Region.hasEndLoc() && "incomplete region");
513
514 SourceLocation LocStart = Region.getBeginLoc();
515 assert(SM.getFileID(LocStart).isValid() && "region in invalid file");
516
517
518
520 SM.isInSystemHeader(SM.getSpellingLoc(LocStart))) {
521 assert(!Region.isMCDCBranch() && !Region.isMCDCDecision() &&
522 "Don't suppress the condition in system headers");
523 continue;
524 }
525
526 auto CovFileID = getCoverageFileID(LocStart);
527
528 if (!CovFileID) {
529 assert(!Region.isMCDCBranch() && !Region.isMCDCDecision() &&
530 "Don't suppress the condition in non-file regions");
531 continue;
532 }
533
534 SourceLocation LocEnd = Region.getEndLoc();
535 assert(SM.isWrittenInSameFile(LocStart, LocEnd) &&
536 "region spans multiple files");
537
538
539
540
541
542 if (Filter.count(std::make_pair(LocStart, LocEnd))) {
543 assert(!Region.isMCDCBranch() && !Region.isMCDCDecision() &&
544 "Don't suppress the condition");
545 continue;
546 }
547
548
549 SpellingRegion SR{SM, LocStart, LocEnd};
550 assert(SR.isInSourceOrder() && "region start and end out of order");
551
552 if (Region.isGap()) {
553 MappingRegions.push_back(CounterMappingRegion::makeGapRegion(
554 Region.getCounter(), *CovFileID, SR.LineStart, SR.ColumnStart,
555 SR.LineEnd, SR.ColumnEnd));
556 } else if (Region.isSkipped()) {
557 MappingRegions.push_back(CounterMappingRegion::makeSkipped(
558 *CovFileID, SR.LineStart, SR.ColumnStart, SR.LineEnd,
559 SR.ColumnEnd));
560 } else if (Region.isBranch()) {
561 MappingRegions.push_back(CounterMappingRegion::makeBranchRegion(
562 Region.getCounter(), Region.getFalseCounter(), *CovFileID,
563 SR.LineStart, SR.ColumnStart, SR.LineEnd, SR.ColumnEnd,
564 Region.getMCDCParams()));
565 } else if (Region.isMCDCDecision()) {
566 MappingRegions.push_back(CounterMappingRegion::makeDecisionRegion(
567 Region.getMCDCDecisionParams(), *CovFileID, SR.LineStart,
568 SR.ColumnStart, SR.LineEnd, SR.ColumnEnd));
569 } else {
570 MappingRegions.push_back(CounterMappingRegion::makeRegion(
571 Region.getCounter(), *CovFileID, SR.LineStart, SR.ColumnStart,
572 SR.LineEnd, SR.ColumnEnd));
573 }
574 }
575 }
576
577
578 SourceRegionFilter emitExpansionRegions() {
579 SourceRegionFilter Filter;
580 for (const auto &FM : FileIDMapping) {
581 SourceLocation ExpandedLoc = FM.second.second;
582 SourceLocation ParentLoc = getIncludeOrExpansionLoc(ExpandedLoc, false);
584 continue;
585
586 auto ParentFileID = getCoverageFileID(ParentLoc);
587 if (!ParentFileID)
588 continue;
589 auto ExpandedFileID = getCoverageFileID(ExpandedLoc);
590 assert(ExpandedFileID && "expansion in uncovered file");
591
592 SourceLocation LocEnd = getPreciseTokenLocEnd(ParentLoc);
593 assert(SM.isWrittenInSameFile(ParentLoc, LocEnd) &&
594 "region spans multiple files");
595 Filter.insert(std::make_pair(ParentLoc, LocEnd));
596
597 SpellingRegion SR{SM, ParentLoc, LocEnd};
598 assert(SR.isInSourceOrder() && "region start and end out of order");
599 MappingRegions.push_back(CounterMappingRegion::makeExpansion(
600 *ParentFileID, *ExpandedFileID, SR.LineStart, SR.ColumnStart,
601 SR.LineEnd, SR.ColumnEnd));
602 }
604 }
605};
606
607
608
609struct EmptyCoverageMappingBuilder : public CoverageMappingBuilder {
610 EmptyCoverageMappingBuilder(CoverageMappingModuleGen &CVM, SourceManager &SM,
611 const LangOptions &LangOpts)
612 : CoverageMappingBuilder(CVM, SM, LangOpts) {}
613
614 void VisitDecl(const Decl *D) {
616 return;
617 auto Body = D->getBody();
618 SourceLocation Start = getStart(Body);
619 SourceLocation End = getEnd(Body);
620 if (.isWrittenInSameFile(Start, End)) {
621
622
623 FileID StartFileID = SM.getFileID(Start);
624 FileID EndFileID = SM.getFileID(End);
625 while (StartFileID != EndFileID && !isNestedIn(End, StartFileID)) {
626 Start = getIncludeOrExpansionLoc(Start);
627 assert(Start.isValid() &&
628 "Declaration start location not nested within a known region");
629 StartFileID = SM.getFileID(Start);
630 }
631 while (StartFileID != EndFileID) {
632 End = getPreciseTokenLocEnd(getIncludeOrExpansionLoc(End));
634 "Declaration end location not nested within a known region");
635 EndFileID = SM.getFileID(End);
636 }
637 }
638 SourceRegions.emplace_back(Counter(), Start, End);
639 }
640
641
642 void write(llvm::raw_ostream &OS) {
643 SmallVector<unsigned, 16> FileIDMapping;
644 gatherFileIDs(FileIDMapping);
645 emitSourceRegions(SourceRegionFilter());
646
647 if (MappingRegions.empty())
648 return;
649
650 CoverageMappingWriter Writer(FileIDMapping, {}, MappingRegions);
651 Writer.write(OS);
652 }
653};
654
655
656
657
658
659
660
661
662
663struct MCDCCoverageBuilder {
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752private:
753 CodeGenModule &CGM;
754
755 llvm::SmallVectormcdc::ConditionIDs DecisionStack;
756 MCDC::State &MCDCState;
757 const Stmt *DecisionStmt = nullptr;
758 mcdc::ConditionID NextID = 0;
759 bool NotMapped = false;
760
761
762
763 static constexpr mcdc::ConditionIDs DecisionStackSentinel{-1, -1};
764
765
766 bool isLAnd(const BinaryOperator *E) const {
767 return E->getOpcode() == BO_LAnd;
768 }
769
770public:
771 MCDCCoverageBuilder(CodeGenModule &CGM, MCDC::State &MCDCState)
772 : CGM(CGM), DecisionStack(1, DecisionStackSentinel),
773 MCDCState(MCDCState) {}
774
775
776
777
778 bool isIdle() const { return (NextID == 0 && !NotMapped); }
779
780
781
782
783 bool isBuilding() const { return (NextID > 0); }
784
785
786 void setCondID(const Expr *Cond, mcdc::ConditionID ID) {
788 DecisionStmt};
789 }
790
791
792 mcdc::ConditionID getCondID(const Expr *Cond) const {
795 return -1;
796 else
797 return I->second.ID;
798 }
799
800
801 const mcdc::ConditionIDs &back() const { return DecisionStack.back(); }
802
803
804
805
806 void pushAndAssignIDs(const BinaryOperator *E) {
808 return;
809
810
811 if (!isBuilding() &&
813 NotMapped = true;
814
815
816 if (NotMapped)
817 return;
818
819 if (NextID == 0) {
820 DecisionStmt = E;
822 }
823
824 const mcdc::ConditionIDs &ParentDecision = DecisionStack.back();
825
826
827
828
830 setCondID(E->getLHS(), getCondID(E));
831 else
832 setCondID(E->getLHS(), NextID++);
833
834
835 mcdc::ConditionID RHSid = NextID++;
836 setCondID(E->getRHS(), RHSid);
837
838
839 if (isLAnd(E))
840 DecisionStack.push_back({ParentDecision[false], RHSid});
841 else
842 DecisionStack.push_back({RHSid, ParentDecision[true]});
843 }
844
845
846 mcdc::ConditionIDs pop() {
848 return DecisionStackSentinel;
849
850 assert(DecisionStack.size() > 1);
851 return DecisionStack.pop_back_val();
852 }
853
854
855
856 unsigned getTotalConditionsAndReset(const BinaryOperator *E) {
858 return 0;
859
860 assert(!isIdle());
861 assert(DecisionStack.size() == 1);
862
863
864 if (NotMapped) {
865 NotMapped = false;
866 assert(NextID == 0);
867 return 0;
868 }
869
870
871 unsigned TotalConds = NextID;
872
873
874 NextID = 0;
875
876 return TotalConds;
877 }
878};
879
880
881
882struct CounterCoverageMappingBuilder
883 : public CoverageMappingBuilder,
884 public ConstStmtVisitor {
885
886 llvm::DenseMap<const Stmt *, CounterPair> &CounterMap;
887
888 MCDC::State &MCDCState;
889
890
891 llvm::SmallVector RegionStack;
892
893
894
895 llvm::DenseSet<const Stmt *> LeafExprSet;
896
897
898 MCDCCoverageBuilder MCDCBuilder;
899
900 CounterExpressionBuilder Builder;
901
902
903
904
905
906 SourceLocation MostRecentLocation;
907
908
909 bool HasTerminateStmt = false;
910
911
912 Counter GapRegionCounter;
913
914
915 Counter subtractCounters(Counter LHS, Counter RHS, bool Simplify = true) {
917 "cannot add counters when single byte coverage mode is enabled");
918 return Builder.subtract(LHS, RHS, Simplify);
919 }
920
921
922 Counter addCounters(Counter LHS, Counter RHS, bool Simplify = true) {
923 return Builder.add(LHS, RHS, Simplify);
924 }
925
926 Counter addCounters(Counter C1, Counter C2, Counter C3,
927 bool Simplify = true) {
928 return addCounters(addCounters(C1, C2, Simplify), C3, Simplify);
929 }
930
931
932
933
934 Counter getRegionCounter(const Stmt *S) {
935 return Counter::getCounter(CounterMap[S].Executed);
936 }
937
938 struct BranchCounterPair {
939 Counter Executed;
940 Counter Skipped;
941 };
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959 BranchCounterPair
960 getBranchCounterPair(const Stmt *S, Counter ParentCnt,
961 std::optional SkipCntForOld = std::nullopt) {
962 Counter ExecCnt = getRegionCounter(S);
963
964
965
967 assert(SkipCntForOld &&
968 "SingleByte must provide SkipCntForOld as a fake Skipped count.");
969 return {ExecCnt, *SkipCntForOld};
970 }
971
972 return {ExecCnt, Builder.subtract(ParentCnt, ExecCnt)};
973 }
974
975 bool IsCounterEqual(Counter OutCount, Counter ParentCount) {
976 if (OutCount == ParentCount)
977 return true;
978
979 return false;
980 }
981
982
983
984
985
986 size_t pushRegion(Counter Count,
987 std::optional StartLoc = std::nullopt,
988 std::optional EndLoc = std::nullopt,
989 std::optional FalseCount = std::nullopt,
990 const mcdc::Parameters &BranchParams = std::monostate()) {
991
992 if (StartLoc && !FalseCount) {
993 MostRecentLocation = *StartLoc;
994 }
995
996
997
998 assert((!StartLoc || StartLoc->isValid()) && "Start location is not valid");
999 assert((!EndLoc || EndLoc->isValid()) && "End location is not valid");
1000
1001
1002
1003
1004
1005 if (StartLoc && StartLoc->isInvalid())
1006 StartLoc = std::nullopt;
1007 if (EndLoc && EndLoc->isInvalid())
1008 EndLoc = std::nullopt;
1009 RegionStack.emplace_back(Count, FalseCount, BranchParams, StartLoc, EndLoc);
1010
1011 return RegionStack.size() - 1;
1012 }
1013
1014 size_t pushRegion(const mcdc::DecisionParameters &DecisionParams,
1015 std::optional StartLoc = std::nullopt,
1016 std::optional EndLoc = std::nullopt) {
1017
1018 RegionStack.emplace_back(DecisionParams, StartLoc, EndLoc);
1019
1020 return RegionStack.size() - 1;
1021 }
1022
1023 size_t locationDepth(SourceLocation Loc) {
1024 size_t Depth = 0;
1026 Loc = getIncludeOrExpansionLoc(Loc);
1027 Depth++;
1028 }
1029 return Depth;
1030 }
1031
1032
1033
1034
1035
1036 void popRegions(size_t ParentIndex) {
1037 assert(RegionStack.size() >= ParentIndex && "parent not in stack");
1038 while (RegionStack.size() > ParentIndex) {
1039 SourceMappingRegion &Region = RegionStack.back();
1040 if (Region.hasStartLoc() &&
1041 (Region.hasEndLoc() || RegionStack[ParentIndex].hasEndLoc())) {
1042 SourceLocation StartLoc = Region.getBeginLoc();
1043 SourceLocation EndLoc = Region.hasEndLoc()
1044 ? Region.getEndLoc()
1045 : RegionStack[ParentIndex].getEndLoc();
1046 bool isBranch = Region.isBranch();
1047 size_t StartDepth = locationDepth(StartLoc);
1048 size_t EndDepth = locationDepth(EndLoc);
1049 while (.isWrittenInSameFile(StartLoc, EndLoc)) {
1050 bool UnnestStart = StartDepth >= EndDepth;
1051 bool UnnestEnd = EndDepth >= StartDepth;
1052 if (UnnestEnd) {
1053
1054
1055
1056
1057
1058
1059 SourceLocation NestedLoc = getStartOfFileOrMacro(EndLoc);
1060 assert(SM.isWrittenInSameFile(NestedLoc, EndLoc));
1061
1062 if (!isBranch && !isRegionAlreadyAdded(NestedLoc, EndLoc))
1063 SourceRegions.emplace_back(Region.getCounter(), NestedLoc,
1064 EndLoc);
1065
1066 EndLoc = getPreciseTokenLocEnd(getIncludeOrExpansionLoc(EndLoc));
1068 llvm::report_fatal_error(
1069 "File exit not handled before popRegions");
1070 EndDepth--;
1071 }
1072 if (UnnestStart) {
1073
1074
1075
1076
1077
1078
1079 SourceLocation NestedLoc = getEndOfFileOrMacro(StartLoc);
1080 assert(SM.isWrittenInSameFile(StartLoc, NestedLoc));
1081
1082 if (!isBranch && !isRegionAlreadyAdded(StartLoc, NestedLoc))
1083 SourceRegions.emplace_back(Region.getCounter(), StartLoc,
1084 NestedLoc);
1085
1086 StartLoc = getIncludeOrExpansionLoc(StartLoc);
1088 llvm::report_fatal_error(
1089 "File exit not handled before popRegions");
1090 StartDepth--;
1091 }
1092 }
1093 Region.setStartLoc(StartLoc);
1094 Region.setEndLoc(EndLoc);
1095
1096 if (!isBranch) {
1097 MostRecentLocation = EndLoc;
1098
1099
1100 if (StartLoc == getStartOfFileOrMacro(StartLoc) &&
1101 EndLoc == getEndOfFileOrMacro(EndLoc))
1102 MostRecentLocation = getIncludeOrExpansionLoc(EndLoc);
1103 }
1104
1105 assert(SM.isWrittenInSameFile(Region.getBeginLoc(), EndLoc));
1106 assert(SpellingRegion(SM, Region).isInSourceOrder());
1107 SourceRegions.push_back(Region);
1108 }
1109 RegionStack.pop_back();
1110 }
1111 }
1112
1113
1114 SourceMappingRegion &getRegion() {
1115 assert(!RegionStack.empty() && "statement has no region");
1116 return RegionStack.back();
1117 }
1118
1119
1120
1121 Counter propagateCounts(Counter TopCount, const Stmt *S,
1122 bool VisitChildren = true) {
1123 SourceLocation StartLoc = getStart(S);
1124 SourceLocation EndLoc = getEnd(S);
1125 size_t Index = pushRegion(TopCount, StartLoc, EndLoc);
1126 if (VisitChildren)
1127 Visit(S);
1128 Counter ExitCount = getRegion().getCounter();
1129 popRegions(Index);
1130
1131
1132
1133 if (SM.isBeforeInTranslationUnit(StartLoc, S->getBeginLoc()))
1134 MostRecentLocation = EndLoc;
1135
1136 return ExitCount;
1137 }
1138
1139
1140
1141
1142
1143 void createBranchRegion(const Expr *C, Counter TrueCnt, Counter FalseCnt,
1144 const mcdc::ConditionIDs &Conds = {}) {
1145
1146 if ()
1147 return;
1148
1149
1150
1151
1152
1153
1154
1157 mcdc::Parameters BranchParams;
1158 mcdc::ConditionID ID = MCDCBuilder.getCondID(C);
1159 if (ID >= 0)
1160 BranchParams = mcdc::BranchParameters{ID, Conds};
1161
1162
1163
1164
1165
1166
1167
1168 Expr::EvalResult Result;
1170 if (Result.Val.getInt().getBoolValue())
1171 FalseCnt = Counter::getZero();
1172 else
1173 TrueCnt = Counter::getZero();
1174 }
1175 popRegions(
1176 pushRegion(TrueCnt, getStart(C), getEnd(C), FalseCnt, BranchParams));
1177 }
1178 }
1179
1180
1181
1182
1183 void createDecisionRegion(const Expr *C,
1184 const mcdc::DecisionParameters &DecisionParams) {
1185 popRegions(pushRegion(DecisionParams, getStart(C), getEnd(C)));
1186 }
1187
1188
1189
1190
1191 Counter createSwitchCaseRegion(const SwitchCase *SC, Counter ParentCount) {
1192
1193
1194
1195 Counter TrueCnt = getRegionCounter(SC);
1196 popRegions(pushRegion(TrueCnt, getStart(SC), SC->getColonLoc(),
1197 subtractCounters(ParentCount, TrueCnt)));
1198 return TrueCnt;
1199 }
1200
1201
1202
1203 bool isRegionAlreadyAdded(SourceLocation StartLoc, SourceLocation EndLoc,
1204 bool isBranch = false) {
1205 return llvm::any_of(
1206 llvm::reverse(SourceRegions), [&](const SourceMappingRegion &Region) {
1207 return Region.getBeginLoc() == StartLoc &&
1208 Region.getEndLoc() == EndLoc && Region.isBranch() == isBranch;
1209 });
1210 }
1211
1212
1213
1214
1215 void adjustForOutOfOrderTraversal(SourceLocation EndLoc) {
1216 MostRecentLocation = EndLoc;
1217
1218
1219
1220
1221
1223 MostRecentLocation == getEndOfFileOrMacro(MostRecentLocation) &&
1224 isRegionAlreadyAdded(getStartOfFileOrMacro(MostRecentLocation),
1225 MostRecentLocation, getRegion().isBranch()))
1226 MostRecentLocation = getIncludeOrExpansionLoc(MostRecentLocation);
1227 }
1228
1229
1230
1231
1232
1233
1234 void handleFileExit(SourceLocation NewLoc) {
1236 SM.isWrittenInSameFile(MostRecentLocation, NewLoc))
1237 return;
1238
1239
1240
1241 SourceLocation LCA = NewLoc;
1242 FileID ParentFile = SM.getFileID(LCA);
1243 while (!isNestedIn(MostRecentLocation, ParentFile)) {
1244 LCA = getIncludeOrExpansionLoc(LCA);
1245 if (LCA.isInvalid() || SM.isWrittenInSameFile(LCA, MostRecentLocation)) {
1246
1247
1248 MostRecentLocation = NewLoc;
1249 return;
1250 }
1251 ParentFile = SM.getFileID(LCA);
1252 }
1253
1254 llvm::SmallSet<SourceLocation, 8> StartLocs;
1255 std::optional ParentCounter;
1256 for (SourceMappingRegion &I : llvm::reverse(RegionStack)) {
1257 if (!I.hasStartLoc())
1258 continue;
1259 SourceLocation Loc = I.getBeginLoc();
1260 if (!isNestedIn(Loc, ParentFile)) {
1261 ParentCounter = I.getCounter();
1262 break;
1263 }
1264
1265 while (.isInFileID(Loc, ParentFile)) {
1266
1267
1268
1269 if (StartLocs.insert(Loc).second) {
1270 if (I.isBranch())
1271 SourceRegions.emplace_back(I.getCounter(), I.getFalseCounter(),
1272 I.getMCDCParams(), Loc,
1273 getEndOfFileOrMacro(Loc), I.isBranch());
1274 else
1275 SourceRegions.emplace_back(I.getCounter(), Loc,
1276 getEndOfFileOrMacro(Loc));
1277 }
1278 Loc = getIncludeOrExpansionLoc(Loc);
1279 }
1280 I.setStartLoc(getPreciseTokenLocEnd(Loc));
1281 }
1282
1283 if (ParentCounter) {
1284
1285
1286
1287 SourceLocation Loc = MostRecentLocation;
1288 while (isNestedIn(Loc, ParentFile)) {
1289 SourceLocation FileStart = getStartOfFileOrMacro(Loc);
1290 if (StartLocs.insert(FileStart).second) {
1291 SourceRegions.emplace_back(*ParentCounter, FileStart,
1292 getEndOfFileOrMacro(Loc));
1293 assert(SpellingRegion(SM, SourceRegions.back()).isInSourceOrder());
1294 }
1295 Loc = getIncludeOrExpansionLoc(Loc);
1296 }
1297 }
1298
1299 MostRecentLocation = NewLoc;
1300 }
1301
1302
1303 void extendRegion(const Stmt *S) {
1304 SourceMappingRegion &Region = getRegion();
1305 SourceLocation StartLoc = getStart(S);
1306
1307 handleFileExit(StartLoc);
1308 if (!Region.hasStartLoc())
1309 Region.setStartLoc(StartLoc);
1310 }
1311
1312
1313 void terminateRegion(const Stmt *S) {
1314 extendRegion(S);
1315 SourceMappingRegion &Region = getRegion();
1316 SourceLocation EndLoc = getEnd(S);
1317 if (!Region.hasEndLoc())
1318 Region.setEndLoc(EndLoc);
1319 pushRegion(Counter::getZero());
1320 HasTerminateStmt = true;
1321 }
1322
1323
1324 std::optional findGapAreaBetween(SourceLocation AfterLoc,
1325 SourceLocation BeforeLoc) {
1326
1327
1328
1330 return std::nullopt;
1331
1332
1333
1335 FileID FID = SM.getFileID(AfterLoc);
1336 const SrcMgr::ExpansionInfo *EI = &SM.getSLocEntry(FID).getExpansion();
1339 }
1340
1341 size_t StartDepth = locationDepth(AfterLoc);
1342 size_t EndDepth = locationDepth(BeforeLoc);
1343 while (.isWrittenInSameFile(AfterLoc, BeforeLoc)) {
1344 bool UnnestStart = StartDepth >= EndDepth;
1345 bool UnnestEnd = EndDepth >= StartDepth;
1346 if (UnnestEnd) {
1347 assert(SM.isWrittenInSameFile(getStartOfFileOrMacro(BeforeLoc),
1348 BeforeLoc));
1349
1350 BeforeLoc = getIncludeOrExpansionLoc(BeforeLoc);
1351 assert(BeforeLoc.isValid());
1352 EndDepth--;
1353 }
1354 if (UnnestStart) {
1355 assert(SM.isWrittenInSameFile(AfterLoc,
1356 getEndOfFileOrMacro(AfterLoc)));
1357
1358 AfterLoc = getIncludeOrExpansionLoc(AfterLoc);
1359 assert(AfterLoc.isValid());
1360 AfterLoc = getPreciseTokenLocEnd(AfterLoc);
1361 assert(AfterLoc.isValid());
1362 StartDepth--;
1363 }
1364 }
1365 AfterLoc = getPreciseTokenLocEnd(AfterLoc);
1366
1367
1369 return std::nullopt;
1370 if (.isWrittenInSameFile(AfterLoc, BeforeLoc) ||
1371 !SpellingRegion(SM, AfterLoc, BeforeLoc).isInSourceOrder())
1372 return std::nullopt;
1373 return {{AfterLoc, BeforeLoc}};
1374 }
1375
1376
1377 void fillGapAreaWithCount(SourceLocation StartLoc, SourceLocation EndLoc,
1378 Counter Count) {
1379 if (StartLoc == EndLoc)
1380 return;
1381 assert(SpellingRegion(SM, StartLoc, EndLoc).isInSourceOrder());
1382 handleFileExit(StartLoc);
1383 size_t Index = pushRegion(Count, StartLoc, EndLoc);
1385 handleFileExit(EndLoc);
1386 popRegions(Index);
1387 }
1388
1389
1390
1391 std::optional findAreaStartingFromTo(SourceLocation StartingLoc,
1392 SourceLocation BeforeLoc) {
1393
1395 FileID FID = SM.getFileID(StartingLoc);
1396 const SrcMgr::ExpansionInfo *EI = &SM.getSLocEntry(FID).getExpansion();
1399 }
1400
1401 size_t StartDepth = locationDepth(StartingLoc);
1402 size_t EndDepth = locationDepth(BeforeLoc);
1403 while (.isWrittenInSameFile(StartingLoc, BeforeLoc)) {
1404 bool UnnestStart = StartDepth >= EndDepth;
1405 bool UnnestEnd = EndDepth >= StartDepth;
1406 if (UnnestEnd) {
1407 assert(SM.isWrittenInSameFile(getStartOfFileOrMacro(BeforeLoc),
1408 BeforeLoc));
1409
1410 BeforeLoc = getIncludeOrExpansionLoc(BeforeLoc);
1411 assert(BeforeLoc.isValid());
1412 EndDepth--;
1413 }
1414 if (UnnestStart) {
1415 assert(SM.isWrittenInSameFile(StartingLoc,
1416 getStartOfFileOrMacro(StartingLoc)));
1417
1418 StartingLoc = getIncludeOrExpansionLoc(StartingLoc);
1419 assert(StartingLoc.isValid());
1420 StartDepth--;
1421 }
1422 }
1423
1424
1426 return std::nullopt;
1427 if (.isWrittenInSameFile(StartingLoc, BeforeLoc) ||
1428 !SpellingRegion(SM, StartingLoc, BeforeLoc).isInSourceOrder())
1429 return std::nullopt;
1430 return {{StartingLoc, BeforeLoc}};
1431 }
1432
1433 void markSkipped(SourceLocation StartLoc, SourceLocation BeforeLoc) {
1434 const auto Skipped = findAreaStartingFromTo(StartLoc, BeforeLoc);
1435
1436 if (!Skipped)
1437 return;
1438
1439 const auto NewStartLoc = Skipped->getBegin();
1440 const auto EndLoc = Skipped->getEnd();
1441
1442 if (NewStartLoc == EndLoc)
1443 return;
1444 assert(SpellingRegion(SM, NewStartLoc, EndLoc).isInSourceOrder());
1445 handleFileExit(NewStartLoc);
1446 size_t Index = pushRegion(Counter{}, NewStartLoc, EndLoc);
1448 handleFileExit(EndLoc);
1449 popRegions(Index);
1450 }
1451
1452
1453 struct BreakContinue {
1454 Counter BreakCount;
1455 Counter ContinueCount;
1456 };
1457 SmallVector<BreakContinue, 8> BreakContinueStack;
1458
1459 CounterCoverageMappingBuilder(
1460 CoverageMappingModuleGen &CVM,
1461 llvm::DenseMap<const Stmt *, CounterPair> &CounterMap,
1462 MCDC::State &MCDCState, SourceManager &SM, const LangOptions &LangOpts)
1463 : CoverageMappingBuilder(CVM, SM, LangOpts), CounterMap(CounterMap),
1464 MCDCState(MCDCState), MCDCBuilder(CVM.getCodeGenModule(), MCDCState) {}
1465
1466
1467 void write(llvm::raw_ostream &OS) {
1468 llvm::SmallVector<unsigned, 8> VirtualFileMapping;
1469 gatherFileIDs(VirtualFileMapping);
1470 SourceRegionFilter Filter = emitExpansionRegions();
1471 emitSourceRegions(Filter);
1472 gatherSkippedRegions();
1473
1474 if (MappingRegions.empty())
1475 return;
1476
1477 CoverageMappingWriter Writer(VirtualFileMapping, Builder.getExpressions(),
1478 MappingRegions);
1479 Writer.write(OS);
1480 }
1481
1482 void VisitStmt(const Stmt *S) {
1484 extendRegion(S);
1485 const Stmt *LastStmt = nullptr;
1486 bool SaveTerminateStmt = HasTerminateStmt;
1487 HasTerminateStmt = false;
1488 GapRegionCounter = Counter::getZero();
1489 for (const Stmt *Child : S->children())
1490 if (Child) {
1491
1492
1493 if (LastStmt && HasTerminateStmt) {
1494 auto Gap = findGapAreaBetween(getEnd(LastStmt), getStart(Child));
1495 if (Gap)
1496 fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(),
1497 GapRegionCounter);
1498 SaveTerminateStmt = true;
1499 HasTerminateStmt = false;
1500 }
1501 this->Visit(Child);
1502 LastStmt = Child;
1503 }
1504 if (SaveTerminateStmt)
1505 HasTerminateStmt = true;
1506 handleFileExit(getEnd(S));
1507 }
1508
1509 void VisitStmtExpr(const StmtExpr *E) {
1511
1512
1513
1514 HasTerminateStmt = false;
1515 }
1516
1517 void VisitDecl(const Decl *D) {
1518 Stmt *Body = D->getBody();
1519
1520
1521
1523 SM.isInSystemHeader(SM.getSpellingLoc(getStart(Body))))
1524 return;
1525
1526
1527
1528
1529
1530 Counter BodyCounter = getRegionCounter(Body);
1532 if (auto *Method = dyn_cast(D))
1534 if (auto *Ctor = dyn_cast(D)) {
1535 for (auto *Initializer : Ctor->inits()) {
1538 if (getStart(Init).isValid() && getEnd(Init).isValid())
1539 propagateCounts(BodyCounter, Init);
1540 }
1541 }
1542 }
1543
1544 propagateCounts(BodyCounter, Body,
1546 assert(RegionStack.empty() && "Regions entered but never exited");
1547 }
1548
1549 void VisitReturnStmt(const ReturnStmt *S) {
1550 extendRegion(S);
1553 terminateRegion(S);
1554 }
1555
1556 void VisitCoroutineBodyStmt(const CoroutineBodyStmt *S) {
1557 extendRegion(S);
1559 }
1560
1561 void VisitCoreturnStmt(const CoreturnStmt *S) {
1562 extendRegion(S);
1565 terminateRegion(S);
1566 }
1567
1568 void VisitCoroutineSuspendExpr(const CoroutineSuspendExpr *E) {
1570 }
1571
1572 void VisitCXXThrowExpr(const CXXThrowExpr *E) {
1573 extendRegion(E);
1576 terminateRegion(E);
1577 }
1578
1579 void VisitGotoStmt(const GotoStmt *S) { terminateRegion(S); }
1580
1581 void VisitLabelStmt(const LabelStmt *S) {
1582 Counter LabelCount = getRegionCounter(S);
1583 SourceLocation Start = getStart(S);
1584
1585 handleFileExit(Start);
1586 pushRegion(LabelCount, Start);
1588 }
1589
1590 void VisitBreakStmt(const BreakStmt *S) {
1591 assert(!BreakContinueStack.empty() && "break not in a loop or switch!");
1593 BreakContinueStack.back().BreakCount = addCounters(
1594 BreakContinueStack.back().BreakCount, getRegion().getCounter());
1595
1596
1597 terminateRegion(S);
1598 }
1599
1600 void VisitContinueStmt(const ContinueStmt *S) {
1601 assert(!BreakContinueStack.empty() && "continue stmt not in a loop!");
1603 BreakContinueStack.back().ContinueCount = addCounters(
1604 BreakContinueStack.back().ContinueCount, getRegion().getCounter());
1605 terminateRegion(S);
1606 }
1607
1608 void VisitCallExpr(const CallExpr *E) {
1609 VisitStmt(E);
1610
1611
1612
1615 terminateRegion(E);
1616 }
1617
1618 void VisitWhileStmt(const WhileStmt *S) {
1619 extendRegion(S);
1620
1621 Counter ParentCount = getRegion().getCounter();
1623 ? getRegionCounter(S->getBody())
1624 : getRegionCounter(S);
1625
1626
1627 BreakContinueStack.push_back(BreakContinue());
1628 extendRegion(S->getBody());
1629 Counter BackedgeCount = propagateCounts(BodyCount, S->getBody());
1630 BreakContinue BC = BreakContinueStack.pop_back_val();
1631
1632 bool BodyHasTerminateStmt = HasTerminateStmt;
1633 HasTerminateStmt = false;
1634
1635
1636 Counter CondCount =
1638 ? getRegionCounter(S->getCond())
1639 : addCounters(ParentCount, BackedgeCount, BC.ContinueCount);
1640 auto BranchCount = getBranchCounterPair(S, CondCount, getRegionCounter(S));
1641 assert(BranchCount.Executed.isZero() || BranchCount.Executed == BodyCount ||
1643
1644 propagateCounts(CondCount, S->getCond());
1645 adjustForOutOfOrderTraversal(getEnd(S));
1646
1647
1648 auto Gap = findGapAreaBetween(S->getRParenLoc(), getStart(S->getBody()));
1649 if (Gap)
1650 fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(), BodyCount);
1651
1652 assert(
1654 (BC.BreakCount.isZero() && BranchCount.Skipped == getRegionCounter(S)));
1655 Counter OutCount = addCounters(BC.BreakCount, BranchCount.Skipped);
1656 if (!IsCounterEqual(OutCount, ParentCount)) {
1657 pushRegion(OutCount);
1658 GapRegionCounter = OutCount;
1659 if (BodyHasTerminateStmt)
1660 HasTerminateStmt = true;
1661 }
1662
1663
1665 createBranchRegion(S->getCond(), BodyCount, BranchCount.Skipped);
1666 }
1667
1668 void VisitDoStmt(const DoStmt *S) {
1669 extendRegion(S);
1670
1671 Counter ParentCount = getRegion().getCounter();
1673 ? getRegionCounter(S->getBody())
1674 : getRegionCounter(S);
1675
1676 BreakContinueStack.push_back(BreakContinue());
1677 extendRegion(S->getBody());
1678
1679 Counter BackedgeCount;
1681 propagateCounts(BodyCount, S->getBody());
1682 else
1683 BackedgeCount =
1684 propagateCounts(addCounters(ParentCount, BodyCount), S->getBody());
1685
1686 BreakContinue BC = BreakContinueStack.pop_back_val();
1687
1688 bool BodyHasTerminateStmt = HasTerminateStmt;
1689 HasTerminateStmt = false;
1690
1692 ? getRegionCounter(S->getCond())
1693 : addCounters(BackedgeCount, BC.ContinueCount);
1694 auto BranchCount = getBranchCounterPair(S, CondCount, getRegionCounter(S));
1695 assert(BranchCount.Executed.isZero() || BranchCount.Executed == BodyCount ||
1697
1698 propagateCounts(CondCount, S->getCond());
1699
1700 assert(
1702 (BC.BreakCount.isZero() && BranchCount.Skipped == getRegionCounter(S)));
1703 Counter OutCount = addCounters(BC.BreakCount, BranchCount.Skipped);
1704 if (!IsCounterEqual(OutCount, ParentCount)) {
1705 pushRegion(OutCount);
1706 GapRegionCounter = OutCount;
1707 if (BodyHasTerminateStmt)
1708 HasTerminateStmt = true;
1709 }
1710
1711
1713 createBranchRegion(S->getCond(), BodyCount, BranchCount.Skipped);
1714 }
1715
1716 void VisitForStmt(const ForStmt *S) {
1717 extendRegion(S);
1720
1721 Counter ParentCount = getRegion().getCounter();
1723 ? getRegionCounter(S->getBody())
1724 : getRegionCounter(S);
1725
1726
1728 BreakContinueStack.emplace_back();
1729
1730
1731 BreakContinueStack.emplace_back();
1732 extendRegion(S->getBody());
1733 Counter BackedgeCount = propagateCounts(BodyCount, S->getBody());
1734 BreakContinue BodyBC = BreakContinueStack.pop_back_val();
1735
1736 bool BodyHasTerminateStmt = HasTerminateStmt;
1737 HasTerminateStmt = false;
1738
1739
1740
1741 BreakContinue IncrementBC;
1742 if (const Stmt *Inc = S->getInc()) {
1743 Counter IncCount;
1744 if (llvm::EnableSingleByteCoverage)
1745 IncCount = getRegionCounter(S->getInc());
1746 else
1747 IncCount = addCounters(BackedgeCount, BodyBC.ContinueCount);
1748 propagateCounts(IncCount, Inc);
1749 IncrementBC = BreakContinueStack.pop_back_val();
1750 }
1751
1752
1753 Counter CondCount =
1755 ? getRegionCounter(S->getCond())
1756 : addCounters(
1757 addCounters(ParentCount, BackedgeCount, BodyBC.ContinueCount),
1758 IncrementBC.ContinueCount);
1759 auto BranchCount = getBranchCounterPair(S, CondCount, getRegionCounter(S));
1760 assert(BranchCount.Executed.isZero() || BranchCount.Executed == BodyCount ||
1762
1764 propagateCounts(CondCount, Cond);
1765 adjustForOutOfOrderTraversal(getEnd(S));
1766 }
1767
1768
1769 auto Gap = findGapAreaBetween(S->getRParenLoc(), getStart(S->getBody()));
1770 if (Gap)
1771 fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(), BodyCount);
1772
1774 (BodyBC.BreakCount.isZero() && IncrementBC.BreakCount.isZero()));
1775 Counter OutCount = addCounters(BodyBC.BreakCount, IncrementBC.BreakCount,
1776 BranchCount.Skipped);
1777 if (!IsCounterEqual(OutCount, ParentCount)) {
1778 pushRegion(OutCount);
1779 GapRegionCounter = OutCount;
1780 if (BodyHasTerminateStmt)
1781 HasTerminateStmt = true;
1782 }
1783
1784
1786 createBranchRegion(S->getCond(), BodyCount, BranchCount.Skipped);
1787 }
1788
1789 void VisitCXXForRangeStmt(const CXXForRangeStmt *S) {
1790 extendRegion(S);
1795
1796 Counter ParentCount = getRegion().getCounter();
1798 ? getRegionCounter(S->getBody())
1799 : getRegionCounter(S);
1800
1801 BreakContinueStack.push_back(BreakContinue());
1802 extendRegion(S->getBody());
1803 Counter BackedgeCount = propagateCounts(BodyCount, S->getBody());
1804 BreakContinue BC = BreakContinueStack.pop_back_val();
1805
1806 bool BodyHasTerminateStmt = HasTerminateStmt;
1807 HasTerminateStmt = false;
1808
1809
1810 auto Gap = findGapAreaBetween(S->getRParenLoc(), getStart(S->getBody()));
1811 if (Gap)
1812 fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(), BodyCount);
1813
1814 Counter LoopCount =
1815 addCounters(ParentCount, BackedgeCount, BC.ContinueCount);
1816 auto BranchCount = getBranchCounterPair(S, LoopCount, getRegionCounter(S));
1817 assert(BranchCount.Executed.isZero() || BranchCount.Executed == BodyCount ||
1819 assert(
1821 (BC.BreakCount.isZero() && BranchCount.Skipped == getRegionCounter(S)));
1822
1823 Counter OutCount = addCounters(BC.BreakCount, BranchCount.Skipped);
1824 if (!IsCounterEqual(OutCount, ParentCount)) {
1825 pushRegion(OutCount);
1826 GapRegionCounter = OutCount;
1827 if (BodyHasTerminateStmt)
1828 HasTerminateStmt = true;
1829 }
1830
1831
1833 createBranchRegion(S->getCond(), BodyCount, BranchCount.Skipped);
1834 }
1835
1836 void VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S) {
1837 extendRegion(S);
1839
1840 Counter ParentCount = getRegion().getCounter();
1841 Counter BodyCount = getRegionCounter(S);
1842
1843 BreakContinueStack.push_back(BreakContinue());
1844 extendRegion(S->getBody());
1845 Counter BackedgeCount = propagateCounts(BodyCount, S->getBody());
1846 BreakContinue BC = BreakContinueStack.pop_back_val();
1847
1848
1849 auto Gap = findGapAreaBetween(S->getRParenLoc(), getStart(S->getBody()));
1850 if (Gap)
1851 fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(), BodyCount);
1852
1853 Counter LoopCount =
1854 addCounters(ParentCount, BackedgeCount, BC.ContinueCount);
1855 auto BranchCount = getBranchCounterPair(S, LoopCount);
1856 assert(BranchCount.Executed.isZero() || BranchCount.Executed == BodyCount);
1857 Counter OutCount = addCounters(BC.BreakCount, BranchCount.Skipped);
1858 if (!IsCounterEqual(OutCount, ParentCount)) {
1859 pushRegion(OutCount);
1860 GapRegionCounter = OutCount;
1861 }
1862 }
1863
1864 void VisitSwitchStmt(const SwitchStmt *S) {
1865 extendRegion(S);
1869
1870 BreakContinueStack.push_back(BreakContinue());
1871
1872 const Stmt *Body = S->getBody();
1873 extendRegion(Body);
1874 if (const auto *CS = dyn_cast(Body)) {
1875 if (!CS->body_empty()) {
1876
1877
1878
1879 size_t Index = pushRegion(Counter::getZero(), getStart(CS));
1881 Visit(Body);
1882
1883
1884 for (size_t i = RegionStack.size(); i != Index; --i) {
1885 if (!RegionStack[i - 1].hasEndLoc())
1886 RegionStack[i - 1].setEndLoc(getEnd(CS->body_back()));
1887 }
1888
1889 popRegions(Index);
1890 }
1891 } else
1892 propagateCounts(Counter::getZero(), Body);
1893 BreakContinue BC = BreakContinueStack.pop_back_val();
1894
1896 BreakContinueStack.back().ContinueCount = addCounters(
1897 BreakContinueStack.back().ContinueCount, BC.ContinueCount);
1898
1899 Counter ParentCount = getRegion().getCounter();
1900 Counter ExitCount = getRegionCounter(S);
1901 SourceLocation ExitLoc = getEnd(S);
1902 pushRegion(ExitCount);
1903 GapRegionCounter = ExitCount;
1904
1905
1906
1907 MostRecentLocation = getStart(S);
1908 handleFileExit(ExitLoc);
1909
1910
1911
1913 return;
1914
1915
1916
1917 Counter CaseCountSum;
1918 bool HasDefaultCase = false;
1922 auto CaseCount = createSwitchCaseRegion(Case, ParentCount);
1923 CaseCountSum = addCounters(CaseCountSum, CaseCount, false);
1924 }
1925
1926
1927
1928 if (!HasDefaultCase) {
1929
1930
1931
1932 CaseCountSum =
1933 addCounters(CaseCountSum, Counter::getZero(), true);
1934
1935
1936 Counter SwitchFalse = subtractCounters(ParentCount, CaseCountSum);
1937 createBranchRegion(S->getCond(), CaseCountSum, SwitchFalse);
1938 }
1939 }
1940
1941 void VisitSwitchCase(const SwitchCase *S) {
1942 extendRegion(S);
1943
1944 SourceMappingRegion &Parent = getRegion();
1946 ? getRegionCounter(S)
1947 : addCounters(Parent.getCounter(), getRegionCounter(S));
1948
1949
1950
1951 if (Parent.hasStartLoc() && Parent.getBeginLoc() == getStart(S))
1952 Parent.setCounter(Count);
1953 else
1954 pushRegion(Count, getStart(S));
1955
1956 GapRegionCounter = Count;
1957
1958 if (const auto *CS = dyn_cast(S)) {
1959 Visit(CS->getLHS());
1960 if (const Expr *RHS = CS->getRHS())
1961 Visit(RHS);
1962 }
1964 }
1965
1966 void coverIfConsteval(const IfStmt *S) {
1968
1969 const auto *Then = S->getThen();
1970 const auto *Else = S->getElse();
1971
1972
1973
1974
1975 const Counter ParentCount = getRegion().getCounter();
1976
1977 extendRegion(S);
1978
1980
1981 markSkipped(S->getIfLoc(), getStart(Then));
1982 propagateCounts(ParentCount, Then);
1983
1984 if (Else) {
1985
1986 markSkipped(getEnd(Then), getEnd(Else));
1987 }
1988 } else {
1990
1991 markSkipped(S->getIfLoc(), Else ? getStart(Else) : getEnd(Then));
1992
1993 if (Else)
1994 propagateCounts(ParentCount, Else);
1995 }
1996 }
1997
1998 void coverIfConstexpr(const IfStmt *S) {
2000
2001
2002 const bool isTrue =
2005 .getBoolValue();
2006
2007 extendRegion(S);
2008
2009
2010
2011 const Counter ParentCount = getRegion().getCounter();
2012
2013
2014 SourceLocation startOfSkipped = S->getIfLoc();
2015
2017 const auto start = getStart(Init);
2018 const auto end = getEnd(Init);
2019
2020
2021
2022 if (start.isValid() && end.isValid()) {
2023 markSkipped(startOfSkipped, start);
2024 propagateCounts(ParentCount, Init);
2025 startOfSkipped = getEnd(Init);
2026 }
2027 }
2028
2029 const auto *Then = S->getThen();
2030 const auto *Else = S->getElse();
2031
2032 if (isTrue) {
2033
2034 markSkipped(startOfSkipped, getStart(Then));
2035 propagateCounts(ParentCount, Then);
2036
2037 if (Else)
2038
2039 markSkipped(getEnd(Then), getEnd(Else));
2040 } else {
2041
2042 markSkipped(startOfSkipped, Else ? getStart(Else) : getEnd(Then));
2043
2044 if (Else)
2045 propagateCounts(ParentCount, Else);
2046 }
2047 }
2048
2049 void VisitIfStmt(const IfStmt *S) {
2050
2051
2053 return coverIfConsteval(S);
2055 return coverIfConstexpr(S);
2056
2057 extendRegion(S);
2060
2061
2062
2063 extendRegion(S->getCond());
2064
2065 Counter ParentCount = getRegion().getCounter();
2066 auto [ThenCount, ElseCount] =
2068 ? BranchCounterPair{getRegionCounter(S->getThen()),
2070 : Counter::getZero())}
2071 : getBranchCounterPair(S, ParentCount));
2072
2073
2074
2075 propagateCounts(ParentCount, S->getCond());
2076
2077
2078 std::optional Gap =
2080 if (Gap)
2081 fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(), ThenCount);
2082
2083 extendRegion(S->getThen());
2084 Counter OutCount = propagateCounts(ThenCount, S->getThen());
2085
2086 if (const Stmt *Else = S->getElse()) {
2087 bool ThenHasTerminateStmt = HasTerminateStmt;
2088 HasTerminateStmt = false;
2089
2090 std::optional Gap =
2091 findGapAreaBetween(getEnd(S->getThen()), getStart(Else));
2092 if (Gap)
2093 fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(), ElseCount);
2094 extendRegion(Else);
2095
2096 Counter ElseOutCount = propagateCounts(ElseCount, Else);
2098 OutCount = addCounters(OutCount, ElseOutCount);
2099
2100 if (ThenHasTerminateStmt)
2101 HasTerminateStmt = true;
2103 OutCount = addCounters(OutCount, ElseCount);
2104
2106 OutCount = getRegionCounter(S);
2107
2108 if (!IsCounterEqual(OutCount, ParentCount)) {
2109 pushRegion(OutCount);
2110 GapRegionCounter = OutCount;
2111 }
2112
2114
2115 createBranchRegion(S->getCond(), ThenCount, ElseCount);
2116 }
2117
2118 void VisitCXXTryStmt(const CXXTryStmt *S) {
2119 extendRegion(S);
2120
2122
2123 Counter ParentCount = getRegion().getCounter();
2124 propagateCounts(ParentCount, S->getTryBlock());
2125
2126 for (unsigned I = 0, E = S->getNumHandlers(); I < E; ++I)
2128
2129 Counter ExitCount = getRegionCounter(S);
2130 pushRegion(ExitCount);
2131 }
2132
2133 void VisitCXXCatchStmt(const CXXCatchStmt *S) {
2134 propagateCounts(getRegionCounter(S), S->getHandlerBlock());
2135 }
2136
2137 void VisitAbstractConditionalOperator(const AbstractConditionalOperator *E) {
2138 extendRegion(E);
2139
2140 Counter ParentCount = getRegion().getCounter();
2141 auto [TrueCount, FalseCount] =
2143 ? BranchCounterPair{getRegionCounter(E->getTrueExpr()),
2145 : getBranchCounterPair(E, ParentCount));
2146 Counter OutCount;
2147
2148 if (const auto *BCO = dyn_cast(E)) {
2149 propagateCounts(ParentCount, BCO->getCommon());
2150 OutCount = TrueCount;
2151 } else {
2152 propagateCounts(ParentCount, E->getCond());
2153
2154 auto Gap =
2156 if (Gap)
2157 fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(), TrueCount);
2158
2160 OutCount = propagateCounts(TrueCount, E->getTrueExpr());
2161 }
2162
2164 Counter FalseOutCount = propagateCounts(FalseCount, E->getFalseExpr());
2166 OutCount = getRegionCounter(E);
2167 else
2168 OutCount = addCounters(OutCount, FalseOutCount);
2169
2170 if (!IsCounterEqual(OutCount, ParentCount)) {
2171 pushRegion(OutCount);
2172 GapRegionCounter = OutCount;
2173 }
2174
2175
2177 createBranchRegion(E->getCond(), TrueCount, FalseCount);
2178 }
2179
2180 void createOrCancelDecision(const BinaryOperator *E, unsigned Since) {
2181 unsigned NumConds = MCDCBuilder.getTotalConditionsAndReset(E);
2182 if (NumConds == 0)
2183 return;
2184
2185
2186 llvm::SmallVectormcdc::ConditionIDs CondIDs(NumConds);
2187 for (const auto &SR : ArrayRef(SourceRegions).slice(Since)) {
2188 if (SR.isMCDCBranch()) {
2189 auto [ID, Conds] = SR.getMCDCBranchParams();
2190 CondIDs[ID] = Conds;
2191 }
2192 }
2193
2194
2195 mcdc::TVIdxBuilder Builder(CondIDs);
2196 unsigned NumTVs = Builder.NumTestVectors;
2198 assert(MaxTVs < mcdc::TVIdxBuilder::HardMaxTVs);
2199
2200 if (NumTVs > MaxTVs) {
2201
2202 cancelDecision(E, Since, NumTVs, MaxTVs);
2203 return;
2204 }
2205
2206
2210 std::move(Builder.Indices),
2211 };
2212
2213 auto DecisionParams = mcdc::DecisionParameters{
2214 MCDCState.BitmapBits += NumTVs,
2215 NumConds,
2216 };
2217
2218
2219 createDecisionRegion(E, DecisionParams);
2220 }
2221
2222
2223 void cancelDecision(const BinaryOperator *E, unsigned Since, int NumTVs,
2224 int MaxTVs) {
2226 unsigned DiagID =
2228 "unsupported MC/DC boolean expression; "
2229 "number of test vectors (%0) exceeds max (%1). "
2230 "Expression will not be covered");
2231 Diag.Report(E->getBeginLoc(), DiagID) << NumTVs << MaxTVs;
2232
2233
2234 for (auto &SR : MutableArrayRef(SourceRegions).slice(Since)) {
2235 assert(!SR.isMCDCDecision() && "Decision shouldn't be seen here");
2236 if (SR.isMCDCBranch())
2237 SR.resetMCDCParams();
2238 }
2239
2240
2242 }
2243
2244
2245 bool isExprInSystemHeader(const BinaryOperator *E) const {
2248 SM.isInSystemHeader(SM.getSpellingLoc(E->getBeginLoc())) &&
2249 SM.isInSystemHeader(SM.getSpellingLoc(E->getEndLoc())));
2250 }
2251
2252 void VisitBinLAnd(const BinaryOperator *E) {
2253 if (isExprInSystemHeader(E)) {
2254 LeafExprSet.insert(E);
2255 return;
2256 }
2257
2258 bool IsRootNode = MCDCBuilder.isIdle();
2259
2260 unsigned SourceRegionsSince = SourceRegions.size();
2261
2262
2263 MCDCBuilder.pushAndAssignIDs(E);
2264
2265 extendRegion(E->getLHS());
2267 handleFileExit(getEnd(E->getLHS()));
2268
2269
2270 const auto DecisionLHS = MCDCBuilder.pop();
2271
2272 if (auto Gap =
2273 findGapAreaBetween(getEnd(E->getLHS()), getStart(E->getRHS()))) {
2274 fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(), getRegionCounter(E));
2275 }
2276
2277
2278 extendRegion(E->getRHS());
2279 propagateCounts(getRegionCounter(E), E->getRHS());
2280
2282 return;
2283
2284
2285 const auto DecisionRHS = MCDCBuilder.back();
2286
2287
2288 Counter ParentCnt = getRegion().getCounter();
2289
2290
2291 auto [RHSExecCnt, LHSExitCnt] = getBranchCounterPair(E, ParentCnt);
2292
2293
2294 auto [RHSTrueCnt, RHSExitCnt] =
2295 getBranchCounterPair(E->getRHS(), RHSExecCnt);
2296
2297
2298 createBranchRegion(E->getLHS(), RHSExecCnt, LHSExitCnt, DecisionLHS);
2299
2300
2301 createBranchRegion(E->getRHS(), RHSTrueCnt, RHSExitCnt, DecisionRHS);
2302
2303
2304 if (IsRootNode)
2305 createOrCancelDecision(E, SourceRegionsSince);
2306 }
2307
2308
2309 bool shouldVisitRHS(const Expr *LHS) {
2310 bool LHSIsTrue = false;
2311 bool LHSIsConst = false;
2315 return !LHSIsConst || (LHSIsConst && !LHSIsTrue);
2316 }
2317
2318 void VisitBinLOr(const BinaryOperator *E) {
2319 if (isExprInSystemHeader(E)) {
2320 LeafExprSet.insert(E);
2321 return;
2322 }
2323
2324 bool IsRootNode = MCDCBuilder.isIdle();
2325
2326 unsigned SourceRegionsSince = SourceRegions.size();
2327
2328
2329 MCDCBuilder.pushAndAssignIDs(E);
2330
2331 extendRegion(E->getLHS());
2332 Counter OutCount = propagateCounts(getRegion().getCounter(), E->getLHS());
2333 handleFileExit(getEnd(E->getLHS()));
2334
2335
2336 const auto DecisionLHS = MCDCBuilder.pop();
2337
2338 if (auto Gap =
2339 findGapAreaBetween(getEnd(E->getLHS()), getStart(E->getRHS()))) {
2340 fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(), getRegionCounter(E));
2341 }
2342
2343
2344 extendRegion(E->getRHS());
2345 propagateCounts(getRegionCounter(E), E->getRHS());
2346
2348 return;
2349
2350
2351 const auto DecisionRHS = MCDCBuilder.back();
2352
2353
2354 Counter ParentCnt = getRegion().getCounter();
2355
2356
2357 auto [RHSExecCnt, LHSExitCnt] = getBranchCounterPair(E, ParentCnt);
2358
2359
2360 auto [RHSFalseCnt, RHSExitCnt] =
2361 getBranchCounterPair(E->getRHS(), RHSExecCnt);
2362
2363 if (!shouldVisitRHS(E->getLHS())) {
2364 GapRegionCounter = OutCount;
2365 }
2366
2367
2368 createBranchRegion(E->getLHS(), LHSExitCnt, RHSExecCnt, DecisionLHS);
2369
2370
2371 createBranchRegion(E->getRHS(), RHSExitCnt, RHSFalseCnt, DecisionRHS);
2372
2373
2374 if (IsRootNode)
2375 createOrCancelDecision(E, SourceRegionsSince);
2376 }
2377
2378 void VisitLambdaExpr(const LambdaExpr *LE) {
2379
2380
2381 }
2382
2383 void VisitArrayInitLoopExpr(const ArrayInitLoopExpr *AILE) {
2385 }
2386
2387 void VisitPseudoObjectExpr(const PseudoObjectExpr *POE) {
2388
2390 }
2391
2392 void VisitOpaqueValueExpr(const OpaqueValueExpr* OVE) {
2395 }
2396};
2397
2398}
2399
2400static void dump(llvm::raw_ostream &OS, StringRef FunctionName,
2403 OS << FunctionName << ":\n";
2404 CounterMappingContext Ctx(Expressions);
2405 for (const auto &R : Regions) {
2406 OS.indent(2);
2407 switch (R.Kind) {
2408 case CounterMappingRegion::CodeRegion:
2409 break;
2410 case CounterMappingRegion::ExpansionRegion:
2411 OS << "Expansion,";
2412 break;
2413 case CounterMappingRegion::SkippedRegion:
2414 OS << "Skipped,";
2415 break;
2416 case CounterMappingRegion::GapRegion:
2417 OS << "Gap,";
2418 break;
2419 case CounterMappingRegion::BranchRegion:
2420 case CounterMappingRegion::MCDCBranchRegion:
2421 OS << "Branch,";
2422 break;
2423 case CounterMappingRegion::MCDCDecisionRegion:
2424 OS << "Decision,";
2425 break;
2426 }
2427
2428 OS << "File " << R.FileID << ", " << R.LineStart << ":" << R.ColumnStart
2429 << " -> " << R.LineEnd << ":" << R.ColumnEnd << " = ";
2430
2431 if (const auto *DecisionParams =
2432 std::get_ifmcdc::DecisionParameters(&R.MCDCParams)) {
2433 OS << "M:" << DecisionParams->BitmapIdx;
2434 OS << ", C:" << DecisionParams->NumConditions;
2435 } else {
2436 Ctx.dump(R.Count, OS);
2437
2438 if (R.isBranch()) {
2439 OS << ", ";
2440 Ctx.dump(R.FalseCount, OS);
2441 }
2442 }
2443
2444 if (const auto *BranchParams =
2445 std::get_ifmcdc::BranchParameters(&R.MCDCParams)) {
2446 OS << " [" << BranchParams->ID + 1 << ","
2447 << BranchParams->Conds[true] + 1;
2448 OS << "," << BranchParams->Conds[false] + 1 << "] ";
2449 }
2450
2451 if (R.Kind == CounterMappingRegion::ExpansionRegion)
2452 OS << " (Expanded file = " << R.ExpandedFileID << ")";
2453 OS << "\n";
2454 }
2455}
2456
2459 : CGM(CGM), SourceInfo(SourceInfo) {}
2460
2461std::string CoverageMappingModuleGen::getCurrentDirname() {
2463}
2464
2465std::string CoverageMappingModuleGen::normalizeFilename(StringRef Filename) {
2467 llvm::sys::path::remove_dots(Path, true);
2468
2469
2470
2471
2472 for (const auto &[From, To] :
2474 if (llvm::sys::path::replace_path_prefix(Path, From, To))
2475 break;
2476 }
2477 return Path.str().str();
2478}
2479
2481 llvm::InstrProfSectKind SK) {
2482 return llvm::getInstrProfSectionName(
2484}
2485
2486void CoverageMappingModuleGen::emitFunctionMappingRecord(
2487 const FunctionInfo &Info, uint64_t FilenamesRef) {
2488 llvm::LLVMContext &Ctx = CGM.getLLVMContext();
2489
2490
2491 std::string FuncRecordName = "__covrec_" + llvm::utohexstr(Info.NameHash);
2492
2493
2494
2495
2496
2497 if (Info.IsUsed)
2498 FuncRecordName += "u";
2499
2500
2501 const uint64_t NameHash = Info.NameHash;
2502 const uint64_t FuncHash = Info.FuncHash;
2503 const std::string &CoverageMapping = Info.CoverageMapping;
2504#define COVMAP_FUNC_RECORD(Type, LLVMType, Name, Init) LLVMType,
2505 llvm::Type *FunctionRecordTypes[] = {
2506#include "llvm/ProfileData/InstrProfData.inc"
2507 };
2508 auto *FunctionRecordTy =
2509 llvm::StructType::get(Ctx, ArrayRef(FunctionRecordTypes),
2510 true);
2511
2512
2513#define COVMAP_FUNC_RECORD(Type, LLVMType, Name, Init) Init,
2514 llvm::Constant *FunctionRecordVals[] = {
2515 #include "llvm/ProfileData/InstrProfData.inc"
2516 };
2517 auto *FuncRecordConstant =
2518 llvm::ConstantStruct::get(FunctionRecordTy, ArrayRef(FunctionRecordVals));
2519
2520
2521 auto *FuncRecord = new llvm::GlobalVariable(
2522 CGM.getModule(), FunctionRecordTy, true,
2523 llvm::GlobalValue::LinkOnceODRLinkage, FuncRecordConstant,
2524 FuncRecordName);
2525 FuncRecord->setVisibility(llvm::GlobalValue::HiddenVisibility);
2527 FuncRecord->setAlignment(llvm::Align(8));
2528 if (CGM.supportsCOMDAT())
2529 FuncRecord->setComdat(CGM.getModule().getOrInsertComdat(FuncRecordName));
2530
2531
2532 CGM.addUsedGlobal(FuncRecord);
2533}
2534
2536 llvm::GlobalVariable *NamePtr, StringRef NameValue, uint64_t FuncHash,
2537 const std::string &CoverageMapping, bool IsUsed) {
2538 const uint64_t NameHash = llvm::IndexedInstrProf::ComputeHash(NameValue);
2539 FunctionRecords.push_back({NameHash, FuncHash, CoverageMapping, IsUsed});
2540
2541 if (!IsUsed)
2542 FunctionNames.push_back(NamePtr);
2543
2544 if (CGM.getCodeGenOpts().DumpCoverageMapping) {
2545
2546
2547
2548
2549
2551 std::vector Filenames;
2552 std::vector Expressions;
2553 std::vector Regions;
2554 FilenameStrs.resize(FileEntries.size() + 1);
2555 FilenameStrs[0] = normalizeFilename(getCurrentDirname());
2556 for (const auto &Entry : FileEntries) {
2557 auto I = Entry.second;
2558 FilenameStrs[I] = normalizeFilename(Entry.first.getName());
2559 }
2561 RawCoverageMappingReader Reader(CoverageMapping, FilenameRefs, Filenames,
2562 Expressions, Regions);
2563 if (Reader.read())
2564 return;
2565 dump(llvm::outs(), NameValue, Expressions, Regions);
2566 }
2567}
2568
2570 if (FunctionRecords.empty())
2571 return;
2572 llvm::LLVMContext &Ctx = CGM.getLLVMContext();
2573 auto *Int32Ty = llvm::Type::getInt32Ty(Ctx);
2574
2575
2577 FilenameStrs.resize(FileEntries.size() + 1);
2578
2579 FilenameStrs[0] = normalizeFilename(getCurrentDirname());
2580 for (const auto &Entry : FileEntries) {
2581 auto I = Entry.second;
2582 FilenameStrs[I] = normalizeFilename(Entry.first.getName());
2583 }
2584
2585 std::string Filenames;
2586 {
2587 llvm::raw_string_ostream OS(Filenames);
2588 CoverageFilenamesSectionWriter(FilenameStrs).write(OS);
2589 }
2590 auto *FilenamesVal =
2591 llvm::ConstantDataArray::getString(Ctx, Filenames, false);
2592 const int64_t FilenamesRef = llvm::IndexedInstrProf::ComputeHash(Filenames);
2593
2594
2595 for (const FunctionInfo &Info : FunctionRecords)
2596 emitFunctionMappingRecord(Info, FilenamesRef);
2597
2598 const unsigned NRecords = 0;
2599 const size_t FilenamesSize = Filenames.size();
2600 const unsigned CoverageMappingSize = 0;
2601 llvm::Type *CovDataHeaderTypes[] = {
2602#define COVMAP_HEADER(Type, LLVMType, Name, Init) LLVMType,
2603#include "llvm/ProfileData/InstrProfData.inc"
2604 };
2605 auto CovDataHeaderTy =
2606 llvm::StructType::get(Ctx, ArrayRef(CovDataHeaderTypes));
2607 llvm::Constant *CovDataHeaderVals[] = {
2608#define COVMAP_HEADER(Type, LLVMType, Name, Init) Init,
2609#include "llvm/ProfileData/InstrProfData.inc"
2610 };
2611 auto CovDataHeaderVal =
2612 llvm::ConstantStruct::get(CovDataHeaderTy, ArrayRef(CovDataHeaderVals));
2613
2614
2615 llvm::Type *CovDataTypes[] = {CovDataHeaderTy, FilenamesVal->getType()};
2616 auto CovDataTy = llvm::StructType::get(Ctx, ArrayRef(CovDataTypes));
2617 llvm::Constant *TUDataVals[] = {CovDataHeaderVal, FilenamesVal};
2618 auto CovDataVal = llvm::ConstantStruct::get(CovDataTy, ArrayRef(TUDataVals));
2619 auto CovData = new llvm::GlobalVariable(
2620 CGM.getModule(), CovDataTy, true, llvm::GlobalValue::PrivateLinkage,
2621 CovDataVal, llvm::getCoverageMappingVarName());
2622
2624 CovData->setAlignment(llvm::Align(8));
2625
2626
2627 CGM.addUsedGlobal(CovData);
2628
2629 if (!FunctionNames.empty()) {
2630 auto AddrSpace = FunctionNames.front()->getType()->getPointerAddressSpace();
2631 auto NamesArrTy = llvm::ArrayType::get(
2632 llvm::PointerType::get(Ctx, AddrSpace), FunctionNames.size());
2633 auto NamesArrVal = llvm::ConstantArray::get(NamesArrTy, FunctionNames);
2634
2635
2636 new llvm::GlobalVariable(CGM.getModule(), NamesArrTy, true,
2637 llvm::GlobalValue::InternalLinkage, NamesArrVal,
2638 llvm::getCoverageUnusedNamesVarName());
2639 }
2640}
2641
2643 return FileEntries.try_emplace(File, FileEntries.size() + 1).first->second;
2644}
2645
2647 llvm::raw_ostream &OS) {
2648 assert(CounterMap && MCDCState);
2649 CounterCoverageMappingBuilder Walker(CVM, *CounterMap, *MCDCState, SM,
2650 LangOpts);
2651 Walker.VisitDecl(D);
2652 Walker.write(OS);
2653}
2654
2656 llvm::raw_ostream &OS) {
2657 EmptyCoverageMappingBuilder Walker(CVM, SM, LangOpts);
2658 Walker.VisitDecl(D);
2659 Walker.write(OS);
2660}
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)
Definition CoverageMappingGen.cpp:2480
static void dump(llvm::raw_ostream &OS, StringRef FunctionName, ArrayRef< CounterExpression > Expressions, ArrayRef< CounterMappingRegion > Regions)
Definition CoverageMappingGen.cpp:2400
static llvm:🆑:opt< bool > EmptyLineCommentCoverage("emptyline-comment-coverage", llvm:🆑:desc("Emit emptylines and comment lines as skipped regions (only " "disable it on test)"), llvm:🆑:init(true), llvm:🆑:Hidden)
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
Expr * getCond() const
getCond - Return the expression representing the condition for the ?
Expr * getTrueExpr() const
getTrueExpr - Return the subexpression representing the value of the expression if the condition eval...
SourceLocation getQuestionLoc() const
Expr * getFalseExpr() const
getFalseExpr - Return the subexpression representing the value of the expression if the condition eva...
OpaqueValueExpr * getCommonExpr() const
Get the common subexpression shared by all initializations (the source array).
SourceLocation getBeginLoc() const LLVM_READONLY
SourceLocation getOperatorLoc() const
SourceLocation getEndLoc() const LLVM_READONLY
Stmt * getHandlerBlock() const
DeclStmt * getLoopVarStmt()
DeclStmt * getRangeStmt()
SourceLocation getRParenLoc() const
const Expr * getSubExpr() const
CXXCatchStmt * getHandler(unsigned i)
unsigned getNumHandlers() const
CompoundStmt * getTryBlock()
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.
static bool isInstrumentedCondition(const Expr *C)
isInstrumentedCondition - Determine whether the given condition is an instrumentable condition (i....
static const Expr * stripCond(const Expr *C)
Ignore parentheses and logical-NOT to track conditions consistently.
This class organizes the cross-function state that is used while generating LLVM code.
DiagnosticsEngine & getDiags() const
ASTContext & getContext() const
const CodeGenOptions & getCodeGenOpts() const
void emitEmptyMapping(const Decl *D, llvm::raw_ostream &OS)
Emit the coverage mapping data for an unused function.
Definition CoverageMappingGen.cpp:2655
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...
Definition CoverageMappingGen.cpp:2646
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.
Definition CoverageMappingGen.cpp:2535
CoverageSourceInfo & getSourceInfo() const
static CoverageSourceInfo * setUpCoverageCallbacks(Preprocessor &PP)
Definition CoverageMappingGen.cpp:59
CoverageMappingModuleGen(CodeGenModule &CGM, CoverageSourceInfo &SourceInfo)
Definition CoverageMappingGen.cpp:2457
void emit()
Emit the coverage mapping data for a translation unit.
Definition CoverageMappingGen.cpp:2569
CodeGenModule & getCodeGenModule()
Return an interface into CodeGenModule.
unsigned getFileID(FileEntryRef File)
Return the coverage mapping translation unit file id for the given file.
Definition CoverageMappingGen.cpp:2642
Expr * getOperand() const
Retrieve the operand of the 'co_return' statement.
CompoundStmt * getBody() const
Retrieve the body of the coroutine as written.
Expr * getOperand() const
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.
Definition CoverageMappingGen.cpp:88
void updateNextTokLoc(SourceLocation Loc)
Definition CoverageMappingGen.cpp:101
void AddSkippedRange(SourceRange Range, SkippedRange::Kind RangeKind)
Definition CoverageMappingGen.cpp:77
std::vector< SkippedRange > & getSkippedRanges()
bool HandleComment(Preprocessor &PP, SourceRange Range) override
Definition CoverageMappingGen.cpp:96
SourceLocation PrevTokLoc
void HandleEmptyline(SourceRange Range) override
Definition CoverageMappingGen.cpp:92
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...
bool isValueDependent() const
Determines whether the value of this expression depends on.
llvm::APSInt EvaluateKnownConstInt(const ASTContext &Ctx) const
EvaluateKnownConstInt - Call EvaluateAsRValue and return the folded integer.
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...
SourceLocation getRParenLoc() const
SourceLocation getIfLoc() const
bool isNonNegatedConsteval() const
bool isNegatedConsteval() const
SourceLocation getRParenLoc() const
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 ...
SourceLocation getRParenLoc() const
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.
Expr * getSyntacticForm()
Return the syntactic form of this expression, i.e.
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.
A trivial tuple used to represent a source range.
SourceLocation getExpansionLocStart() const
bool isFunctionMacroExpansion() const
SourceLocation getExpansionLocEnd() const
CompoundStmt * getSubStmt()
SourceLocation getEndLoc() const LLVM_READONLY
SourceLocation getBeginLoc() const LLVM_READONLY
SourceLocation getColonLoc() const
const SwitchCase * getNextSwitchCase() const
SwitchCase * getSwitchCaseList()
const llvm::Triple & getTriple() const
Returns the target triple of the primary target.
Token - This structure provides full information about a lexed token.
SourceLocation getRParenLoc() const
@ Decl
The l-value was an access to a declared entity or something equivalently strong, like the address of ...
The JSON file list parser is used to communicate input to InstallAPI.
bool isa(CodeGen::Address addr)
if(T->getSizeExpr()) TRY_TO(TraverseStmt(const_cast< Expr * >(T -> getSizeExpr())))
FunctionType::ExtInfo getFunctionExtInfo(const Type &t)
@ Result
The result type of a method or function.
cl::opt< bool > SystemHeadersCoverage
Diagnostic wrappers for TextAPI types for error reporting.
cl::opt< bool > EnableSingleByteCoverage
llvm::DenseMap< const Stmt *, Branch > BranchByStmt
llvm::DenseMap< const Stmt *, Decision > DecisionByStmt