clang: lib/CodeGen/CoverageMappingGen.cpp Source File (original) (raw)

1

2

3

4

5

6

7

8

9

10

11

12

18#include "llvm/ADT/DenseSet.h"

19#include "llvm/ADT/SmallSet.h"

20#include "llvm/ADT/StringExtras.h"

21#include "llvm/ProfileData/Coverage/CoverageMapping.h"

22#include "llvm/ProfileData/Coverage/CoverageMappingReader.h"

23#include "llvm/ProfileData/Coverage/CoverageMappingWriter.h"

24#include "llvm/Support/FileSystem.h"

25#include "llvm/Support/Path.h"

26#include

27

28

29

30#define COVMAP_V3

31

32namespace llvm {

33cl::opt

35 llvm:🆑:ZeroOrMore,

36 llvm:🆑:desc("Enable single byte coverage"),

37 llvm:🆑:Hidden, llvm:🆑:init(false));

38}

39

41 "emptyline-comment-coverage",

42 llvm:🆑:desc("Emit emptylines and comment lines as skipped regions (only "

43 "disable it on test)"),

44 llvm:🆑:init(true), llvm:🆑:Hidden);

45

48 "system-headers-coverage",

49 cl::desc("Enable collecting coverage from system headers"), cl::init(false),

50 cl::Hidden);

51}

52

53using namespace clang;

54using namespace CodeGen;

56

61 PP.addPPCallbacks(std::unique_ptr(CoverageInfo));

67

69 if (Tok.getKind() != clang::tok::eod)

71 });

72 }

73 return CoverageInfo;

74}

75

79 PrevTokLoc == SkippedRanges.back().PrevTokLoc &&

80 SourceMgr.isWrittenInSameFile(SkippedRanges.back().Range.getEnd(),

82 SkippedRanges.back().Range.setEnd(Range.getEnd());

83 else

84 SkippedRanges.push_back({Range, RangeKind, PrevTokLoc});

85}

86

89}

90

93}

94

97 return false;

98}

99

101 if (!SkippedRanges.empty() && SkippedRanges.back().NextTokLoc.isInvalid())

102 SkippedRanges.back().NextTokLoc = Loc;

103}

104

105namespace {

106

107class SourceMappingRegion {

108

109 Counter Count;

110

111

112 std::optional FalseCount;

113

114

115 mcdc::Parameters MCDCParams;

116

117

118 std::optional LocStart;

119

120

121 std::optional LocEnd;

122

123

124

125 bool GapRegion;

126

127

128

129 bool SkippedRegion;

130

131public:

132 SourceMappingRegion(Counter Count, std::optional LocStart,

133 std::optional LocEnd,

134 bool GapRegion = false)

135 : Count(Count), LocStart(LocStart), LocEnd(LocEnd), GapRegion(GapRegion),

136 SkippedRegion(false) {}

137

138 SourceMappingRegion(Counter Count, std::optional FalseCount,

139 mcdc::Parameters MCDCParams,

140 std::optional LocStart,

141 std::optional LocEnd,

142 bool GapRegion = false)

143 : Count(Count), FalseCount(FalseCount), MCDCParams(MCDCParams),

144 LocStart(LocStart), LocEnd(LocEnd), GapRegion(GapRegion),

145 SkippedRegion(false) {}

146

147 SourceMappingRegion(mcdc::Parameters MCDCParams,

148 std::optional LocStart,

149 std::optional LocEnd)

150 : MCDCParams(MCDCParams), LocStart(LocStart), LocEnd(LocEnd),

151 GapRegion(false), SkippedRegion(false) {}

152

153 const Counter &getCounter() const { return Count; }

154

155 const Counter &getFalseCounter() const {

156 assert(FalseCount && "Region has no alternate counter");

157 return *FalseCount;

158 }

159

160 void setCounter(Counter C) { Count = C; }

161

162 bool hasStartLoc() const { return LocStart.has_value(); }

163

165

167 assert(LocStart && "Region has no start location");

168 return *LocStart;

169 }

170

171 bool hasEndLoc() const { return LocEnd.has_value(); }

172

174 assert(Loc.isValid() && "Setting an invalid end location");

175 LocEnd = Loc;

176 }

177

179 assert(LocEnd && "Region has no end location");

180 return *LocEnd;

181 }

182

183 bool isGap() const { return GapRegion; }

184

185 void setGap(bool Gap) { GapRegion = Gap; }

186

187 bool isSkipped() const { return SkippedRegion; }

188

189 void setSkipped(bool Skipped) { SkippedRegion = Skipped; }

190

191 bool isBranch() const { return FalseCount.has_value(); }

192

193 bool isMCDCBranch() const {

194 return std::holds_alternativemcdc::BranchParameters(MCDCParams);

195 }

196

197 const auto &getMCDCBranchParams() const {

198 return mcdc::getParams(MCDCParams);

199 }

200

201 bool isMCDCDecision() const {

202 return std::holds_alternativemcdc::DecisionParameters(MCDCParams);

203 }

204

205 const auto &getMCDCDecisionParams() const {

206 return mcdc::getParams(MCDCParams);

207 }

208

209 const mcdc::Parameters &getMCDCParams() const { return MCDCParams; }

210

211 void resetMCDCParams() { MCDCParams = mcdc::Parameters(); }

212};

213

214

215struct SpellingRegion {

216

217 unsigned LineStart;

218

219

220 unsigned ColumnStart;

221

222

223 unsigned LineEnd;

224

225

226 unsigned ColumnEnd;

227

230 LineStart = SM.getSpellingLineNumber(LocStart);

231 ColumnStart = SM.getSpellingColumnNumber(LocStart);

232 LineEnd = SM.getSpellingLineNumber(LocEnd);

233 ColumnEnd = SM.getSpellingColumnNumber(LocEnd);

234 }

235

236 SpellingRegion(SourceManager &SM, SourceMappingRegion &R)

237 : SpellingRegion(SM, R.getBeginLoc(), R.getEndLoc()) {}

238

239

240

241 bool isInSourceOrder() const {

242 return (LineStart < LineEnd) ||

243 (LineStart == LineEnd && ColumnStart <= ColumnEnd);

244 }

245};

246

247

248

249class CoverageMappingBuilder {

250public:

254

255private:

256

257 llvm::SmallDenseMap<FileID, std::pair<unsigned, SourceLocation>, 8>

258 FileIDMapping;

259

260public:

261

263

264 std::vector SourceRegions;

265

266

267

268

269

270

271 typedef llvm::SmallSet<std::pair<SourceLocation, SourceLocation>, 8>

272 SourceRegionFilter;

273

276 : CVM(CVM), SM(SM), LangOpts(LangOpts) {}

277

278

280

281

282 unsigned TokLen =

285 }

286

287

291 return SM.getLocForStartOfFile(SM.getFileID(Loc));

292 }

293

294

298 SM.getFileOffset(Loc));

299 return SM.getLocForEndOfFile(SM.getFileID(Loc));

300 }

301

302

303

304

305

306

307

308

309 std::pair<SourceLocation, std::optional>

311 std::optional EndLoc = std::nullopt;

313 SM.isWrittenInScratchSpace(SM.getSpellingLoc(Loc))) {

314 auto ExpansionRange = SM.getImmediateExpansionRange(Loc);

315 Loc = ExpansionRange.getBegin();

316 EndLoc = ExpansionRange.getEnd();

317 }

318 return std::make_pair(Loc, EndLoc);

319 }

320

321

322

323

325 bool AcceptScratch = true) {

327 return SM.getIncludeLoc(SM.getFileID(Loc));

328 Loc = SM.getImmediateExpansionRange(Loc).getBegin();

329 if (AcceptScratch)

330 return Loc;

331 return getNonScratchExpansionLoc(Loc).first;

332 }

333

334

336 return SM.getBufferName(SM.getSpellingLoc(Loc)) == "";

337 }

338

339

341 do {

342 Loc = getIncludeOrExpansionLoc(Loc);

344 return false;

346 return true;

347 }

348

349

352 while (SM.isMacroArgExpansion(Loc) || isInBuiltin(Loc))

353 Loc = SM.getImmediateExpansionRange(Loc).getBegin();

354 return Loc;

355 }

356

357

360 while (SM.isMacroArgExpansion(Loc) || isInBuiltin(Loc))

361 Loc = SM.getImmediateExpansionRange(Loc).getBegin();

362 return getPreciseTokenLocEnd(Loc);

363 }

364

365

366

367

368

369

371 FileIDMapping.clear();

372

373 llvm::SmallSet<FileID, 8> Visited;

375 for (auto &Region : SourceRegions) {

377

378

379 auto NonScratchExpansionLoc = getNonScratchExpansionLoc(Loc);

380 auto EndLoc = NonScratchExpansionLoc.second;

381 if (EndLoc.has_value()) {

382 Loc = NonScratchExpansionLoc.first;

383 Region.setStartLoc(Loc);

384 Region.setEndLoc(EndLoc.value());

385 }

386

387

389 auto BeginLoc = SM.getSpellingLoc(Loc);

390 auto EndLoc = SM.getSpellingLoc(Region.getEndLoc());

391 if (SM.isWrittenInSameFile(BeginLoc, EndLoc)) {

393 Region.setStartLoc(Loc);

394 Region.setEndLoc(SM.getFileLoc(Region.getEndLoc()));

395 }

396 }

397

400 continue;

401

403 SM.isInSystemHeader(SM.getSpellingLoc(Loc)));

404

405 unsigned Depth = 0;

408 ++Depth;

409 FileLocs.push_back(std::make_pair(Loc, Depth));

410 }

411 llvm::stable_sort(FileLocs, llvm::less_second());

412

