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 (SM.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 SM.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 (SM.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 (SM.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 (C)

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 (SM.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 (SM.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 (SM.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 (SM.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 (SM.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