A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://clang.llvm.org/doxygen/CoverageMappingGen_8cpp_source.html below:

clang: lib/CodeGen/CoverageMappingGen.cpp Source File

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" 36

llvm::cl::desc(

"Enable single byte coverage"

),

37

llvm::cl::Hidden, llvm::cl::init(

false

));

41 "emptyline-comment-coverage"

,

42

llvm::cl::desc(

"Emit emptylines and comment lines as skipped regions (only " 43 "disable it on test)"

),

44

llvm::cl::init(

true

), llvm::cl::Hidden);

48 "system-headers-coverage"

,

49

cl::desc(

"Enable collecting coverage from system headers"

), cl::init(

false

),

53using namespace clang

;

54using namespace

CodeGen;

69 if

(Tok.

getKind

() != clang::tok::eod)

79

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

80

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

82

SkippedRanges.back().Range.setEnd(

Range

.

getEnd

());

84

SkippedRanges.push_back({

Range

, RangeKind, PrevTokLoc});

101 if

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

102

SkippedRanges.back().NextTokLoc =

Loc

;

107class

SourceMappingRegion {

112

std::optional<Counter> FalseCount;

115

mcdc::Parameters MCDCParams;

118

std::optional<SourceLocation> LocStart;

121

std::optional<SourceLocation> LocEnd;

132

SourceMappingRegion(Counter Count, std::optional<SourceLocation> LocStart,

133

std::optional<SourceLocation> LocEnd,

134 bool

GapRegion =

false

)

135

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

136

SkippedRegion(

false

) {}

138

SourceMappingRegion(Counter Count, std::optional<Counter> FalseCount,

139

mcdc::Parameters MCDCParams,

140

std::optional<SourceLocation> LocStart,

141

std::optional<SourceLocation> LocEnd,

142 bool

GapRegion =

false

)

143

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

144

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

145

SkippedRegion(

false

) {}

147

SourceMappingRegion(mcdc::Parameters MCDCParams,

148

std::optional<SourceLocation> LocStart,

149

std::optional<SourceLocation> LocEnd)

150

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

153 const

Counter &getCounter()

const

{

return

Count; }

155 const

Counter &getFalseCounter()

const

{

156

assert(FalseCount &&

"Region has no alternate counter"

);

160 void

setCounter(Counter

C

) { Count =

C

; }

162 bool

hasStartLoc()

const

{

return

LocStart.has_value(); }

167

assert(LocStart &&

"Region has no start location"

);

171 bool

hasEndLoc()

const

{

return

LocEnd.has_value(); }

174

assert(

Loc

.

isValid

() &&

"Setting an invalid end location"

);

179

assert(LocEnd &&

"Region has no end location"

);

183 bool

isGap()

const

{

return

GapRegion; }

185 void

setGap(

bool

Gap) { GapRegion = Gap; }

187 bool

isSkipped()

const

{

return

SkippedRegion; }

189 void

setSkipped(

bool

Skipped) { SkippedRegion = Skipped; }

191 bool

isBranch()

const

{

return

FalseCount.has_value(); }

193 bool

isMCDCBranch()

const

{

194 return

std::holds_alternative<mcdc::BranchParameters>(MCDCParams);

197 const auto

&getMCDCBranchParams()

const

{

198 return

mcdc::getParams<const mcdc::BranchParameters>(MCDCParams);

201 bool

isMCDCDecision()

const

{

202 return

std::holds_alternative<mcdc::DecisionParameters>(MCDCParams);

205 const auto

&getMCDCDecisionParams()

const

{

206 return

mcdc::getParams<const mcdc::DecisionParameters>(MCDCParams);

209 const

mcdc::Parameters &getMCDCParams()

const

{

return

MCDCParams; }

211 void

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

215struct

SpellingRegion {

220 unsigned

ColumnStart;

230

LineStart =

SM

.getSpellingLineNumber(LocStart);

231

ColumnStart =

SM

.getSpellingColumnNumber(LocStart);

232

LineEnd =

SM

.getSpellingLineNumber(LocEnd);

233

ColumnEnd =

SM

.getSpellingColumnNumber(LocEnd);

237

: SpellingRegion(

SM

, R.getBeginLoc(), R.getEndLoc()) {}

241 bool

isInSourceOrder()

const

{

242 return

(LineStart < LineEnd) ||

243

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

249class

CoverageMappingBuilder {

257

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

264

std::vector<SourceMappingRegion> SourceRegions;

271 typedef

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

276

: CVM(CVM),

SM

(

SM

), LangOpts(LangOpts) {}

291 return SM

.getLocForStartOfFile(

SM

.getFileID(

Loc

));

298 SM

.getFileOffset(

Loc

));

299 return SM

.getLocForEndOfFile(

SM

.getFileID(

Loc

));

309

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

311

std::optional<SourceLocation> EndLoc = std::nullopt;

313 SM

.isWrittenInScratchSpace(

SM

.getSpellingLoc(

Loc

))) {

314 auto

ExpansionRange =

SM

.getImmediateExpansionRange(

Loc

);

315 Loc

= ExpansionRange.getBegin();

316

EndLoc = ExpansionRange.getEnd();

318 return

std::make_pair(

Loc

, EndLoc);

325 bool

AcceptScratch =

true

) {

327 return SM

.getIncludeLoc(

SM

.getFileID(

Loc

));

328 Loc

=

SM

.getImmediateExpansionRange(

Loc

).getBegin();

331 return

getNonScratchExpansionLoc(

Loc

).first;

336 return SM

.getBufferName(

SM

.getSpellingLoc(

Loc

)) ==

"<built-in>"

;

342 Loc

= getIncludeOrExpansionLoc(

Loc

);

352 while

(

SM

.isMacroArgExpansion(

Loc

) || isInBuiltin(

Loc

))

353 Loc

=

SM

.getImmediateExpansionRange(

Loc

).getBegin();

360 while

(

SM

.isMacroArgExpansion(

Loc

) || isInBuiltin(

Loc

))

361 Loc

=

SM

.getImmediateExpansionRange(

Loc

).getBegin();

362 return

getPreciseTokenLocEnd(

Loc

);

371

FileIDMapping.clear();

373

llvm::SmallSet<FileID, 8>

Visited

;

375 for

(

auto

&Region : SourceRegions) {

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());

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()));

403

!

SM

.isInSystemHeader(

SM

.getSpellingLoc(

Loc

)));

409

FileLocs.push_back(std::make_pair(

Loc

, Depth));

411

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

413 for

(

const auto

&FL : FileLocs) {

415 FileID

SpellingFile =

SM

.getDecomposedSpellingLoc(

Loc

).first;

416 auto

Entry =

SM

.getFileEntryRefForID(SpellingFile);

420

FileIDMapping[

SM

.getFileID(

Loc

)] = std::make_pair(Mapping.size(),

Loc

);

421

Mapping.push_back(CVM.

getFileID

(*Entry));

429 auto

Mapping = FileIDMapping.find(

SM

.getFileID(

Loc

));

430 if

(Mapping != FileIDMapping.end())

431 return

Mapping->second.first;

445

SpellingRegion SR{

SM

, LocStart, LocEnd};

447 if

(PrevTokLoc.

isValid

() &&

SM

.isWrittenInSameFile(LocStart, PrevTokLoc) &&

448

SR.LineStart ==

SM

.getSpellingLineNumber(PrevTokLoc))

450 if

(NextTokLoc.

isValid

() &&

SM

.isWrittenInSameFile(LocEnd, NextTokLoc) &&

451

SR.LineEnd ==

SM

.getSpellingLineNumber(NextTokLoc)) {

455 if

(SR.isInSourceOrder())

462 void

gatherSkippedRegions() {

466

FileLineRanges.resize(

467

FileIDMapping.size(),

468

std::make_pair(std::numeric_limits<unsigned>::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);

477 for

(

auto

&I : SkippedRanges) {

481

assert(

SM

.isWrittenInSameFile(LocStart, LocEnd) &&

482 "region spans multiple files"

);

484 auto

CovFileID = getCoverageFileID(LocStart);

487

std::optional<SpellingRegion> SR;

489

SR = adjustSkippedRange(

SM

, LocStart, LocEnd, I.PrevTokLoc,

491 else if

(I.isPPIfElse() || I.isEmptyLine())

492

SR = {

SM

, LocStart, LocEnd};

496 auto

Region = CounterMappingRegion::makeSkipped(

497

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

501 if

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

502

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

503

MappingRegions.push_back(Region);

509 void

emitSourceRegions(

const

SourceRegionFilter &Filter) {

510 for

(

const auto

&Region : SourceRegions) {

511

assert(Region.hasEndLoc() &&

"incomplete region"

);

514

assert(

SM

.getFileID(LocStart).isValid() &&

"region in invalid file"

);

519 SM

.isInSystemHeader(

SM

.getSpellingLoc(LocStart))) {

520

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

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

);

525 auto

CovFileID = getCoverageFileID(LocStart);

528

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

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

);

534

assert(

SM

.isWrittenInSameFile(LocStart, LocEnd) &&

535 "region spans multiple files"

);

541 if

(

Filter

.count(std::make_pair(LocStart, LocEnd))) {

542

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

543 "Don't suppress the condition"

);

548

SpellingRegion SR{

SM

, LocStart, LocEnd};

549

assert(SR.isInSourceOrder() &&

"region start and end out of order"

);

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,

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

569

MappingRegions.push_back(CounterMappingRegion::makeRegion(

570

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

571

SR.LineEnd, SR.ColumnEnd));

577

SourceRegionFilter emitExpansionRegions() {

578

SourceRegionFilter

Filter

;

579 for

(

const auto

&FM : FileIDMapping) {

581 SourceLocation

ParentLoc = getIncludeOrExpansionLoc(ExpandedLoc,

false

);

585 auto

ParentFileID = getCoverageFileID(ParentLoc);

588 auto

ExpandedFileID = getCoverageFileID(ExpandedLoc);

589

assert(ExpandedFileID &&

"expansion in uncovered file"

);

592

assert(

SM

.isWrittenInSameFile(ParentLoc, LocEnd) &&

593 "region spans multiple files"

);

594 Filter

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

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

608struct

EmptyCoverageMappingBuilder :

public

CoverageMappingBuilder {

611

: CoverageMappingBuilder(CVM,

SM

, LangOpts) {}

613 void

VisitDecl(

const Decl

*

D

) {

619 if

(!

SM

.isWrittenInSameFile(Start, End)) {

622 FileID

StartFileID =

SM

.getFileID(Start);

623 FileID

EndFileID =

SM

.getFileID(End);

624 while

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

625

Start = getIncludeOrExpansionLoc(Start);

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

);

628

StartFileID =

SM

.getFileID(Start);

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

637

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

641 void

write(llvm::raw_ostream &OS) {

643

gatherFileIDs(FileIDMapping);

644

emitSourceRegions(SourceRegionFilter());

646 if

(MappingRegions.empty())

649

CoverageMappingWriter Writer(FileIDMapping, {}, MappingRegions);

662struct

MCDCCoverageBuilder {

756 const Stmt

*DecisionStmt =

nullptr

;

757

mcdc::ConditionID NextID = 0;

758 bool

NotMapped =

false

;

762 static constexpr

mcdc::ConditionIDs DecisionStackSentinel{-1, -1};

766 return E

->getOpcode() == BO_LAnd;

771

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

772

MCDCState(MCDCState) {}

777 bool

isIdle()

const

{

return

(NextID == 0 && !NotMapped); }

782 bool

isBuilding()

const

{

return

(NextID > 0); }

785 void

setCondID(

const Expr

*Cond, mcdc::ConditionID

ID

) {

786

MCDCState.

BranchByStmt

[CodeGenFunction::stripCond(Cond)] = {

ID

,

791

mcdc::ConditionID getCondID(

const Expr

*Cond)

const

{

792 auto

I = MCDCState.

BranchByStmt

.find(CodeGenFunction::stripCond(Cond));

800 const

mcdc::ConditionIDs &back()

const

{

return

DecisionStack.back(); }

823 const

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

828 if

(MCDCState.

BranchByStmt

.contains(CodeGenFunction::stripCond(

E

)))

829

setCondID(

E

->getLHS(), getCondID(

E

));

831

setCondID(

E

->getLHS(), NextID++);

834

mcdc::ConditionID RHSid = NextID++;

835

setCondID(

E

->getRHS(), RHSid);

839

DecisionStack.push_back({ParentDecision[

false

], RHSid});

841

DecisionStack.push_back({RHSid, ParentDecision[

true

]});

845

mcdc::ConditionIDs pop() {

847 return

DecisionStackSentinel;

849

assert(DecisionStack.size() > 1);

850 return

DecisionStack.pop_back_val();

860

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

870 unsigned

TotalConds = NextID;

881struct

CounterCoverageMappingBuilder

882

:

public

CoverageMappingBuilder,

885

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

894

llvm::DenseSet<const Stmt *> LeafExprSet;

897

MCDCCoverageBuilder MCDCBuilder;

899

CounterExpressionBuilder Builder;

908 bool

HasTerminateStmt =

false

;

911

Counter GapRegionCounter;

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

921

Counter addCounters(Counter LHS, Counter RHS,

bool

Simplify =

true

) {

922 return

Builder.add(LHS, RHS, Simplify);

925

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

926 bool

Simplify =

true

) {

927 return

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

933

Counter getRegionCounter(

const Stmt

*S) {

934 return

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

937 struct

BranchCounterPair {

959

getBranchCounterPair(

const Stmt

*S, Counter ParentCnt,

960

std::optional<Counter> SkipCntForOld = std::nullopt) {

961

Counter ExecCnt = getRegionCounter(S);

966

assert(SkipCntForOld &&

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

);

968 return

{ExecCnt, *SkipCntForOld};

971 return

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

974 bool

IsCounterEqual(Counter OutCount, Counter ParentCount) {

975 if

(OutCount == ParentCount)

985 size_t

pushRegion(Counter Count,

986

std::optional<SourceLocation> StartLoc = std::nullopt,

987

std::optional<SourceLocation> EndLoc = std::nullopt,

988

std::optional<Counter> FalseCount = std::nullopt,

989 const

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

991 if

(StartLoc && !FalseCount) {

992

MostRecentLocation = *StartLoc;

997

assert((!StartLoc || StartLoc->isValid()) &&

"Start location is not valid"

);

998

assert((!EndLoc || EndLoc->isValid()) &&

"End location is not valid"

);

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

1010 return

RegionStack.size() - 1;

1013 size_t

pushRegion(

const

mcdc::DecisionParameters &DecisionParams,

1014

std::optional<SourceLocation> StartLoc = std::nullopt,

1015

std::optional<SourceLocation> EndLoc = std::nullopt) {

1017

RegionStack.emplace_back(DecisionParams, StartLoc, EndLoc);

1019 return

RegionStack.size() - 1;

1025 Loc

= getIncludeOrExpansionLoc(

Loc

);

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;

1059

assert(

SM

.isWrittenInSameFile(NestedLoc, EndLoc));

1061 if

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

1062

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

1065

EndLoc = getPreciseTokenLocEnd(getIncludeOrExpansionLoc(EndLoc));

1067

llvm::report_fatal_error(

1068 "File exit not handled before popRegions"

);

1079

assert(

SM

.isWrittenInSameFile(StartLoc, NestedLoc));

1081 if

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

1082

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

1085

StartLoc = getIncludeOrExpansionLoc(StartLoc);

1087

llvm::report_fatal_error(

1088 "File exit not handled before popRegions"

);

1092

Region.setStartLoc(StartLoc);

1093

Region.setEndLoc(EndLoc);

1096

MostRecentLocation = EndLoc;

1099 if

(StartLoc == getStartOfFileOrMacro(StartLoc) &&

1100

EndLoc == getEndOfFileOrMacro(EndLoc))

1101

MostRecentLocation = getIncludeOrExpansionLoc(EndLoc);

1104

assert(

SM

.isWrittenInSameFile(Region.getBeginLoc(), EndLoc));

1105

assert(SpellingRegion(

SM

, Region).isInSourceOrder());

1106

SourceRegions.push_back(Region);

1108

RegionStack.pop_back();

1114

assert(!RegionStack.empty() &&

"statement has no region"

);

1115 return

RegionStack.back();

1120

Counter propagateCounts(Counter TopCount,

const Stmt

*S,

1121 bool

VisitChildren =

true

) {

1124 size_t

Index = pushRegion(TopCount, StartLoc, EndLoc);

1127

Counter ExitCount =

getRegion

().getCounter();

1132 if

(

SM

.isBeforeInTranslationUnit(StartLoc, S->getBeginLoc()))

1133

MostRecentLocation = EndLoc;

1142 void

createBranchRegion(

const Expr

*

C

, Counter TrueCnt, Counter FalseCnt,

1143 const

mcdc::ConditionIDs &Conds = {}) {

1154 if

(CodeGenFunction::isInstrumentedCondition(

C

) ||

1155

LeafExprSet.count(CodeGenFunction::stripCond(

C

))) {

1156

mcdc::Parameters BranchParams;

1157

mcdc::ConditionID

ID

= MCDCBuilder.getCondID(

C

);

1159

BranchParams = mcdc::BranchParameters{

ID

, Conds};

1169 if

(Result.Val.getInt().getBoolValue())

1170

FalseCnt = Counter::getZero();

1172

TrueCnt = Counter::getZero();

1175

pushRegion(TrueCnt, getStart(

C

), getEnd(

C

), FalseCnt, BranchParams));

1182 void

createDecisionRegion(

const Expr

*

C

,

1183 const

mcdc::DecisionParameters &DecisionParams) {

1184

popRegions(pushRegion(DecisionParams, getStart(

C

), getEnd(

C

)));

1190

Counter createSwitchCaseRegion(

const SwitchCase

*SC, Counter ParentCount) {

1194

Counter TrueCnt = getRegionCounter(SC);

1195

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

getColonLoc

(),

1196

subtractCounters(ParentCount, TrueCnt)));

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;

1215

MostRecentLocation = EndLoc;

1222

MostRecentLocation == getEndOfFileOrMacro(MostRecentLocation) &&

1223

isRegionAlreadyAdded(getStartOfFileOrMacro(MostRecentLocation),

1224

MostRecentLocation,

getRegion

().isBranch()))

1225

MostRecentLocation = getIncludeOrExpansionLoc(MostRecentLocation);

1235 SM

.isWrittenInSameFile(MostRecentLocation, NewLoc))

1241 FileID

ParentFile =

SM

.getFileID(LCA);

1242 while

(!isNestedIn(MostRecentLocation, ParentFile)) {

1243

LCA = getIncludeOrExpansionLoc(LCA);

1244 if

(LCA.

isInvalid

() ||

SM

.isWrittenInSameFile(LCA, MostRecentLocation)) {

1247

MostRecentLocation = NewLoc;

1250

ParentFile =

SM

.getFileID(LCA);

1253

llvm::SmallSet<SourceLocation, 8> StartLocs;

1254

std::optional<Counter> ParentCounter;

1255 for

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

1256 if

(!I.hasStartLoc())

1259 if

(!isNestedIn(

Loc

, ParentFile)) {

1260

ParentCounter = I.getCounter();

1264 while

(!

SM

.isInFileID(

Loc

, ParentFile)) {

1268 if

(StartLocs.insert(

Loc

).second) {

1270

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

1271

I.getMCDCParams(),

Loc

,

1272

getEndOfFileOrMacro(

Loc

), I.isBranch());

1274

SourceRegions.emplace_back(I.getCounter(),

Loc

,

1275

getEndOfFileOrMacro(

Loc

));

1277 Loc

= getIncludeOrExpansionLoc(

Loc

);

1279

I.setStartLoc(getPreciseTokenLocEnd(

Loc

));

1282 if

(ParentCounter) {

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());

1294 Loc

= getIncludeOrExpansionLoc(

Loc

);

1298

MostRecentLocation = NewLoc;

1302 void

extendRegion(

const Stmt

*S) {

1303

SourceMappingRegion &Region =

getRegion

();

1306

handleFileExit(StartLoc);

1307 if

(!Region.hasStartLoc())

1308

Region.setStartLoc(StartLoc);

1312 void

terminateRegion(

const Stmt

*S) {

1314

SourceMappingRegion &Region =

getRegion

();

1316 if

(!Region.hasEndLoc())

1317

Region.setEndLoc(EndLoc);

1318

pushRegion(Counter::getZero());

1319

HasTerminateStmt =

true

;

1323

std::optional<SourceRange> findGapAreaBetween(

SourceLocation

AfterLoc,

1329 return

std::nullopt;

1334 FileID

FID =

SM

.getFileID(AfterLoc);

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;

1346

assert(

SM

.isWrittenInSameFile(getStartOfFileOrMacro(BeforeLoc),

1349

BeforeLoc = getIncludeOrExpansionLoc(BeforeLoc);

1350

assert(BeforeLoc.

isValid

());

1354

assert(

SM

.isWrittenInSameFile(AfterLoc,

1355

getEndOfFileOrMacro(AfterLoc)));

1357

AfterLoc = getIncludeOrExpansionLoc(AfterLoc);

1359

AfterLoc = getPreciseTokenLocEnd(AfterLoc);

1364

AfterLoc = getPreciseTokenLocEnd(AfterLoc);

1368 return

std::nullopt;

1369 if

(!

SM

.isWrittenInSameFile(AfterLoc, BeforeLoc) ||

1370

!SpellingRegion(

SM

, AfterLoc, BeforeLoc).isInSourceOrder())

1371 return

std::nullopt;

1372 return

{{AfterLoc, BeforeLoc}};

1378 if

(StartLoc == EndLoc)

1380

assert(SpellingRegion(

SM

, StartLoc, EndLoc).isInSourceOrder());

1381

handleFileExit(StartLoc);

1382 size_t

Index = pushRegion(Count, StartLoc, EndLoc);

1384

handleFileExit(EndLoc);

1390

std::optional<SourceRange> findAreaStartingFromTo(

SourceLocation

StartingLoc,

1394 FileID

FID =

SM

.getFileID(StartingLoc);

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;

1406

assert(

SM

.isWrittenInSameFile(getStartOfFileOrMacro(BeforeLoc),

1409

BeforeLoc = getIncludeOrExpansionLoc(BeforeLoc);

1410

assert(BeforeLoc.

isValid

());

1414

assert(

SM

.isWrittenInSameFile(StartingLoc,

1415

getStartOfFileOrMacro(StartingLoc)));

1417

StartingLoc = getIncludeOrExpansionLoc(StartingLoc);

1418

assert(StartingLoc.

isValid

());

1425 return

std::nullopt;

1426 if

(!

SM

.isWrittenInSameFile(StartingLoc, BeforeLoc) ||

1427

!SpellingRegion(

SM

, StartingLoc, BeforeLoc).isInSourceOrder())

1428 return

std::nullopt;

1429 return

{{StartingLoc, BeforeLoc}};

1433 const auto

Skipped = findAreaStartingFromTo(StartLoc, BeforeLoc);

1438 const auto

NewStartLoc = Skipped->getBegin();

1439 const auto

EndLoc = Skipped->getEnd();

1441 if

(NewStartLoc == EndLoc)

1443

assert(SpellingRegion(

SM

, NewStartLoc, EndLoc).isInSourceOrder());

1444

handleFileExit(NewStartLoc);

1445 size_t

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

1447

handleFileExit(EndLoc);

1452 struct

BreakContinue {

1454

Counter ContinueCount;

1458

CounterCoverageMappingBuilder(

1460

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

1462

: CoverageMappingBuilder(CVM,

SM

, LangOpts), CounterMap(CounterMap),

1463

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

1466 void

write(llvm::raw_ostream &OS) {

1468

gatherFileIDs(VirtualFileMapping);

1469

SourceRegionFilter

Filter

= emitExpansionRegions();

1470

emitSourceRegions(Filter);

1471

gatherSkippedRegions();

1473 if

(MappingRegions.empty())

1476

CoverageMappingWriter Writer(VirtualFileMapping, Builder.getExpressions(),

1481 void

VisitStmt(

const Stmt

*S) {

1482 if

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

1484 const Stmt

*LastStmt =

nullptr

;

1485 bool

SaveTerminateStmt = HasTerminateStmt;

1486

HasTerminateStmt =

false

;

1487

GapRegionCounter = Counter::getZero();

1488 for

(

const Stmt

*Child : S->children())

1492 if

(LastStmt && HasTerminateStmt) {

1493 auto

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

1495

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

1497

SaveTerminateStmt =

true

;

1498

HasTerminateStmt =

false

;

1503 if

(SaveTerminateStmt)

1504

HasTerminateStmt =

true

;

1505

handleFileExit(getEnd(S));

1508 void

VisitDecl(

const Decl

*

D

) {

1514 SM

.isInSystemHeader(

SM

.getSpellingLoc(getStart(Body))))

1521

Counter BodyCounter = getRegionCounter(Body);

1523 if

(

auto

*Method = dyn_cast<CXXMethodDecl>(

D

))

1525 if

(

auto

*Ctor = dyn_cast<CXXConstructorDecl>(

D

)) {

1529 if

(getStart(

Init

).isValid() && getEnd(

Init

).isValid())

1530

propagateCounts(BodyCounter,

Init

);

1535

propagateCounts(BodyCounter, Body,

1537

assert(RegionStack.empty() &&

"Regions entered but never exited"

);

1542 if

(S->getRetValue())

1543

Visit(S->getRetValue());

1549

Visit(S->getBody());

1554 if

(S->getOperand())

1555

Visit(S->getOperand());

1560

Visit(

E

->getOperand());

1565 if

(

E

->getSubExpr())

1566

Visit(

E

->getSubExpr());

1567

terminateRegion(

E

);

1570 void

VisitGotoStmt(

const GotoStmt

*S) { terminateRegion(S); }

1572 void

VisitLabelStmt(

const LabelStmt

*S) {

1573

Counter LabelCount = getRegionCounter(S);

1576

handleFileExit(Start);

1577

pushRegion(LabelCount, Start);

1578

Visit(S->getSubStmt());

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());

1592

assert(!BreakContinueStack.empty() &&

"continue stmt not in a loop!"

);

1594

BreakContinueStack.back().ContinueCount = addCounters(

1595

BreakContinueStack.back().ContinueCount,

getRegion

().getCounter());

1599 void

VisitCallExpr(

const CallExpr

*

E

) {

1606

terminateRegion(

E

);

1609 void

VisitWhileStmt(

const WhileStmt

*S) {

1612

Counter ParentCount =

getRegion

().getCounter();

1614

? getRegionCounter(S->getBody())

1615

: getRegionCounter(S);

1618

BreakContinueStack.push_back(BreakContinue());

1619

extendRegion(S->getBody());

1620

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

1621

BreakContinue BC = BreakContinueStack.pop_back_val();

1623 bool

BodyHasTerminateStmt = HasTerminateStmt;

1624

HasTerminateStmt =

false

;

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 ||

1635

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

1636

adjustForOutOfOrderTraversal(getEnd(S));

1639 auto

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

1641

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

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

;

1656

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

1659 void

VisitDoStmt(

const DoStmt

*S) {

1662

Counter ParentCount =

getRegion

().getCounter();

1664

? getRegionCounter(S->getBody())

1665

: getRegionCounter(S);

1667

BreakContinueStack.push_back(BreakContinue());

1668

extendRegion(S->getBody());

1670

Counter BackedgeCount;

1672

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

1675

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

1677

BreakContinue BC = BreakContinueStack.pop_back_val();

1679 bool

BodyHasTerminateStmt = HasTerminateStmt;

1680

HasTerminateStmt =

false

;

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 ||

1689

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

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;

1702

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

1704 if

(BodyHasTerminateStmt)

1705

HasTerminateStmt =

true

;

1708 void

VisitForStmt(

const ForStmt

*S) {

1711

Visit(S->getInit());

1713

Counter ParentCount =

getRegion

().getCounter();

1715

? getRegionCounter(S->getBody())

1716

: getRegionCounter(S);

1720

BreakContinueStack.emplace_back();

1723

BreakContinueStack.emplace_back();

1724

extendRegion(S->getBody());

1725

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

1726

BreakContinue BodyBC = BreakContinueStack.pop_back_val();

1728 bool

BodyHasTerminateStmt = HasTerminateStmt;

1729

HasTerminateStmt =

false

;

1733

BreakContinue IncrementBC;

1734 if

(

const Stmt

*Inc = S->getInc()) {

1737

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

1739

IncCount = addCounters(BackedgeCount, BodyBC.ContinueCount);

1740

propagateCounts(IncCount, Inc);

1741

IncrementBC = BreakContinueStack.pop_back_val();

1747

? getRegionCounter(S->getCond())

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 ||

1755 if

(

const Expr

*Cond = S->getCond()) {

1756

propagateCounts(CondCount, Cond);

1757

adjustForOutOfOrderTraversal(getEnd(S));

1761 auto

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

1763

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

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

;

1778

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

1784

Visit(S->getInit());

1785

Visit(S->getLoopVarStmt());

1786

Visit(S->getRangeStmt());

1788

Counter ParentCount =

getRegion

().getCounter();

1790

? getRegionCounter(S->getBody())

1791

: getRegionCounter(S);

1793

BreakContinueStack.push_back(BreakContinue());

1794

extendRegion(S->getBody());

1795

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

1796

BreakContinue BC = BreakContinueStack.pop_back_val();

1798 bool

BodyHasTerminateStmt = HasTerminateStmt;

1799

HasTerminateStmt =

false

;

1802 auto

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

1804

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

1807

addCounters(ParentCount, BackedgeCount, BC.ContinueCount);

1808 auto

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

1809

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

1813

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

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

;

1825

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

1830

Visit(S->getElement());

1832

Counter ParentCount =

getRegion

().getCounter();

1833

Counter BodyCount = getRegionCounter(S);

1835

BreakContinueStack.push_back(BreakContinue());

1836

extendRegion(S->getBody());

1837

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

1838

BreakContinue BC = BreakContinueStack.pop_back_val();

1841 auto

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

1843

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

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;

1859

Visit(S->getInit());

1860

Visit(S->getCond());

1862

BreakContinueStack.push_back(BreakContinue());

1864 const Stmt

*Body = S->getBody();

1866 if

(

const auto

*CS = dyn_cast<CompoundStmt>(Body)) {

1867 if

(!CS->body_empty()) {

1871 size_t

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

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()));

1884

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

1885

BreakContinue BC = BreakContinueStack.pop_back_val();

1888

BreakContinueStack.back().ContinueCount = addCounters(

1889

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

1891

Counter ParentCount =

getRegion

().getCounter();

1892

Counter ExitCount = getRegionCounter(S);

1894

pushRegion(ExitCount);

1895

GapRegionCounter = ExitCount;

1899

MostRecentLocation = getStart(S);

1900

handleFileExit(ExitLoc);

1909

Counter CaseCountSum;

1910 bool

HasDefaultCase =

false

;

1911 const SwitchCase

*Case = S->getSwitchCaseList();

1913

HasDefaultCase = HasDefaultCase || isa<DefaultStmt>(Case);

1914 auto

CaseCount = createSwitchCaseRegion(Case, ParentCount);

1915

CaseCountSum = addCounters(CaseCountSum, CaseCount,

false

);

1920 if

(!HasDefaultCase) {

1925

addCounters(CaseCountSum, Counter::getZero(),

true

);

1928

Counter SwitchFalse = subtractCounters(ParentCount, CaseCountSum);

1929

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

1938

? getRegionCounter(S)

1939

: addCounters(

Parent

.getCounter(), getRegionCounter(S));

1943 if

(

Parent

.hasStartLoc() &&

Parent

.getBeginLoc() == getStart(S))

1944 Parent

.setCounter(Count);

1946

pushRegion(Count, getStart(S));

1948

GapRegionCounter = Count;

1950 if

(

const auto

*CS = dyn_cast<CaseStmt>(S)) {

1951

Visit(CS->getLHS());

1952 if

(

const Expr

*RHS = CS->getRHS())

1955

Visit(S->getSubStmt());

1958 void

coverIfConsteval(

const IfStmt

*S) {

1959

assert(S->isConsteval());

1961 const auto

*Then = S->getThen();

1962 const auto

*Else = S->getElse();

1967 const

Counter ParentCount =

getRegion

().getCounter();

1971 if

(S->isNegatedConsteval()) {

1973

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

1974

propagateCounts(ParentCount, Then);

1978

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

1981

assert(S->isNonNegatedConsteval());

1983

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

1986

propagateCounts(ParentCount, Else);

1990 void

coverIfConstexpr(

const IfStmt

*S) {

1991

assert(S->isConstexpr());

2003 const

Counter ParentCount =

getRegion

().getCounter();

2008 if

(

const auto

*

Init

= S->getInit()) {

2009 const auto

start = getStart(

Init

);

2010 const auto

end = getEnd(

Init

);

2014 if

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

2015

markSkipped(startOfSkipped, start);

2016

propagateCounts(ParentCount,

Init

);

2017

startOfSkipped = getEnd(

Init

);

2021 const auto

*Then = S->getThen();

2022 const auto

*Else = S->getElse();

2026

markSkipped(startOfSkipped, getStart(Then));

2027

propagateCounts(ParentCount, Then);

2031

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

2034

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

2037

propagateCounts(ParentCount, Else);

2041 void

VisitIfStmt(

const IfStmt

*S) {

2044 if

(S->isConsteval())

2045 return

coverIfConsteval(S);

2046 else if

(S->isConstexpr())

2047 return

coverIfConstexpr(S);

2051

Visit(S->getInit());

2055

extendRegion(S->getCond());

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

2067

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

2070

std::optional<SourceRange> Gap =

2071

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

2073

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

2075

extendRegion(S->getThen());

2076

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

2078 if

(

const Stmt

*Else = S->getElse()) {

2079 bool

ThenHasTerminateStmt = HasTerminateStmt;

2080

HasTerminateStmt =

false

;

2082

std::optional<SourceRange> Gap =

2083

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

2085

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

2088

Counter ElseOutCount = propagateCounts(ElseCount, Else);

2090

OutCount = addCounters(OutCount, ElseOutCount);

2092 if

(ThenHasTerminateStmt)

2093

HasTerminateStmt =

true

;

2095

OutCount = addCounters(OutCount, ElseCount);

2098

OutCount = getRegionCounter(S);

2100 if

(!IsCounterEqual(OutCount, ParentCount)) {

2101

pushRegion(OutCount);

2102

GapRegionCounter = OutCount;

2107

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

2113

extendRegion(S->getTryBlock());

2115

Counter ParentCount =

getRegion

().getCounter();

2116

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

2118 for

(

unsigned

I = 0,

E

= S->getNumHandlers(); I <

E

; ++I)

2119

Visit(S->getHandler(I));

2121

Counter ExitCount = getRegionCounter(S);

2122

pushRegion(ExitCount);

2126

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

2132

Counter ParentCount =

getRegion

().getCounter();

2133 auto

[TrueCount, FalseCount] =

2135

? BranchCounterPair{getRegionCounter(

E

->getTrueExpr()),

2136

getRegionCounter(

E

->getFalseExpr())}

2137

: getBranchCounterPair(

E

, ParentCount));

2140 if

(

const auto

*BCO = dyn_cast<BinaryConditionalOperator>(

E

)) {

2141

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

2142

OutCount = TrueCount;

2144

propagateCounts(ParentCount,

E

->getCond());

2147

findGapAreaBetween(

E

->getQuestionLoc(), getStart(

E

->getTrueExpr()));

2149

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

2151

extendRegion(

E

->getTrueExpr());

2152

OutCount = propagateCounts(TrueCount,

E

->getTrueExpr());

2155

extendRegion(

E

->getFalseExpr());

2156

Counter FalseOutCount = propagateCounts(FalseCount,

E

->getFalseExpr());

2158

OutCount = getRegionCounter(

E

);

2160

OutCount = addCounters(OutCount, FalseOutCount);

2162 if

(!IsCounterEqual(OutCount, ParentCount)) {

2163

pushRegion(OutCount);

2164

GapRegionCounter = OutCount;

2169

createBranchRegion(

E

->getCond(), TrueCount, FalseCount);

2172 void

createOrCancelDecision(

const BinaryOperator

*

E

,

unsigned

Since) {

2173 unsigned

NumConds = MCDCBuilder.getTotalConditionsAndReset(

E

);

2179 for

(

const auto

&SR :

ArrayRef

(SourceRegions).slice(Since)) {

2180 if

(SR.isMCDCBranch()) {

2181 auto

[

ID

, Conds] = SR.getMCDCBranchParams();

2182

CondIDs[

ID

] = Conds;

2187

mcdc::TVIdxBuilder Builder(CondIDs);

2188 unsigned

NumTVs = Builder.NumTestVectors;

2190

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

2192 if

(NumTVs > MaxTVs) {

2194

cancelDecision(

E

, Since, NumTVs, MaxTVs);

2202

std::move(Builder.Indices),

2205 auto

DecisionParams = mcdc::DecisionParameters{

2211

createDecisionRegion(

E

, DecisionParams);

2215 void

cancelDecision(

const BinaryOperator

*

E

,

unsigned

Since,

int

NumTVs,

2220 "unsupported MC/DC boolean expression; " 2221 "number of test vectors (%0) exceeds max (%1). " 2222 "Expression will not be covered"

);

2227

assert(!SR.isMCDCDecision() &&

"Decision shouldn't be seen here"

);

2228 if

(SR.isMCDCBranch())

2229

SR.resetMCDCParams();

2239 SM

.isInSystemHeader(

SM

.getSpellingLoc(

E

->getOperatorLoc())) &&

2245 if

(isExprInSystemHeader(

E

)) {

2246

LeafExprSet.insert(

E

);

2250 bool

IsRootNode = MCDCBuilder.isIdle();

2252 unsigned

SourceRegionsSince = SourceRegions.size();

2255

MCDCBuilder.pushAndAssignIDs(

E

);

2257

extendRegion(

E

->getLHS());

2258

propagateCounts(

getRegion

().getCounter(),

E

->getLHS());

2259

handleFileExit(getEnd(

E

->getLHS()));

2262 const auto

DecisionLHS = MCDCBuilder.pop();

2265

extendRegion(

E

->getRHS());

2266

propagateCounts(getRegionCounter(

E

),

E

->getRHS());

2272 const auto

DecisionRHS = MCDCBuilder.back();

2275

Counter ParentCnt =

getRegion

().getCounter();

2278 auto

[RHSExecCnt, LHSExitCnt] = getBranchCounterPair(

E

, ParentCnt);

2281 auto

[RHSTrueCnt, RHSExitCnt] =

2282

getBranchCounterPair(

E

->getRHS(), RHSExecCnt);

2285

createBranchRegion(

E

->getLHS(), RHSExecCnt, LHSExitCnt, DecisionLHS);

2288

createBranchRegion(

E

->getRHS(), RHSTrueCnt, RHSExitCnt, DecisionRHS);

2292

createOrCancelDecision(

E

, SourceRegionsSince);

2296 bool

shouldVisitRHS(

const Expr

*LHS) {

2297 bool

LHSIsTrue =

false

;

2298 bool

LHSIsConst =

false

;

2302 return

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

2306 if

(isExprInSystemHeader(

E

)) {

2307

LeafExprSet.insert(

E

);

2311 bool

IsRootNode = MCDCBuilder.isIdle();

2313 unsigned

SourceRegionsSince = SourceRegions.size();

2316

MCDCBuilder.pushAndAssignIDs(

E

);

2318

extendRegion(

E

->getLHS());

2319

Counter OutCount = propagateCounts(

getRegion

().getCounter(),

E

->getLHS());

2320

handleFileExit(getEnd(

E

->getLHS()));

2323 const auto

DecisionLHS = MCDCBuilder.pop();

2326

extendRegion(

E

->getRHS());

2327

propagateCounts(getRegionCounter(

E

),

E

->getRHS());

2333 const auto

DecisionRHS = MCDCBuilder.back();

2336

Counter ParentCnt =

getRegion

().getCounter();

2339 auto

[RHSExecCnt, LHSExitCnt] = getBranchCounterPair(

E

, ParentCnt);

2342 auto

[RHSFalseCnt, RHSExitCnt] =

2343

getBranchCounterPair(

E

->getRHS(), RHSExecCnt);

2345 if

(!shouldVisitRHS(

E

->getLHS())) {

2346

GapRegionCounter = OutCount;

2350

createBranchRegion(

E

->getLHS(), LHSExitCnt, RHSExecCnt, DecisionLHS);

2353

createBranchRegion(

E

->getRHS(), RHSExitCnt, RHSFalseCnt, DecisionRHS);

2357

createOrCancelDecision(

E

, SourceRegionsSince);

2360 void

VisitLambdaExpr(

const LambdaExpr

*LE) {

2382static void dump

(llvm::raw_ostream &OS, StringRef FunctionName,

2385

OS << FunctionName <<

":\n"

;

2386

CounterMappingContext Ctx(Expressions);

2387 for

(

const auto

&R : Regions) {

2390 case

CounterMappingRegion::CodeRegion:

2392 case

CounterMappingRegion::ExpansionRegion:

2393

OS <<

"Expansion,"

;

2395 case

CounterMappingRegion::SkippedRegion:

2398 case

CounterMappingRegion::GapRegion:

2401 case

CounterMappingRegion::BranchRegion:

2402 case

CounterMappingRegion::MCDCBranchRegion:

2405 case

CounterMappingRegion::MCDCDecisionRegion:

2410

OS <<

"File "

<< R.FileID <<

", "

<< R.LineStart <<

":"

<< R.ColumnStart

2411

<<

" -> "

<< R.LineEnd <<

":"

<< R.ColumnEnd <<

" = "

;

2413 if

(

const auto

*DecisionParams =

2414

std::get_if<mcdc::DecisionParameters>(&R.MCDCParams)) {

2415

OS <<

"M:"

<< DecisionParams->BitmapIdx;

2416

OS <<

", C:"

<< DecisionParams->NumConditions;

2418

Ctx.dump(R.Count, OS);

2422

Ctx.dump(R.FalseCount, OS);

2426 if

(

const auto

*BranchParams =

2427

std::get_if<mcdc::BranchParameters>(&R.MCDCParams)) {

2428

OS <<

" ["

<< BranchParams->ID + 1 <<

"," 2429

<< BranchParams->Conds[

true

] + 1;

2430

OS <<

","

<< BranchParams->Conds[

false

] + 1 <<

"] "

;

2433 if

(R.Kind == CounterMappingRegion::ExpansionRegion)

2434

OS <<

" (Expanded file = "

<< R.ExpandedFileID <<

")"

;

2441

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

2443

std::string CoverageMappingModuleGen::getCurrentDirname() {

2448

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

2449 return

CWD.str().str();

2452

std::string CoverageMappingModuleGen::normalizeFilename(StringRef

Filename

) {

2454

llvm::sys::path::remove_dots(

Path

,

true

);

2459 for

(

const auto

&[From, To] :

2461 if

(llvm::sys::path::replace_path_prefix(

Path

, From, To))

2464 return Path

.str().str();

2468

llvm::InstrProfSectKind SK) {

2469 return

llvm::getInstrProfSectionName(

2473void

CoverageMappingModuleGen::emitFunctionMappingRecord(

2474 const

FunctionInfo &Info, uint64_t FilenamesRef) {

2478

std::string FuncRecordName =

"__covrec_"

+ llvm::utohexstr(Info.NameHash);

2485

FuncRecordName +=

"u"

;

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" 2495 auto

*FunctionRecordTy =

2496

llvm::StructType::get(Ctx,

ArrayRef

(FunctionRecordTypes),

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

llvm::Constant *FunctionRecordVals[] = {

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

*FuncRecordConstant =

2505

llvm::ConstantStruct::get(FunctionRecordTy,

ArrayRef

(FunctionRecordVals));

2508 auto

*FuncRecord =

new

llvm::GlobalVariable(

2509

CGM.

getModule

(), FunctionRecordTy,

true

,

2510

llvm::GlobalValue::LinkOnceODRLinkage, FuncRecordConstant,

2512

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

2514

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

2516

FuncRecord->setComdat(CGM.

getModule

().getOrInsertComdat(FuncRecordName));

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});

2529

FunctionNames.push_back(NamePtr);

2538

std::vector<StringRef> Filenames;

2539

std::vector<CounterExpression> Expressions;

2540

std::vector<CounterMappingRegion> 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());

2548

RawCoverageMappingReader Reader(CoverageMapping, FilenameRefs, Filenames,

2549

Expressions, Regions);

2552 dump

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

2557 if

(FunctionRecords.empty())

2560 auto

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

2564

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

2566

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

2567 for

(

const auto

&Entry : FileEntries) {

2568 auto

I = Entry.second;

2569

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

2572

std::string Filenames;

2574

llvm::raw_string_ostream OS(Filenames);

2575

CoverageFilenamesSectionWriter(FilenameStrs).write(OS);

2577 auto

*FilenamesVal =

2578

llvm::ConstantDataArray::getString(Ctx, Filenames,

false

);

2579 const

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

2582 for

(

const

FunctionInfo &Info : FunctionRecords)

2583

emitFunctionMappingRecord(Info, FilenamesRef);

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" 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" 2598 auto

CovDataHeaderVal =

2599

llvm::ConstantStruct::get(CovDataHeaderTy,

ArrayRef

(CovDataHeaderVals));

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());

2611

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

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

2622 new

llvm::GlobalVariable(CGM.

getModule

(), NamesArrTy,

true

,

2623

llvm::GlobalValue::InternalLinkage, NamesArrVal,

2624

llvm::getCoverageUnusedNamesVarName());

2629 return

FileEntries.try_emplace(

File

, FileEntries.size() + 1).first->second;

2633

llvm::raw_ostream &OS) {

2634

assert(CounterMap && MCDCState);

2635

CounterCoverageMappingBuilder Walker(CVM, *CounterMap, *MCDCState,

SM

,

2637

Walker.VisitDecl(

D

);

2642

llvm::raw_ostream &OS) {

2643

EmptyCoverageMappingBuilder Walker(CVM,

SM

, LangOpts);

2644

Walker.VisitDecl(

D

);

Defines the Diagnostic-related interfaces.

static const MemRegion * getRegion(const CallEvent &Call, const MutexDescriptor &Descriptor, bool IsLock)

static std::string getInstrProfSection(const CodeGenModule &CGM, llvm::InstrProfSectKind SK)

static void dump(llvm::raw_ostream &OS, StringRef FunctionName, ArrayRef< CounterExpression > Expressions, ArrayRef< CounterMappingRegion > Regions)

static llvm::cl::opt< bool > EmptyLineCommentCoverage("emptyline-comment-coverage", llvm::cl::desc("Emit emptylines and comment lines as skipped regions (only " "disable it on test)"), llvm::cl::init(true), llvm::cl::Hidden)

llvm::DenseSet< const void * > Visited

static DiagnosticBuilder Diag(DiagnosticsEngine *Diags, const LangOptions &Features, FullSourceLoc TokLoc, const char *TokBegin, const char *TokRangeBegin, const char *TokRangeEnd, unsigned DiagID)

Produce a diagnostic highlighting some portion of a literal.

const TargetInfo & getTargetInfo() const

AbstractConditionalOperator - An abstract base class for ConditionalOperator and BinaryConditionalOpe...

Represents a loop initializing the elements of an array.

OpaqueValueExpr * getCommonExpr() const

Get the common subexpression shared by all initializations (the source array).

A builtin binary operation expression such as "x + y" or "x <= y".

BreakStmt - This represents a break.

CXXCatchStmt - This represents a C++ catch block.

CXXForRangeStmt - This represents C++0x [stmt.ranged]'s ranged for statement, represented as 'for (ra...

A C++ throw-expression (C++ [except.throw]).

CXXTryStmt - A C++ try block, including all handlers.

CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).

llvm::SmallVector< std::pair< std::string, std::string >, 0 > CoveragePrefixMap

Prefix replacement map for source-based code coverage to remap source file paths in coverage mapping.

std::string CoverageCompilationDir

The string to embed in coverage mapping as the current working directory.

This class organizes the cross-function state that is used while generating LLVM code.

llvm::Module & getModule() const

DiagnosticsEngine & getDiags() const

void addUsedGlobal(llvm::GlobalValue *GV)

Add a global to a list to be added to the llvm.used metadata.

ASTContext & getContext() const

bool supportsCOMDAT() const

const CodeGenOptions & getCodeGenOpts() const

llvm::LLVMContext & getLLVMContext()

void emitEmptyMapping(const Decl *D, llvm::raw_ostream &OS)

Emit the coverage mapping data for an unused function.

void emitCounterMapping(const Decl *D, llvm::raw_ostream &OS)

Emit the coverage mapping data which maps the regions of code to counters that will be used to find t...

Organizes the cross-function state that is used while generating code coverage mapping data.

void addFunctionMappingRecord(llvm::GlobalVariable *FunctionName, StringRef FunctionNameValue, uint64_t FunctionHash, const std::string &CoverageMapping, bool IsUsed=true)

Add a function's coverage mapping record to the collection of the function mapping records.

CoverageSourceInfo & getSourceInfo() const

static CoverageSourceInfo * setUpCoverageCallbacks(Preprocessor &PP)

CoverageMappingModuleGen(CodeGenModule &CGM, CoverageSourceInfo &SourceInfo)

void emit()

Emit the coverage mapping data for a translation unit.

CodeGenModule & getCodeGenModule()

Return an interface into CodeGenModule.

unsigned getFileID(FileEntryRef File)

Return the coverage mapping translation unit file id for the given file.

ConstStmtVisitor - This class implements a simple visitor for Stmt subclasses.

ContinueStmt - This represents a continue.

Represents a 'co_return' statement in the C++ Coroutines TS.

Represents the body of a coroutine.

Represents an expression that might suspend coroutine execution; either a co_await or co_yield expres...

Stores additional source code information like skipped ranges which is required by the coverage mappi...

void SourceRangeSkipped(SourceRange Range, SourceLocation EndifLoc) override

Hook called when a source range is skipped.

void updateNextTokLoc(SourceLocation Loc)

void AddSkippedRange(SourceRange Range, SkippedRange::Kind RangeKind)

std::vector< SkippedRange > & getSkippedRanges()

bool HandleComment(Preprocessor &PP, SourceRange Range) override

SourceLocation PrevTokLoc

void HandleEmptyline(SourceRange Range) override

Decl - This represents one declaration (or definition), e.g.

virtual Stmt * getBody() const

getBody - If this Decl represents a declaration for a body of code, such as a function or method defi...

virtual bool hasBody() const

Returns true if this Decl represents a declaration for a body of code, such as a function or method d...

DoStmt - This represents a 'do/while' stmt.

This represents one expression.

bool isValueDependent() const

Determines whether the value of this expression depends on.

bool EvaluateAsBooleanCondition(bool &Result, const ASTContext &Ctx, bool InConstantContext=false) const

EvaluateAsBooleanCondition - Return true if this is a constant which we can fold and convert to a boo...

A reference to a FileEntry that includes the name of the file as it was accessed by the FileManager's...

An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...

ForStmt - This represents a 'for (init;cond;inc)' stmt.

GotoStmt - This represents a direct goto.

IfStmt - This represents an if/then/else.

LabelStmt - Represents a label, which has a substatement.

A C++ lambda expression, which produces a function object (of unspecified type) that can be invoked l...

Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...

static unsigned MeasureTokenLength(SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts)

MeasureTokenLength - Relex the token at the specified location and return its length in bytes in the ...

Represents Objective-C's collection statement.

OpaqueValueExpr - An expression referring to an opaque object of a fixed type and value class.

Expr * getSourceExpr() const

The source expression of an opaque value expression is the expression which originally generated the ...

Engages in a tight little dance with the lexer to efficiently preprocess tokens.

void addCommentHandler(CommentHandler *Handler)

Add the specified comment handler to the preprocessor.

void addPPCallbacks(std::unique_ptr< PPCallbacks > C)

SourceManager & getSourceManager() const

void setPreprocessToken(bool Preprocess)

void setTokenWatcher(llvm::unique_function< void(const clang::Token &)> F)

Register a function that would be called on each token in the final expanded token stream.

void setEmptylineHandler(EmptylineHandler *Handler)

Set empty line handler.

PseudoObjectExpr - An expression which accesses a pseudo-object l-value.

Expr * getSyntacticForm()

Return the syntactic form of this expression, i.e.

A (possibly-)qualified type.

ReturnStmt - This represents a return, optionally of an expression: return; return 4;.

Encodes a location in the source.

bool isValid() const

Return true if this is a valid SourceLocation object.

SourceLocation getLocWithOffset(IntTy Offset) const

Return a source location with the specified offset from this SourceLocation.

This class handles loading and caching of source files into memory.

A trivial tuple used to represent a source range.

SourceLocation getEnd() const

SourceLocation getBegin() const

Each ExpansionInfo encodes the expansion location - where the token was ultimately expanded,...

SourceLocation getExpansionLocStart() const

bool isFunctionMacroExpansion() const

SourceLocation getExpansionLocEnd() const

Stmt - This represents one statement.

SourceLocation getEndLoc() const LLVM_READONLY

SourceLocation getBeginLoc() const LLVM_READONLY

SourceLocation getColonLoc() const

const SwitchCase * getNextSwitchCase() const

SwitchStmt - This represents a 'switch' stmt.

const llvm::Triple & getTriple() const

Returns the target triple of the primary target.

Token - This structure provides full information about a lexed token.

SourceLocation getLocation() const

Return a source location identifier for the specified offset in the current file.

tok::TokenKind getKind() const

WhileStmt - This represents a 'while' stmt.

The JSON file list parser is used to communicate input to InstallAPI.

FunctionType::ExtInfo getFunctionExtInfo(const Type &t)

cl::opt< bool > SystemHeadersCoverage

Diagnostic wrappers for TextAPI types for error reporting.

cl::opt< bool > EnableSingleByteCoverage

Per-Function MC/DC state.

llvm::DenseMap< const Stmt *, Branch > BranchByStmt

llvm::DenseMap< const Stmt *, Decision > DecisionByStmt

EvalResult is a struct with detailed info about an evaluated expression.


RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4