413 for (const auto &FL : FileLocs) {

415 FileID SpellingFile = SM.getDecomposedSpellingLoc(Loc).first;

416 auto Entry = SM.getFileEntryRefForID(SpellingFile);

417 if (!Entry)

418 continue;

419

420 FileIDMapping[SM.getFileID(Loc)] = std::make_pair(Mapping.size(), Loc);

421 Mapping.push_back(CVM.getFileID(*Entry));

422 }

423 }

424

425

426

427

429 auto Mapping = FileIDMapping.find(SM.getFileID(Loc));

430 if (Mapping != FileIDMapping.end())

431 return Mapping->second.first;

432 return std::nullopt;

433 }

434

435

436

437

438

439

440 std::optional adjustSkippedRange(SourceManager &SM,

445 SpellingRegion SR{SM, LocStart, LocEnd};

446 SR.ColumnStart = 1;

447 if (PrevTokLoc.isValid() && SM.isWrittenInSameFile(LocStart, PrevTokLoc) &&

448 SR.LineStart == SM.getSpellingLineNumber(PrevTokLoc))

449 SR.LineStart++;

450 if (NextTokLoc.isValid() && SM.isWrittenInSameFile(LocEnd, NextTokLoc) &&

451 SR.LineEnd == SM.getSpellingLineNumber(NextTokLoc)) {

452 SR.LineEnd--;

453 SR.ColumnEnd++;

454 }

455 if (SR.isInSourceOrder())

456 return SR;

457 return std::nullopt;

458 }

459

460

461

462 void gatherSkippedRegions() {

463

464

466 FileLineRanges.resize(

467 FileIDMapping.size(),

468 std::make_pair(std::numeric_limits::max(), 0));

469 for (const auto &R : MappingRegions) {

470 FileLineRanges[R.FileID].first =

471 std::min(FileLineRanges[R.FileID].first, R.LineStart);

472 FileLineRanges[R.FileID].second =

473 std::max(FileLineRanges[R.FileID].second, R.LineEnd);

474 }

475

477 for (auto &I : SkippedRanges) {

481 assert(SM.isWrittenInSameFile(LocStart, LocEnd) &&

482 "region spans multiple files");

483

484 auto CovFileID = getCoverageFileID(LocStart);

485 if (!CovFileID)

486 continue;

487 std::optional SR;

488 if (I.isComment())

489 SR = adjustSkippedRange(SM, LocStart, LocEnd, I.PrevTokLoc,

490 I.NextTokLoc);

491 else if (I.isPPIfElse() || I.isEmptyLine())

492 SR = {SM, LocStart, LocEnd};

493

494 if (!SR)

495 continue;

496 auto Region = CounterMappingRegion::makeSkipped(

497 *CovFileID, SR->LineStart, SR->ColumnStart, SR->LineEnd,

498 SR->ColumnEnd);

499

500

501 if (Region.LineStart >= FileLineRanges[*CovFileID].first &&

502 Region.LineEnd <= FileLineRanges[*CovFileID].second)

503 MappingRegions.push_back(Region);

504 }

505 }

506

507

508

509 void emitSourceRegions(const SourceRegionFilter &Filter) {

510 for (const auto &Region : SourceRegions) {

511 assert(Region.hasEndLoc() && "incomplete region");

512

514 assert(SM.getFileID(LocStart).isValid() && "region in invalid file");

515

516

517

519 SM.isInSystemHeader(SM.getSpellingLoc(LocStart))) {

520 assert(!Region.isMCDCBranch() && !Region.isMCDCDecision() &&

521 "Don't suppress the condition in system headers");

522 continue;

523 }

524

525 auto CovFileID = getCoverageFileID(LocStart);

526

527 if (!CovFileID) {

528 assert(!Region.isMCDCBranch() && !Region.isMCDCDecision() &&

529 "Don't suppress the condition in non-file regions");

530 continue;

531 }

532

534 assert(SM.isWrittenInSameFile(LocStart, LocEnd) &&

535 "region spans multiple files");

536

537

538

539

540

541 if (Filter.count(std::make_pair(LocStart, LocEnd))) {

542 assert(!Region.isMCDCBranch() && !Region.isMCDCDecision() &&

543 "Don't suppress the condition");

544 continue;

545 }

546

547

548 SpellingRegion SR{SM, LocStart, LocEnd};

549 assert(SR.isInSourceOrder() && "region start and end out of order");

550

551 if (Region.isGap()) {

552 MappingRegions.push_back(CounterMappingRegion::makeGapRegion(

553 Region.getCounter(), *CovFileID, SR.LineStart, SR.ColumnStart,

554 SR.LineEnd, SR.ColumnEnd));

555 } else if (Region.isSkipped()) {

556 MappingRegions.push_back(CounterMappingRegion::makeSkipped(

557 *CovFileID, SR.LineStart, SR.ColumnStart, SR.LineEnd,

558 SR.ColumnEnd));

559 } else if (Region.isBranch()) {

560 MappingRegions.push_back(CounterMappingRegion::makeBranchRegion(

561 Region.getCounter(), Region.getFalseCounter(), *CovFileID,

562 SR.LineStart, SR.ColumnStart, SR.LineEnd, SR.ColumnEnd,

563 Region.getMCDCParams()));

564 } else if (Region.isMCDCDecision()) {

565 MappingRegions.push_back(CounterMappingRegion::makeDecisionRegion(

566 Region.getMCDCDecisionParams(), *CovFileID, SR.LineStart,

567 SR.ColumnStart, SR.LineEnd, SR.ColumnEnd));

568 } else {

569 MappingRegions.push_back(CounterMappingRegion::makeRegion(

570 Region.getCounter(), *CovFileID, SR.LineStart, SR.ColumnStart,

571 SR.LineEnd, SR.ColumnEnd));

572 }

573 }

574 }

575

576

577 SourceRegionFilter emitExpansionRegions() {

578 SourceRegionFilter Filter;

579 for (const auto &FM : FileIDMapping) {

581 SourceLocation ParentLoc = getIncludeOrExpansionLoc(ExpandedLoc, false);

583 continue;

584

585 auto ParentFileID = getCoverageFileID(ParentLoc);

586 if (!ParentFileID)

587 continue;

588 auto ExpandedFileID = getCoverageFileID(ExpandedLoc);

589 assert(ExpandedFileID && "expansion in uncovered file");

590

591 SourceLocation LocEnd = getPreciseTokenLocEnd(ParentLoc);

592 assert(SM.isWrittenInSameFile(ParentLoc, LocEnd) &&

593 "region spans multiple files");

594 Filter.insert(std::make_pair(ParentLoc, LocEnd));

595

596 SpellingRegion SR{SM, ParentLoc, LocEnd};

597 assert(SR.isInSourceOrder() && "region start and end out of order");

598 MappingRegions.push_back(CounterMappingRegion::makeExpansion(

599 *ParentFileID, *ExpandedFileID, SR.LineStart, SR.ColumnStart,

600 SR.LineEnd, SR.ColumnEnd));

601 }

603 }

604};

605

606

607

608struct EmptyCoverageMappingBuilder : public CoverageMappingBuilder {

611 : CoverageMappingBuilder(CVM, SM, LangOpts) {}

612

613 void VisitDecl(const Decl *D) {

615 return;

619 if (SM.isWrittenInSameFile(Start, End)) {

620

621

622 FileID StartFileID = SM.getFileID(Start);

623 FileID EndFileID = SM.getFileID(End);

624 while (StartFileID != EndFileID && !isNestedIn(End, StartFileID)) {

625 Start = getIncludeOrExpansionLoc(Start);

626 assert(Start.isValid() &&

627 "Declaration start location not nested within a known region");

628 StartFileID = SM.getFileID(Start);

629 }

630 while (StartFileID != EndFileID) {

631 End = getPreciseTokenLocEnd(getIncludeOrExpansionLoc(End));

632 assert(End.isValid() &&

633 "Declaration end location not nested within a known region");

634 EndFileID = SM.getFileID(End);

635 }

636 }

637 SourceRegions.emplace_back(Counter(), Start, End);

638 }

639

640

641 void write(llvm::raw_ostream &OS) {

643 gatherFileIDs(FileIDMapping);

644 emitSourceRegions(SourceRegionFilter());

645

646 if (MappingRegions.empty())

647 return;

648

649 CoverageMappingWriter Writer(FileIDMapping, {}, MappingRegions);

650 Writer.write(OS);

651 }

652};

653

654

655

656

657

658

659

660

661

662struct MCDCCoverageBuilder {

663

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

751private:

753

756 const Stmt *DecisionStmt = nullptr;

757 mcdc::ConditionID NextID = 0;

758 bool NotMapped = false;

759

760

761

762 static constexpr mcdc::ConditionIDs DecisionStackSentinel{-1, -1};

763

764

766 return E->getOpcode() == BO_LAnd;

767 }

768

769public:

771 : CGM(CGM), DecisionStack(1, DecisionStackSentinel),

772 MCDCState(MCDCState) {}

773

774

775

776

777 bool isIdle() const { return (NextID == 0 && !NotMapped); }

778

779

780

781

782 bool isBuilding() const { return (NextID > 0); }

783

784

785 void setCondID(const Expr *Cond, mcdc::ConditionID ID) {

786 MCDCState.BranchByStmt[CodeGenFunction::stripCond(Cond)] = {ID,

787 DecisionStmt};

788 }

789

790

791 mcdc::ConditionID getCondID(const Expr *Cond) const {

792 auto I = MCDCState.BranchByStmt.find(CodeGenFunction::stripCond(Cond));

794 return -1;

795 else

796 return I->second.ID;

797 }

798

799

800 const mcdc::ConditionIDs &back() const { return DecisionStack.back(); }

801

802

803

804

807 return;

808

809

810 if (!isBuilding() &&

811 !MCDCState.DecisionByStmt.contains(CodeGenFunction::stripCond(E)))

812 NotMapped = true;

813

814

815 if (NotMapped)

816 return;

817

818 if (NextID == 0) {

819 DecisionStmt = E;

821 }

822

823 const mcdc::ConditionIDs &ParentDecision = DecisionStack.back();

824

825

826

827

828 if (MCDCState.BranchByStmt.contains(CodeGenFunction::stripCond(E)))

829 setCondID(E->getLHS(), getCondID(E));

830 else

831 setCondID(E->getLHS(), NextID++);

832

833

834 mcdc::ConditionID RHSid = NextID++;

835 setCondID(E->getRHS(), RHSid);

836

837

838 if (isLAnd(E))

839 DecisionStack.push_back({ParentDecision[false], RHSid});

840 else

841 DecisionStack.push_back({RHSid, ParentDecision[true]});

842 }

843

844

845 mcdc::ConditionIDs pop() {

847 return DecisionStackSentinel;

848

849 assert(DecisionStack.size() > 1);

850 return DecisionStack.pop_back_val();

851 }

852

853

854

855 unsigned getTotalConditionsAndReset(const BinaryOperator *E) {

857 return 0;

858

859 assert(!isIdle());

860 assert(DecisionStack.size() == 1);

861

862

863 if (NotMapped) {

864 NotMapped = false;

865 assert(NextID == 0);

866 return 0;

867 }

868

869

870 unsigned TotalConds = NextID;

871

872

873 NextID = 0;

874

875 return TotalConds;

876 }

877};

878

879

880

881struct CounterCoverageMappingBuilder

882 : public CoverageMappingBuilder,

884

885 llvm::DenseMap<const Stmt *, CounterPair> &CounterMap;

886

888

889

891

892

893

894 llvm::DenseSet<const Stmt *> LeafExprSet;

895

896

897 MCDCCoverageBuilder MCDCBuilder;

898

899 CounterExpressionBuilder Builder;

900

901

902

903

904

906

907

908 bool HasTerminateStmt = false;

909

910

911 Counter GapRegionCounter;

912

913

914 Counter subtractCounters(Counter LHS, Counter RHS, bool Simplify = true) {

916 "cannot add counters when single byte coverage mode is enabled");

917 return Builder.subtract(LHS, RHS, Simplify);

918 }

919

920

921 Counter addCounters(Counter LHS, Counter RHS, bool Simplify = true) {

922 return Builder.add(LHS, RHS, Simplify);

923 }

924

925 Counter addCounters(Counter C1, Counter C2, Counter C3,

926 bool Simplify = true) {

927 return addCounters(addCounters(C1, C2, Simplify), C3, Simplify);

928 }

929

930

931

932

933 Counter getRegionCounter(const Stmt *S) {

934 return Counter::getCounter(CounterMap[S].Executed);

935 }

936

937 struct BranchCounterPair {

938 Counter Executed;

939 Counter Skipped;

940 };

941

942

943

944

945

946

947

948

949

950

951

952

953

954

955

956

957

958 BranchCounterPair

959 getBranchCounterPair(const Stmt *S, Counter ParentCnt,

960 std::optional SkipCntForOld = std::nullopt) {

961 Counter ExecCnt = getRegionCounter(S);

962

963

964

966 assert(SkipCntForOld &&

967 "SingleByte must provide SkipCntForOld as a fake Skipped count.");

968 return {ExecCnt, *SkipCntForOld};

969 }

970

971 return {ExecCnt, Builder.subtract(ParentCnt, ExecCnt)};

972 }

973

974 bool IsCounterEqual(Counter OutCount, Counter ParentCount) {

975 if (OutCount == ParentCount)

976 return true;

977

978 return false;

979 }

980

981

982

983

984

985 size_t pushRegion(Counter Count,

986 std::optional StartLoc = std::nullopt,

987 std::optional EndLoc = std::nullopt,

988 std::optional FalseCount = std::nullopt,

989 const mcdc::Parameters &BranchParams = std::monostate()) {

990

991 if (StartLoc && !FalseCount) {

992 MostRecentLocation = *StartLoc;

993 }

994

995

996

997 assert((!StartLoc || StartLoc->isValid()) && "Start location is not valid");

998 assert((!EndLoc || EndLoc->isValid()) && "End location is not valid");

999

1000

1001

1002

1003

1004 if (StartLoc && StartLoc->isInvalid())

1005 StartLoc = std::nullopt;

1006 if (EndLoc && EndLoc->isInvalid())

1007 EndLoc = std::nullopt;

1008 RegionStack.emplace_back(Count, FalseCount, BranchParams, StartLoc, EndLoc);

1009

1010 return RegionStack.size() - 1;

1011 }

1012

1013 size_t pushRegion(const mcdc::DecisionParameters &DecisionParams,

1014 std::optional StartLoc = std::nullopt,

1015 std::optional EndLoc = std::nullopt) {

1016

1017 RegionStack.emplace_back(DecisionParams, StartLoc, EndLoc);

1018

1019 return RegionStack.size() - 1;

1020 }

1021

1023 size_t Depth = 0;

1025 Loc = getIncludeOrExpansionLoc(Loc);

1026 Depth++;

1027 }

1028 return Depth;

1029 }

1030

1031

1032

1033

1034

1035 void popRegions(size_t ParentIndex) {

1036 assert(RegionStack.size() >= ParentIndex && "parent not in stack");

1037 while (RegionStack.size() > ParentIndex) {

1038 SourceMappingRegion &Region = RegionStack.back();

1039 if (Region.hasStartLoc() &&

1040 (Region.hasEndLoc() || RegionStack[ParentIndex].hasEndLoc())) {

1043 ? Region.getEndLoc()

1044 : RegionStack[ParentIndex].getEndLoc();

1045 bool isBranch = Region.isBranch();

1046 size_t StartDepth = locationDepth(StartLoc);

1047 size_t EndDepth = locationDepth(EndLoc);

1048 while (SM.isWrittenInSameFile(StartLoc, EndLoc)) {

1049 bool UnnestStart = StartDepth >= EndDepth;

1050 bool UnnestEnd = EndDepth >= StartDepth;

1051 if (UnnestEnd) {

1052

1053

1054

1055

1056

1057

1058 SourceLocation NestedLoc = getStartOfFileOrMacro(EndLoc);

1059 assert(SM.isWrittenInSameFile(NestedLoc, EndLoc));

1060

1061 if (!isBranch && !isRegionAlreadyAdded(NestedLoc, EndLoc))

1062 SourceRegions.emplace_back(Region.getCounter(), NestedLoc,

1063 EndLoc);

1064

1065 EndLoc = getPreciseTokenLocEnd(getIncludeOrExpansionLoc(EndLoc));

1067 llvm::report_fatal_error(

1068 "File exit not handled before popRegions");

1069 EndDepth--;

1070 }

1071 if (UnnestStart) {

1072

1073

1074

1075

1076

1077

1078 SourceLocation NestedLoc = getEndOfFileOrMacro(StartLoc);

1079 assert(SM.isWrittenInSameFile(StartLoc, NestedLoc));

1080

1081 if (!isBranch && !isRegionAlreadyAdded(StartLoc, NestedLoc))

1082 SourceRegions.emplace_back(Region.getCounter(), StartLoc,

1083 NestedLoc);

1084

1085 StartLoc = getIncludeOrExpansionLoc(StartLoc);

1087 llvm::report_fatal_error(

1088 "File exit not handled before popRegions");

1089 StartDepth--;

1090 }

1091 }

1092 Region.setStartLoc(StartLoc);

1093 Region.setEndLoc(EndLoc);

1094

1095 if (!isBranch) {

1096 MostRecentLocation = EndLoc;

1097

1098

1099 if (StartLoc == getStartOfFileOrMacro(StartLoc) &&

1100 EndLoc == getEndOfFileOrMacro(EndLoc))

1101 MostRecentLocation = getIncludeOrExpansionLoc(EndLoc);

1102 }

1103

1104 assert(SM.isWrittenInSameFile(Region.getBeginLoc(), EndLoc));

1105 assert(SpellingRegion(SM, Region).isInSourceOrder());

1106 SourceRegions.push_back(Region);

1107 }

1108 RegionStack.pop_back();

1109 }

1110 }

1111

1112

1113 SourceMappingRegion &getRegion() {

1114 assert(!RegionStack.empty() && "statement has no region");

1115 return RegionStack.back();

1116 }

1117

1118

1119

1120 Counter propagateCounts(Counter TopCount, const Stmt *S,

1121 bool VisitChildren = true) {

1124 size_t Index = pushRegion(TopCount, StartLoc, EndLoc);

1125 if (VisitChildren)

1126 Visit(S);

1127 Counter ExitCount = getRegion().getCounter();

1128 popRegions(Index);

1129

1130

1131

1132 if (SM.isBeforeInTranslationUnit(StartLoc, S->getBeginLoc()))

1133 MostRecentLocation = EndLoc;

1134

1135 return ExitCount;

1136 }

1137

1138

1139

1140

1141

1142 void createBranchRegion(const Expr *C, Counter TrueCnt, Counter FalseCnt,

1143 const mcdc::ConditionIDs &Conds = {}) {

1144

1145 if (C)

1146 return;

1147

1148

1149

1150

1151

1152

1153

1154 if (CodeGenFunction::isInstrumentedCondition(C) ||

1155 LeafExprSet.count(CodeGenFunction::stripCond(C))) {

1156 mcdc::Parameters BranchParams;

1157 mcdc::ConditionID ID = MCDCBuilder.getCondID(C);

1158 if (ID >= 0)

1159 BranchParams = mcdc::BranchParameters{ID, Conds};

1160

1161

1162

1163

1164

1165

1166

1169 if (Result.Val.getInt().getBoolValue())

1170 FalseCnt = Counter::getZero();

1171 else

1172 TrueCnt = Counter::getZero();

1173 }

1174 popRegions(

1175 pushRegion(TrueCnt, getStart(C), getEnd(C), FalseCnt, BranchParams));

1176 }

1177 }

1178

1179

1180

1181

1182 void createDecisionRegion(const Expr *C,

1183 const mcdc::DecisionParameters &DecisionParams) {

1184 popRegions(pushRegion(DecisionParams, getStart(C), getEnd(C)));

1185 }

1186

1187

1188

1189

1190 Counter createSwitchCaseRegion(const SwitchCase *SC, Counter ParentCount) {

1191

1192

1193

1194 Counter TrueCnt = getRegionCounter(SC);

1195 popRegions(pushRegion(TrueCnt, getStart(SC), SC->getColonLoc(),

1196 subtractCounters(ParentCount, TrueCnt)));

1197 return TrueCnt;

1198 }

1199

1200

1201

1203 bool isBranch = false) {

1204 return llvm::any_of(

1205 llvm::reverse(SourceRegions), [&](const SourceMappingRegion &Region) {

1206 return Region.getBeginLoc() == StartLoc &&

1207 Region.getEndLoc() == EndLoc && Region.isBranch() == isBranch;

1208 });

1209 }

1210

1211

1212

1213

1214 void adjustForOutOfOrderTraversal(SourceLocation EndLoc) {

1215 MostRecentLocation = EndLoc;

1216

1217

1218

1219

1220

1222 MostRecentLocation == getEndOfFileOrMacro(MostRecentLocation) &&

1223 isRegionAlreadyAdded(getStartOfFileOrMacro(MostRecentLocation),

1224 MostRecentLocation, getRegion().isBranch()))

1225 MostRecentLocation = getIncludeOrExpansionLoc(MostRecentLocation);

1226 }

1227

1228

1229

1230

1231

1232

1235 SM.isWrittenInSameFile(MostRecentLocation, NewLoc))

1236 return;

1237

1238

1239

1241 FileID ParentFile = SM.getFileID(LCA);

1242 while (!isNestedIn(MostRecentLocation, ParentFile)) {

1243 LCA = getIncludeOrExpansionLoc(LCA);

1244 if (LCA.isInvalid() || SM.isWrittenInSameFile(LCA, MostRecentLocation)) {

1245

1246

1247 MostRecentLocation = NewLoc;

1248 return;

1249 }

1250 ParentFile = SM.getFileID(LCA);

1251 }

1252

1253 llvm::SmallSet<SourceLocation, 8> StartLocs;

1254 std::optional ParentCounter;

1255 for (SourceMappingRegion &I : llvm::reverse(RegionStack)) {

1256 if (!I.hasStartLoc())

1257 continue;

1259 if (!isNestedIn(Loc, ParentFile)) {

1260 ParentCounter = I.getCounter();

1261 break;

1262 }

1263

1264 while (SM.isInFileID(Loc, ParentFile)) {

1265

1266

1267

1268 if (StartLocs.insert(Loc).second) {

1269 if (I.isBranch())

1270 SourceRegions.emplace_back(I.getCounter(), I.getFalseCounter(),

1271 I.getMCDCParams(), Loc,

1272 getEndOfFileOrMacro(Loc), I.isBranch());

1273 else

1274 SourceRegions.emplace_back(I.getCounter(), Loc,

1275 getEndOfFileOrMacro(Loc));

1276 }

1277 Loc = getIncludeOrExpansionLoc(Loc);

1278 }

1279 I.setStartLoc(getPreciseTokenLocEnd(Loc));

1280 }

1281

1282 if (ParentCounter) {

1283

1284

1285

1287 while (isNestedIn(Loc, ParentFile)) {

1289 if (StartLocs.insert(FileStart).second) {

1290 SourceRegions.emplace_back(*ParentCounter, FileStart,

1291 getEndOfFileOrMacro(Loc));

1292 assert(SpellingRegion(SM, SourceRegions.back()).isInSourceOrder());

1293 }

1294 Loc = getIncludeOrExpansionLoc(Loc);

1295 }

1296 }

1297

1298 MostRecentLocation = NewLoc;

1299 }

1300

1301

1302 void extendRegion(const Stmt *S) {

1303 SourceMappingRegion &Region = getRegion();

1305

1306 handleFileExit(StartLoc);

1307 if (!Region.hasStartLoc())

1308 Region.setStartLoc(StartLoc);

1309 }

1310

1311

1312 void terminateRegion(const Stmt *S) {

1313 extendRegion(S);

1314 SourceMappingRegion &Region = getRegion();

1316 if (!Region.hasEndLoc())

1317 Region.setEndLoc(EndLoc);

1318 pushRegion(Counter::getZero());

1319 HasTerminateStmt = true;

1320 }

1321

1322

1323 std::optional findGapAreaBetween(SourceLocation AfterLoc,

1325

1326

1327

1329 return std::nullopt;

1330

1331

1332

1334 FileID FID = SM.getFileID(AfterLoc);

1338 }

1339

1340 size_t StartDepth = locationDepth(AfterLoc);

1341 size_t EndDepth = locationDepth(BeforeLoc);

1342 while (SM.isWrittenInSameFile(AfterLoc, BeforeLoc)) {

1343 bool UnnestStart = StartDepth >= EndDepth;

1344 bool UnnestEnd = EndDepth >= StartDepth;

1345 if (UnnestEnd) {

1346 assert(SM.isWrittenInSameFile(getStartOfFileOrMacro(BeforeLoc),

1347 BeforeLoc));

1348

1349 BeforeLoc = getIncludeOrExpansionLoc(BeforeLoc);

1350 assert(BeforeLoc.isValid());

1351 EndDepth--;

1352 }

1353 if (UnnestStart) {

1354 assert(SM.isWrittenInSameFile(AfterLoc,

1355 getEndOfFileOrMacro(AfterLoc)));

1356

1357 AfterLoc = getIncludeOrExpansionLoc(AfterLoc);

1358 assert(AfterLoc.isValid());

1359 AfterLoc = getPreciseTokenLocEnd(AfterLoc);

1360 assert(AfterLoc.isValid());

1361 StartDepth--;

1362 }

1363 }

1364 AfterLoc = getPreciseTokenLocEnd(AfterLoc);

1365

1366

1368 return std::nullopt;

1369 if (SM.isWrittenInSameFile(AfterLoc, BeforeLoc) ||

1370 !SpellingRegion(SM, AfterLoc, BeforeLoc).isInSourceOrder())

1371 return std::nullopt;

1372 return {{AfterLoc, BeforeLoc}};

1373 }

1374

1375

1377 Counter Count) {

1378 if (StartLoc == EndLoc)

1379 return;

1380 assert(SpellingRegion(SM, StartLoc, EndLoc).isInSourceOrder());

1381 handleFileExit(StartLoc);

1382 size_t Index = pushRegion(Count, StartLoc, EndLoc);

1384 handleFileExit(EndLoc);

1385 popRegions(Index);

1386 }

1387

1388

1389

1390 std::optional findAreaStartingFromTo(SourceLocation StartingLoc,

1392

1394 FileID FID = SM.getFileID(StartingLoc);

1398 }

1399

1400 size_t StartDepth = locationDepth(StartingLoc);

1401 size_t EndDepth = locationDepth(BeforeLoc);

1402 while (SM.isWrittenInSameFile(StartingLoc, BeforeLoc)) {

1403 bool UnnestStart = StartDepth >= EndDepth;

1404 bool UnnestEnd = EndDepth >= StartDepth;

1405 if (UnnestEnd) {

1406 assert(SM.isWrittenInSameFile(getStartOfFileOrMacro(BeforeLoc),

1407 BeforeLoc));

1408

1409 BeforeLoc = getIncludeOrExpansionLoc(BeforeLoc);

1410 assert(BeforeLoc.isValid());

1411 EndDepth--;

1412 }

1413 if (UnnestStart) {

1414 assert(SM.isWrittenInSameFile(StartingLoc,

1415 getStartOfFileOrMacro(StartingLoc)));

1416

1417 StartingLoc = getIncludeOrExpansionLoc(StartingLoc);

1418 assert(StartingLoc.isValid());

1419 StartDepth--;

1420 }

1421 }

1422

1423

1425 return std::nullopt;

1426 if (SM.isWrittenInSameFile(StartingLoc, BeforeLoc) ||

1427 !SpellingRegion(SM, StartingLoc, BeforeLoc).isInSourceOrder())

1428 return std::nullopt;

1429 return {{StartingLoc, BeforeLoc}};

1430 }

1431

1433 const auto Skipped = findAreaStartingFromTo(StartLoc, BeforeLoc);

1434

1435 if (!Skipped)

1436 return;

1437

1438 const auto NewStartLoc = Skipped->getBegin();

1439 const auto EndLoc = Skipped->getEnd();

1440

1441 if (NewStartLoc == EndLoc)

1442 return;

1443 assert(SpellingRegion(SM, NewStartLoc, EndLoc).isInSourceOrder());

1444 handleFileExit(NewStartLoc);

1445 size_t Index = pushRegion(Counter{}, NewStartLoc, EndLoc);

1447 handleFileExit(EndLoc);

1448 popRegions(Index);

1449 }

1450

1451

1452 struct BreakContinue {

1453 Counter BreakCount;

1454 Counter ContinueCount;

1455 };

1457

1458 CounterCoverageMappingBuilder(

1460 llvm::DenseMap<const Stmt *, CounterPair> &CounterMap,

1462 : CoverageMappingBuilder(CVM, SM, LangOpts), CounterMap(CounterMap),

1463 MCDCState(MCDCState), MCDCBuilder(CVM.getCodeGenModule(), MCDCState) {}

1464

1465

1466 void write(llvm::raw_ostream &OS) {

1468 gatherFileIDs(VirtualFileMapping);

1469 SourceRegionFilter Filter = emitExpansionRegions();

1470 emitSourceRegions(Filter);

1471 gatherSkippedRegions();

1472

1473 if (MappingRegions.empty())

1474 return;

1475

1476 CoverageMappingWriter Writer(VirtualFileMapping, Builder.getExpressions(),

1477 MappingRegions);

1478 Writer.write(OS);

1479 }

1480

1481 void VisitStmt(const Stmt *S) {

1482 if (S->getBeginLoc().isValid())

1483 extendRegion(S);

1484 const Stmt *LastStmt = nullptr;

1485 bool SaveTerminateStmt = HasTerminateStmt;

1486 HasTerminateStmt = false;

1487 GapRegionCounter = Counter::getZero();

1488 for (const Stmt *Child : S->children())

1489 if (Child) {

1490

1491

1492 if (LastStmt && HasTerminateStmt) {

1493 auto Gap = findGapAreaBetween(getEnd(LastStmt), getStart(Child));

1494 if (Gap)

1495 fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(),

1496 GapRegionCounter);

1497 SaveTerminateStmt = true;

1498 HasTerminateStmt = false;

1499 }

1500 this->Visit(Child);

1501 LastStmt = Child;

1502 }

1503 if (SaveTerminateStmt)

1504 HasTerminateStmt = true;

1505 handleFileExit(getEnd(S));

1506 }

1507

1508 void VisitDecl(const Decl *D) {

1510

1511

1512

1514 SM.isInSystemHeader(SM.getSpellingLoc(getStart(Body))))

1515 return;

1516

1517

1518

1519

1520

1521 Counter BodyCounter = getRegionCounter(Body);

1523 if (auto *Method = dyn_cast(D))

1524 Defaulted = Method->isDefaulted();

1525 if (auto *Ctor = dyn_cast(D)) {

1526 for (auto *Initializer : Ctor->inits()) {

1529 if (getStart(Init).isValid() && getEnd(Init).isValid())

1530 propagateCounts(BodyCounter, Init);

1531 }

1532 }

1533 }

1534

1535 propagateCounts(BodyCounter, Body,

1537 assert(RegionStack.empty() && "Regions entered but never exited");

1538 }

1539

1540 void VisitReturnStmt(const ReturnStmt *S) {

1541 extendRegion(S);

1542 if (S->getRetValue())

1543 Visit(S->getRetValue());

1544 terminateRegion(S);

1545 }

1546

1548 extendRegion(S);

1549 Visit(S->getBody());

1550 }

1551

1552 void VisitCoreturnStmt(const CoreturnStmt *S) {

1553 extendRegion(S);

1554 if (S->getOperand())

1555 Visit(S->getOperand());

1556 terminateRegion(S);

1557 }

1558

1560 Visit(E->getOperand());

1561 }

1562

1564 extendRegion(E);

1565 if (E->getSubExpr())

1566 Visit(E->getSubExpr());

1567 terminateRegion(E);

1568 }

1569

1570 void VisitGotoStmt(const GotoStmt *S) { terminateRegion(S); }

1571

1572 void VisitLabelStmt(const LabelStmt *S) {

1573 Counter LabelCount = getRegionCounter(S);

1575

1576 handleFileExit(Start);

1577 pushRegion(LabelCount, Start);

1578 Visit(S->getSubStmt());

1579 }

1580

1581 void VisitBreakStmt(const BreakStmt *S) {

1582 assert(!BreakContinueStack.empty() && "break not in a loop or switch!");

1584 BreakContinueStack.back().BreakCount = addCounters(

1585 BreakContinueStack.back().BreakCount, getRegion().getCounter());

1586

1587

1588 terminateRegion(S);

1589 }

1590

1591 void VisitContinueStmt(const ContinueStmt *S) {

1592 assert(!BreakContinueStack.empty() && "continue stmt not in a loop!");

1594 BreakContinueStack.back().ContinueCount = addCounters(

1595 BreakContinueStack.back().ContinueCount, getRegion().getCounter());

1596 terminateRegion(S);

1597 }

1598

1599 void VisitCallExpr(const CallExpr *E) {

1600 VisitStmt(E);

1601

1602

1603

1606 terminateRegion(E);

1607 }

1608

1609 void VisitWhileStmt(const WhileStmt *S) {

1610 extendRegion(S);

1611

1612 Counter ParentCount = getRegion().getCounter();

1614 ? getRegionCounter(S->getBody())

1615 : getRegionCounter(S);

1616

1617

1618 BreakContinueStack.push_back(BreakContinue());

1619 extendRegion(S->getBody());

1620 Counter BackedgeCount = propagateCounts(BodyCount, S->getBody());

1621 BreakContinue BC = BreakContinueStack.pop_back_val();

1622

1623 bool BodyHasTerminateStmt = HasTerminateStmt;

1624 HasTerminateStmt = false;

1625

1626

1627 Counter CondCount =

1629 ? getRegionCounter(S->getCond())

1630 : addCounters(ParentCount, BackedgeCount, BC.ContinueCount);

1631 auto BranchCount = getBranchCounterPair(S, CondCount, getRegionCounter(S));

1632 assert(BranchCount.Executed.isZero() || BranchCount.Executed == BodyCount ||

1634

1635 propagateCounts(CondCount, S->getCond());

1636 adjustForOutOfOrderTraversal(getEnd(S));

1637

1638

1639 auto Gap = findGapAreaBetween(S->getRParenLoc(), getStart(S->getBody()));

1640 if (Gap)

1641 fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(), BodyCount);

1642

1643 assert(

1645 (BC.BreakCount.isZero() && BranchCount.Skipped == getRegionCounter(S)));

1646 Counter OutCount = addCounters(BC.BreakCount, BranchCount.Skipped);

1647 if (!IsCounterEqual(OutCount, ParentCount)) {

1648 pushRegion(OutCount);

1649 GapRegionCounter = OutCount;

1650 if (BodyHasTerminateStmt)

1651 HasTerminateStmt = true;

1652 }

1653

1654

1656 createBranchRegion(S->getCond(), BodyCount, BranchCount.Skipped);

1657 }

1658

1659 void VisitDoStmt(const DoStmt *S) {

1660 extendRegion(S);

1661

1662 Counter ParentCount = getRegion().getCounter();

1664 ? getRegionCounter(S->getBody())

1665 : getRegionCounter(S);

1666

1667 BreakContinueStack.push_back(BreakContinue());

1668 extendRegion(S->getBody());

1669

1670 Counter BackedgeCount;

1672 propagateCounts(BodyCount, S->getBody());

1673 else

1674 BackedgeCount =

1675 propagateCounts(addCounters(ParentCount, BodyCount), S->getBody());

1676

1677 BreakContinue BC = BreakContinueStack.pop_back_val();

1678

1679 bool BodyHasTerminateStmt = HasTerminateStmt;

1680 HasTerminateStmt = false;

1681

1683 ? getRegionCounter(S->getCond())

1684 : addCounters(BackedgeCount, BC.ContinueCount);

1685 auto BranchCount = getBranchCounterPair(S, CondCount, getRegionCounter(S));

1686 assert(BranchCount.Executed.isZero() || BranchCount.Executed == BodyCount ||

1688

1689 propagateCounts(CondCount, S->getCond());

1690

1691 assert(

1693 (BC.BreakCount.isZero() && BranchCount.Skipped == getRegionCounter(S)));

1694 Counter OutCount = addCounters(BC.BreakCount, BranchCount.Skipped);

1695 if (!IsCounterEqual(OutCount, ParentCount)) {

1696 pushRegion(OutCount);

1697 GapRegionCounter = OutCount;

1698 }

1699

1700

1702 createBranchRegion(S->getCond(), BodyCount, BranchCount.Skipped);

1703

1704 if (BodyHasTerminateStmt)

1705 HasTerminateStmt = true;

1706 }

1707

1708 void VisitForStmt(const ForStmt *S) {

1709 extendRegion(S);

1710 if (S->getInit())

1711 Visit(S->getInit());

1712

1713 Counter ParentCount = getRegion().getCounter();

1715 ? getRegionCounter(S->getBody())

1716 : getRegionCounter(S);

1717

1718

1719 if (S->getInc())

1720 BreakContinueStack.emplace_back();

1721

1722

1723 BreakContinueStack.emplace_back();

1724 extendRegion(S->getBody());

1725 Counter BackedgeCount = propagateCounts(BodyCount, S->getBody());

1726 BreakContinue BodyBC = BreakContinueStack.pop_back_val();

1727

1728 bool BodyHasTerminateStmt = HasTerminateStmt;

1729 HasTerminateStmt = false;

1730

1731

1732

1733 BreakContinue IncrementBC;

1734 if (const Stmt *Inc = S->getInc()) {

1735 Counter IncCount;

1737 IncCount = getRegionCounter(S->getInc());

1738 else

1739 IncCount = addCounters(BackedgeCount, BodyBC.ContinueCount);

1740 propagateCounts(IncCount, Inc);

1741 IncrementBC = BreakContinueStack.pop_back_val();

1742 }

1743

1744

1745 Counter CondCount =

1747 ? getRegionCounter(S->getCond())

1748 : addCounters(

1749 addCounters(ParentCount, BackedgeCount, BodyBC.ContinueCount),

1750 IncrementBC.ContinueCount);

1751 auto BranchCount = getBranchCounterPair(S, CondCount, getRegionCounter(S));

1752 assert(BranchCount.Executed.isZero() || BranchCount.Executed == BodyCount ||

1754

1755 if (const Expr *Cond = S->getCond()) {

1756 propagateCounts(CondCount, Cond);

1757 adjustForOutOfOrderTraversal(getEnd(S));

1758 }

1759

1760

1761 auto Gap = findGapAreaBetween(S->getRParenLoc(), getStart(S->getBody()));

1762 if (Gap)

1763 fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(), BodyCount);

1764

1766 (BodyBC.BreakCount.isZero() && IncrementBC.BreakCount.isZero()));

1767 Counter OutCount = addCounters(BodyBC.BreakCount, IncrementBC.BreakCount,

1768 BranchCount.Skipped);

1769 if (!IsCounterEqual(OutCount, ParentCount)) {

1770 pushRegion(OutCount);

1771 GapRegionCounter = OutCount;

1772 if (BodyHasTerminateStmt)

1773 HasTerminateStmt = true;

1774 }

1775

1776

1778 createBranchRegion(S->getCond(), BodyCount, BranchCount.Skipped);

1779 }

1780

1782 extendRegion(S);

1783 if (S->getInit())

1784 Visit(S->getInit());

1785 Visit(S->getLoopVarStmt());

1786 Visit(S->getRangeStmt());

1787

1788 Counter ParentCount = getRegion().getCounter();

1790 ? getRegionCounter(S->getBody())

1791 : getRegionCounter(S);

1792

1793 BreakContinueStack.push_back(BreakContinue());

1794 extendRegion(S->getBody());

1795 Counter BackedgeCount = propagateCounts(BodyCount, S->getBody());

1796 BreakContinue BC = BreakContinueStack.pop_back_val();

1797

1798 bool BodyHasTerminateStmt = HasTerminateStmt;

1799 HasTerminateStmt = false;

1800

1801

1802 auto Gap = findGapAreaBetween(S->getRParenLoc(), getStart(S->getBody()));

1803 if (Gap)

1804 fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(), BodyCount);

1805

1806 Counter LoopCount =

1807 addCounters(ParentCount, BackedgeCount, BC.ContinueCount);

1808 auto BranchCount = getBranchCounterPair(S, LoopCount, getRegionCounter(S));

1809 assert(BranchCount.Executed.isZero() || BranchCount.Executed == BodyCount ||

1811 assert(

1813 (BC.BreakCount.isZero() && BranchCount.Skipped == getRegionCounter(S)));

1814

1815 Counter OutCount = addCounters(BC.BreakCount, BranchCount.Skipped);

1816 if (!IsCounterEqual(OutCount, ParentCount)) {

1817 pushRegion(OutCount);

1818 GapRegionCounter = OutCount;

1819 if (BodyHasTerminateStmt)

1820 HasTerminateStmt = true;

1821 }

1822

1823

1825 createBranchRegion(S->getCond(), BodyCount, BranchCount.Skipped);

1826 }

1827

1829 extendRegion(S);

1830 Visit(S->getElement());

1831

1832 Counter ParentCount = getRegion().getCounter();

1833 Counter BodyCount = getRegionCounter(S);

1834

1835 BreakContinueStack.push_back(BreakContinue());

1836 extendRegion(S->getBody());

1837 Counter BackedgeCount = propagateCounts(BodyCount, S->getBody());

1838 BreakContinue BC = BreakContinueStack.pop_back_val();

1839

1840

1841 auto Gap = findGapAreaBetween(S->getRParenLoc(), getStart(S->getBody()));

1842 if (Gap)

1843 fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(), BodyCount);

1844

1845 Counter LoopCount =

1846 addCounters(ParentCount, BackedgeCount, BC.ContinueCount);

1847 auto BranchCount = getBranchCounterPair(S, LoopCount);

1848 assert(BranchCount.Executed.isZero() || BranchCount.Executed == BodyCount);

1849 Counter OutCount = addCounters(BC.BreakCount, BranchCount.Skipped);

1850 if (!IsCounterEqual(OutCount, ParentCount)) {

1851 pushRegion(OutCount);

1852 GapRegionCounter = OutCount;

1853 }

1854 }

1855

1856 void VisitSwitchStmt(const SwitchStmt *S) {

1857 extendRegion(S);

1858 if (S->getInit())

1859 Visit(S->getInit());

1860 Visit(S->getCond());

1861

1862 BreakContinueStack.push_back(BreakContinue());

1863

1864 const Stmt *Body = S->getBody();

1865 extendRegion(Body);

1866 if (const auto *CS = dyn_cast(Body)) {

1867 if (!CS->body_empty()) {

1868

1869

1870

1871 size_t Index = pushRegion(Counter::getZero(), getStart(CS));

1873 Visit(Body);

1874

1875

1876 for (size_t i = RegionStack.size(); i != Index; --i) {

1877 if (!RegionStack[i - 1].hasEndLoc())

1878 RegionStack[i - 1].setEndLoc(getEnd(CS->body_back()));

1879 }

1880

1881 popRegions(Index);

1882 }

1883 } else

1884 propagateCounts(Counter::getZero(), Body);

1885 BreakContinue BC = BreakContinueStack.pop_back_val();

1886

1888 BreakContinueStack.back().ContinueCount = addCounters(

1889 BreakContinueStack.back().ContinueCount, BC.ContinueCount);

1890

1891 Counter ParentCount = getRegion().getCounter();

1892 Counter ExitCount = getRegionCounter(S);

1894 pushRegion(ExitCount);

1895 GapRegionCounter = ExitCount;

1896

1897

1898

1899 MostRecentLocation = getStart(S);

1900 handleFileExit(ExitLoc);

1901

1902

1903

1905 return;

1906

1907

1908

1909 Counter CaseCountSum;

1910 bool HasDefaultCase = false;

1911 const SwitchCase *Case = S->getSwitchCaseList();

1913 HasDefaultCase = HasDefaultCase || isa(Case);

1914 auto CaseCount = createSwitchCaseRegion(Case, ParentCount);

1915 CaseCountSum = addCounters(CaseCountSum, CaseCount, false);

1916 }

1917

1918

1919

1920 if (!HasDefaultCase) {

1921

1922

1923

1924 CaseCountSum =

1925 addCounters(CaseCountSum, Counter::getZero(), true);

1926

1927

1928 Counter SwitchFalse = subtractCounters(ParentCount, CaseCountSum);

1929 createBranchRegion(S->getCond(), CaseCountSum, SwitchFalse);

1930 }

1931 }

1932

1933 void VisitSwitchCase(const SwitchCase *S) {

1934 extendRegion(S);

1935

1938 ? getRegionCounter(S)

1939 : addCounters(Parent.getCounter(), getRegionCounter(S));

1940

1941

1942

1943 if (Parent.hasStartLoc() && Parent.getBeginLoc() == getStart(S))

1944 Parent.setCounter(Count);

1945 else

1946 pushRegion(Count, getStart(S));

1947

1948 GapRegionCounter = Count;

1949

1950 if (const auto *CS = dyn_cast(S)) {

1951 Visit(CS->getLHS());

1952 if (const Expr *RHS = CS->getRHS())

1953 Visit(RHS);

1954 }

1955 Visit(S->getSubStmt());

1956 }

1957

1958 void coverIfConsteval(const IfStmt *S) {

1959 assert(S->isConsteval());

1960

1961 const auto *Then = S->getThen();

1962 const auto *Else = S->getElse();

1963

1964

1965

1966

1967 const Counter ParentCount = getRegion().getCounter();

1968

1969 extendRegion(S);

1970

1971 if (S->isNegatedConsteval()) {

1972

1973 markSkipped(S->getIfLoc(), getStart(Then));

1974 propagateCounts(ParentCount, Then);

1975

1976 if (Else) {

1977

1978 markSkipped(getEnd(Then), getEnd(Else));

1979 }

1980 } else {

1981 assert(S->isNonNegatedConsteval());

1982

1983 markSkipped(S->getIfLoc(), Else ? getStart(Else) : getEnd(Then));

1984

1985 if (Else)

1986 propagateCounts(ParentCount, Else);

1987 }

1988 }

1989

1990 void coverIfConstexpr(const IfStmt *S) {

1991 assert(S->isConstexpr());

1992

1993

1994 const bool isTrue =

1995 S->getCond()

1997 .getBoolValue();

1998

1999 extendRegion(S);

2000

2001

2002

2003 const Counter ParentCount = getRegion().getCounter();

2004

2005

2007

2008 if (const auto *Init = S->getInit()) {

2009 const auto start = getStart(Init);

2010 const auto end = getEnd(Init);

2011

2012

2013

2014 if (start.isValid() && end.isValid()) {

2015 markSkipped(startOfSkipped, start);

2016 propagateCounts(ParentCount, Init);

2017 startOfSkipped = getEnd(Init);

2018 }

2019 }

2020

2021 const auto *Then = S->getThen();

2022 const auto *Else = S->getElse();

2023

2024 if (isTrue) {

2025

2026 markSkipped(startOfSkipped, getStart(Then));

2027 propagateCounts(ParentCount, Then);

2028

2029 if (Else)

2030

2031 markSkipped(getEnd(Then), getEnd(Else));

2032 } else {

2033

2034 markSkipped(startOfSkipped, Else ? getStart(Else) : getEnd(Then));

2035

2036 if (Else)

2037 propagateCounts(ParentCount, Else);

2038 }

2039 }

2040

2041 void VisitIfStmt(const IfStmt *S) {

2042

2043

2044 if (S->isConsteval())

2045 return coverIfConsteval(S);

2046 else if (S->isConstexpr())

2047 return coverIfConstexpr(S);

2048

2049 extendRegion(S);

2050 if (S->getInit())

2051 Visit(S->getInit());

2052

2053

2054

2055 extendRegion(S->getCond());

2056

2057 Counter 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));

2064

2065

2066

2067 propagateCounts(ParentCount, S->getCond());

2068

2069

2070 std::optional Gap =

2071 findGapAreaBetween(S->getRParenLoc(), getStart(S->getThen()));

2072 if (Gap)

2073 fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(), ThenCount);

2074

2075 extendRegion(S->getThen());

2076 Counter OutCount = propagateCounts(ThenCount, S->getThen());

2077

2078 if (const Stmt *Else = S->getElse()) {

2079 bool ThenHasTerminateStmt = HasTerminateStmt;

2080 HasTerminateStmt = false;

2081

2082 std::optional Gap =

2083 findGapAreaBetween(getEnd(S->getThen()), getStart(Else));

2084 if (Gap)

2085 fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(), ElseCount);

2086 extendRegion(Else);

2087

2088 Counter ElseOutCount = propagateCounts(ElseCount, Else);

2090 OutCount = addCounters(OutCount, ElseOutCount);

2091

2092 if (ThenHasTerminateStmt)

2093 HasTerminateStmt = true;

2095 OutCount = addCounters(OutCount, ElseCount);

2096

2098 OutCount = getRegionCounter(S);

2099

2100 if (!IsCounterEqual(OutCount, ParentCount)) {

2101 pushRegion(OutCount);

2102 GapRegionCounter = OutCount;

2103 }

2104

2106

2107 createBranchRegion(S->getCond(), ThenCount, ElseCount);

2108 }

2109

2110 void VisitCXXTryStmt(const CXXTryStmt *S) {

2111 extendRegion(S);

2112

2113 extendRegion(S->getTryBlock());

2114

2115 Counter ParentCount = getRegion().getCounter();

2116 propagateCounts(ParentCount, S->getTryBlock());

2117

2118 for (unsigned I = 0, E = S->getNumHandlers(); I < E; ++I)

2119 Visit(S->getHandler(I));

2120

2121 Counter ExitCount = getRegionCounter(S);

2122 pushRegion(ExitCount);

2123 }

2124

2125 void VisitCXXCatchStmt(const CXXCatchStmt *S) {

2126 propagateCounts(getRegionCounter(S), S->getHandlerBlock());

2127 }

2128

2130 extendRegion(E);

2131

2132 Counter ParentCount = getRegion().getCounter();

2133 auto [TrueCount, FalseCount] =

2135 ? BranchCounterPair{getRegionCounter(E->getTrueExpr()),

2136 getRegionCounter(E->getFalseExpr())}

2137 : getBranchCounterPair(E, ParentCount));

2138 Counter OutCount;

2139

2140 if (const auto *BCO = dyn_cast(E)) {

2141 propagateCounts(ParentCount, BCO->getCommon());

2142 OutCount = TrueCount;

2143 } else {

2144 propagateCounts(ParentCount, E->getCond());

2145

2146 auto Gap =

2147 findGapAreaBetween(E->getQuestionLoc(), getStart(E->getTrueExpr()));

2148 if (Gap)

2149 fillGapAreaWithCount(Gap->getBegin(), Gap->getEnd(), TrueCount);

2150

2151 extendRegion(E->getTrueExpr());

2152 OutCount = propagateCounts(TrueCount, E->getTrueExpr());

2153 }

2154

2155 extendRegion(E->getFalseExpr());

2156 Counter FalseOutCount = propagateCounts(FalseCount, E->getFalseExpr());

2158 OutCount = getRegionCounter(E);

2159 else

2160 OutCount = addCounters(OutCount, FalseOutCount);

2161

2162 if (!IsCounterEqual(OutCount, ParentCount)) {

2163 pushRegion(OutCount);

2164 GapRegionCounter = OutCount;

2165 }

2166

2167

2169 createBranchRegion(E->getCond(), TrueCount, FalseCount);

2170 }

2171

2172 void createOrCancelDecision(const BinaryOperator *E, unsigned Since) {

2173 unsigned NumConds = MCDCBuilder.getTotalConditionsAndReset(E);

2174 if (NumConds == 0)

2175 return;

2176

2177

2179 for (const auto &SR : ArrayRef(SourceRegions).slice(Since)) {

2180 if (SR.isMCDCBranch()) {

2181 auto [ID, Conds] = SR.getMCDCBranchParams();

2182 CondIDs[ID] = Conds;

2183 }

2184 }

2185

2186

2187 mcdc::TVIdxBuilder Builder(CondIDs);

2188 unsigned NumTVs = Builder.NumTestVectors;

2190 assert(MaxTVs < mcdc::TVIdxBuilder::HardMaxTVs);

2191

2192 if (NumTVs > MaxTVs) {

2193

2194 cancelDecision(E, Since, NumTVs, MaxTVs);

2195 return;

2196 }

2197

2198

2202 std::move(Builder.Indices),

2203 };

2204

2205 auto DecisionParams = mcdc::DecisionParameters{

2206 MCDCState.BitmapBits += NumTVs,

2207 NumConds,

2208 };

2209

2210

2211 createDecisionRegion(E, DecisionParams);

2212 }

2213

2214

2215 void cancelDecision(const BinaryOperator *E, unsigned Since, int NumTVs,

2216 int MaxTVs) {

2218 unsigned DiagID =

2220 "unsupported MC/DC boolean expression; "

2221 "number of test vectors (%0) exceeds max (%1). "

2222 "Expression will not be covered");

2224

2225

2226 for (auto &SR : MutableArrayRef(SourceRegions).slice(Since)) {

2227 assert(!SR.isMCDCDecision() && "Decision shouldn't be seen here");

2228 if (SR.isMCDCBranch())

2229 SR.resetMCDCParams();

2230 }

2231

2232

2234 }

2235

2236

2237 bool isExprInSystemHeader(const BinaryOperator *E) const {

2239 SM.isInSystemHeader(SM.getSpellingLoc(E->getOperatorLoc())) &&

2241 SM.isInSystemHeader(SM.getSpellingLoc(E->getEndLoc())));

2242 }

2243

2245 if (isExprInSystemHeader(E)) {

2246 LeafExprSet.insert(E);

2247 return;

2248 }

2249

2250 bool IsRootNode = MCDCBuilder.isIdle();

2251

2252 unsigned SourceRegionsSince = SourceRegions.size();

2253

2254

2255 MCDCBuilder.pushAndAssignIDs(E);

2256

2257 extendRegion(E->getLHS());

2258 propagateCounts(getRegion().getCounter(), E->getLHS());

2259 handleFileExit(getEnd(E->getLHS()));

2260

2261

2262 const auto DecisionLHS = MCDCBuilder.pop();

2263

2264

2265 extendRegion(E->getRHS());

2266 propagateCounts(getRegionCounter(E), E->getRHS());

2267

2269 return;

2270

2271

2272 const auto DecisionRHS = MCDCBuilder.back();

2273

2274

2275 Counter ParentCnt = getRegion().getCounter();

2276

2277

2278 auto [RHSExecCnt, LHSExitCnt] = getBranchCounterPair(E, ParentCnt);

2279

2280

2281 auto [RHSTrueCnt, RHSExitCnt] =

2282 getBranchCounterPair(E->getRHS(), RHSExecCnt);

2283

2284

2285 createBranchRegion(E->getLHS(), RHSExecCnt, LHSExitCnt, DecisionLHS);

2286

2287

2288 createBranchRegion(E->getRHS(), RHSTrueCnt, RHSExitCnt, DecisionRHS);

2289

2290

2291 if (IsRootNode)

2292 createOrCancelDecision(E, SourceRegionsSince);

2293 }

2294

2295

2296 bool shouldVisitRHS(const Expr *LHS) {

2297 bool LHSIsTrue = false;

2298 bool LHSIsConst = false;

2302 return !LHSIsConst || (LHSIsConst && !LHSIsTrue);

2303 }

2304

2306 if (isExprInSystemHeader(E)) {

2307 LeafExprSet.insert(E);

2308 return;

2309 }

2310

2311 bool IsRootNode = MCDCBuilder.isIdle();

2312

2313 unsigned SourceRegionsSince = SourceRegions.size();

2314

2315

2316 MCDCBuilder.pushAndAssignIDs(E);

2317

2318 extendRegion(E->getLHS());

2319 Counter OutCount = propagateCounts(getRegion().getCounter(), E->getLHS());

2320 handleFileExit(getEnd(E->getLHS()));

2321

2322

2323 const auto DecisionLHS = MCDCBuilder.pop();

2324

2325

2326 extendRegion(E->getRHS());

2327 propagateCounts(getRegionCounter(E), E->getRHS());

2328

2330 return;

2331

2332

2333 const auto DecisionRHS = MCDCBuilder.back();

2334

2335

2336 Counter ParentCnt = getRegion().getCounter();

2337

2338

2339 auto [RHSExecCnt, LHSExitCnt] = getBranchCounterPair(E, ParentCnt);

2340

2341

2342 auto [RHSFalseCnt, RHSExitCnt] =

2343 getBranchCounterPair(E->getRHS(), RHSExecCnt);

2344

2345 if (!shouldVisitRHS(E->getLHS())) {

2346 GapRegionCounter = OutCount;

2347 }

2348

2349

2350 createBranchRegion(E->getLHS(), LHSExitCnt, RHSExecCnt, DecisionLHS);

2351

2352

2353 createBranchRegion(E->getRHS(), RHSExitCnt, RHSFalseCnt, DecisionRHS);

2354

2355

2356 if (IsRootNode)

2357 createOrCancelDecision(E, SourceRegionsSince);

2358 }

2359

2360 void VisitLambdaExpr(const LambdaExpr *LE) {

2361

2362

2363 }

2364

2367 }

2368

2370

2372 }

2373

2377 }

2378};

2379

2380}

2381

2382static void dump(llvm::raw_ostream &OS, StringRef FunctionName,

2385 OS << FunctionName << ":\n";

2386 CounterMappingContext Ctx(Expressions);

2387 for (const auto &R : Regions) {

2388 OS.indent(2);

2389 switch (R.Kind) {

2390 case CounterMappingRegion::CodeRegion:

2391 break;

2392 case CounterMappingRegion::ExpansionRegion:

2393 OS << "Expansion,";

2394 break;

2395 case CounterMappingRegion::SkippedRegion:

2396 OS << "Skipped,";

2397 break;

2398 case CounterMappingRegion::GapRegion:

2399 OS << "Gap,";

2400 break;

2401 case CounterMappingRegion::BranchRegion:

2402 case CounterMappingRegion::MCDCBranchRegion:

2403 OS << "Branch,";

2404 break;

2405 case CounterMappingRegion::MCDCDecisionRegion:

2406 OS << "Decision,";

2407 break;

2408 }

2409

2410 OS << "File " << R.FileID << ", " << R.LineStart << ":" << R.ColumnStart

2411 << " -> " << R.LineEnd << ":" << R.ColumnEnd << " = ";

2412

2413 if (const auto *DecisionParams =

2414 std::get_ifmcdc::DecisionParameters(&R.MCDCParams)) {

2415 OS << "M:" << DecisionParams->BitmapIdx;

2416 OS << ", C:" << DecisionParams->NumConditions;

2417 } else {

2418 Ctx.dump(R.Count, OS);

2419

2420 if (R.isBranch()) {

2421 OS << ", ";

2422 Ctx.dump(R.FalseCount, OS);

2423 }

2424 }

2425

2426 if (const auto *BranchParams =

2427 std::get_ifmcdc::BranchParameters(&R.MCDCParams)) {

2428 OS << " [" << BranchParams->ID + 1 << ","

2429 << BranchParams->Conds[true] + 1;

2430 OS << "," << BranchParams->Conds[false] + 1 << "] ";

2431 }

2432

2433 if (R.Kind == CounterMappingRegion::ExpansionRegion)

2434 OS << " (Expanded file = " << R.ExpandedFileID << ")";

2435 OS << "\n";

2436 }

2437}

2438

2441 : CGM(CGM), SourceInfo(SourceInfo) {}

2442

2443std::string CoverageMappingModuleGen::getCurrentDirname() {

2446

2448 llvm::sys::fs::current_path(CWD);

2449 return CWD.str().str();

2450}

2451

2452std::string CoverageMappingModuleGen::normalizeFilename(StringRef Filename) {

2454 llvm::sys::path::remove_dots(Path, true);

2455

2456

2457

2458

2459 for (const auto &[From, To] :

2461 if (llvm::sys::path::replace_path_prefix(Path, From, To))

2462 break;

2463 }

2464 return Path.str().str();

2465}

2466

2468 llvm::InstrProfSectKind SK) {

2469 return llvm::getInstrProfSectionName(

2471}

2472

2473void CoverageMappingModuleGen::emitFunctionMappingRecord(

2474 const FunctionInfo &Info, uint64_t FilenamesRef) {

2476

2477

2478 std::string FuncRecordName = "__covrec_" + llvm::utohexstr(Info.NameHash);

2479

2480

2481

2482

2483

2484 if (Info.IsUsed)

2485 FuncRecordName += "u";

2486

2487

2488 const uint64_t NameHash = Info.NameHash;

2489 const uint64_t FuncHash = Info.FuncHash;

2490 const std::string &CoverageMapping = Info.CoverageMapping;

2491#define COVMAP_FUNC_RECORD(Type, LLVMType, Name, Init) LLVMType,

2492 llvm::Type *FunctionRecordTypes[] = {

2493#include "llvm/ProfileData/InstrProfData.inc"

2494 };

2495 auto *FunctionRecordTy =

2496 llvm::StructType::get(Ctx, ArrayRef(FunctionRecordTypes),

2497 true);

2498

2499

2500#define COVMAP_FUNC_RECORD(Type, LLVMType, Name, Init) Init,

2501 llvm::Constant *FunctionRecordVals[] = {

2502 #include "llvm/ProfileData/InstrProfData.inc"

2503 };

2504 auto *FuncRecordConstant =

2505 llvm::ConstantStruct::get(FunctionRecordTy, ArrayRef(FunctionRecordVals));

2506

2507

2508 auto *FuncRecord = new llvm::GlobalVariable(

2509 CGM.getModule(), FunctionRecordTy, true,

2510 llvm::GlobalValue::LinkOnceODRLinkage, FuncRecordConstant,

2511 FuncRecordName);

2512 FuncRecord->setVisibility(llvm::GlobalValue::HiddenVisibility);

2514 FuncRecord->setAlignment(llvm::Align(8));

2516 FuncRecord->setComdat(CGM.getModule().getOrInsertComdat(FuncRecordName));

2517

2518

2520}

2521

2523 llvm::GlobalVariable *NamePtr, StringRef NameValue, uint64_t FuncHash,

2524 const std::string &CoverageMapping, bool IsUsed) {

2525 const uint64_t NameHash = llvm::IndexedInstrProf::ComputeHash(NameValue);

2526 FunctionRecords.push_back({NameHash, FuncHash, CoverageMapping, IsUsed});

2527

2528 if (!IsUsed)

2529 FunctionNames.push_back(NamePtr);

2530

2532

2533

2534

2535

2536

2538 std::vector Filenames;

2539 std::vector Expressions;

2540 std::vector Regions;

2541 FilenameStrs.resize(FileEntries.size() + 1);

2542 FilenameStrs[0] = normalizeFilename(getCurrentDirname());

2543 for (const auto &Entry : FileEntries) {

2544 auto I = Entry.second;

2545 FilenameStrs[I] = normalizeFilename(Entry.first.getName());

2546 }

2548 RawCoverageMappingReader Reader(CoverageMapping, FilenameRefs, Filenames,

2549 Expressions, Regions);

2550 if (Reader.read())

2551 return;

2552 dump(llvm::outs(), NameValue, Expressions, Regions);

2553 }

2554}

2555

2557 if (FunctionRecords.empty())

2558 return;

2560 auto *Int32Ty = llvm::Type::getInt32Ty(Ctx);

2561

2562

2564 FilenameStrs.resize(FileEntries.size() + 1);

2565

2566 FilenameStrs[0] = normalizeFilename(getCurrentDirname());

2567 for (const auto &Entry : FileEntries) {

2568 auto I = Entry.second;

2569 FilenameStrs[I] = normalizeFilename(Entry.first.getName());

2570 }

2571

2572 std::string Filenames;

2573 {

2574 llvm::raw_string_ostream OS(Filenames);

2575 CoverageFilenamesSectionWriter(FilenameStrs).write(OS);

2576 }

2577 auto *FilenamesVal =

2578 llvm::ConstantDataArray::getString(Ctx, Filenames, false);

2579 const int64_t FilenamesRef = llvm::IndexedInstrProf::ComputeHash(Filenames);

2580

2581

2582 for (const FunctionInfo &Info : FunctionRecords)

2583 emitFunctionMappingRecord(Info, FilenamesRef);

2584

2585 const unsigned NRecords = 0;

2586 const size_t FilenamesSize = Filenames.size();

2587 const unsigned CoverageMappingSize = 0;

2588 llvm::Type *CovDataHeaderTypes[] = {

2589#define COVMAP_HEADER(Type, LLVMType, Name, Init) LLVMType,

2590#include "llvm/ProfileData/InstrProfData.inc"

2591 };

2592 auto CovDataHeaderTy =

2593 llvm::StructType::get(Ctx, ArrayRef(CovDataHeaderTypes));

2594 llvm::Constant *CovDataHeaderVals[] = {

2595#define COVMAP_HEADER(Type, LLVMType, Name, Init) Init,

2596#include "llvm/ProfileData/InstrProfData.inc"

2597 };

2598 auto CovDataHeaderVal =

2599 llvm::ConstantStruct::get(CovDataHeaderTy, ArrayRef(CovDataHeaderVals));

2600

2601

2602 llvm::Type *CovDataTypes[] = {CovDataHeaderTy, FilenamesVal->getType()};

2603 auto CovDataTy = llvm::StructType::get(Ctx, ArrayRef(CovDataTypes));

2604 llvm::Constant *TUDataVals[] = {CovDataHeaderVal, FilenamesVal};

2605 auto CovDataVal = llvm::ConstantStruct::get(CovDataTy, ArrayRef(TUDataVals));

2606 auto CovData = new llvm::GlobalVariable(

2607 CGM.getModule(), CovDataTy, true, llvm::GlobalValue::PrivateLinkage,

2608 CovDataVal, llvm::getCoverageMappingVarName());

2609

2611 CovData->setAlignment(llvm::Align(8));

2612

2613

2615

2616 if (!FunctionNames.empty()) {

2617 auto NamesArrTy = llvm::ArrayType::get(llvm::PointerType::getUnqual(Ctx),

2618 FunctionNames.size());

2619 auto NamesArrVal = llvm::ConstantArray::get(NamesArrTy, FunctionNames);

2620

2621

2622 new llvm::GlobalVariable(CGM.getModule(), NamesArrTy, true,

2623 llvm::GlobalValue::InternalLinkage, NamesArrVal,

2624 llvm::getCoverageUnusedNamesVarName());

2625 }

2626}

2627

2629 return FileEntries.try_emplace(File, FileEntries.size() + 1).first->second;

2630}

2631

2633 llvm::raw_ostream &OS) {

2634 assert(CounterMap && MCDCState);

2635 CounterCoverageMappingBuilder Walker(CVM, *CounterMap, *MCDCState, SM,

2636 LangOpts);

2637 Walker.VisitDecl(D);

2638 Walker.write(OS);

2639}

2640

2642 llvm::raw_ostream &OS) {

2643 EmptyCoverageMappingBuilder Walker(CVM, SM, LangOpts);

2644 Walker.VisitDecl(D);

2645 Walker.write(OS);

2646}

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:🆑: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)

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.