A RetroSearch Logo

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

Search Query:

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

clang: lib/Analysis/ThreadSafety.cpp Source File

40#include "llvm/ADT/ArrayRef.h" 41#include "llvm/ADT/DenseMap.h" 42#include "llvm/ADT/ImmutableMap.h" 43#include "llvm/ADT/STLExtras.h" 44#include "llvm/ADT/SmallVector.h" 45#include "llvm/ADT/StringRef.h" 46#include "llvm/Support/Allocator.h" 47#include "llvm/Support/Casting.h" 48#include "llvm/Support/ErrorHandling.h" 49#include "llvm/Support/raw_ostream.h" 61using namespace clang

;

62using namespace

threadSafety;

70 const Expr

*DeclExp, StringRef Kind) {

84class

CapExprSet :

public SmallVector

<CapabilityExpr, 4> {

89 return

CapE.

equals

(CapE2);

106 enum

FactEntryKind { Lockable, ScopedLockable };

117 const

FactEntryKind

Kind

: 8;

123

SourceKind Source : 8;

132 virtual

~FactEntry() =

default

;

136

FactEntryKind getFactEntryKind()

const

{

return Kind

; }

138 bool

asserted()

const

{

return

Source == Asserted; }

139 bool

declared()

const

{

return

Source == Declared; }

140 bool

managed()

const

{

return

Source == Managed; }

143

handleRemovalFromIntersection(

const

FactSet &FSet, FactManager &FactMan,

146 virtual void

handleLock(FactSet &FSet, FactManager &FactMan,

147 const

FactEntry &entry,

149 virtual void

handleUnlock(FactSet &FSet, FactManager &FactMan,

155 bool

isAtLeast(

LockKind

LK)

const

{

160using

FactID =

unsigned

short;

166

std::vector<std::unique_ptr<const FactEntry>> Facts;

169

FactID newFact(std::unique_ptr<FactEntry> Entry) {

170

Facts.push_back(std::move(Entry));

171 return static_cast<unsigned short>

(Facts.size() - 1);

174 const

FactEntry &operator[](FactID F)

const

{

return

*Facts[F]; }

191 using

iterator = FactVec::iterator;

192 using

const_iterator = FactVec::const_iterator;

194

iterator begin() {

return

FactIDs.begin(); }

195

const_iterator begin()

const

{

return

FactIDs.begin(); }

197

iterator end() {

return

FactIDs.end(); }

198

const_iterator end()

const

{

return

FactIDs.end(); }

200 bool

isEmpty()

const

{

return

FactIDs.size() == 0; }

203 bool

isEmpty(FactManager &FactMan)

const

{

204 for

(

const auto

FID : *

this

) {

205 if

(!FactMan[FID].negative())

211 void

addLockByID(FactID ID) { FactIDs.push_back(ID); }

213

FactID addLock(FactManager &FM, std::unique_ptr<FactEntry> Entry) {

214

FactID F = FM.newFact(std::move(Entry));

215

FactIDs.push_back(F);

220 unsigned

n = FactIDs.size();

224 for

(

unsigned

i = 0; i < n-1; ++i) {

225 if

(FM[FactIDs[i]].

matches

(CapE)) {

226

FactIDs[i] = FactIDs[n-1];

231 if

(FM[FactIDs[n-1]].

matches

(CapE)) {

238

iterator findLockIter(FactManager &FM,

const CapabilityExpr

&CapE) {

239 return

std::find_if(begin(), end(), [&](FactID ID) {

240 return

FM[

ID

].matches(CapE);

244 const

FactEntry *findLock(FactManager &FM,

const CapabilityExpr

&CapE)

const

{

245 auto

I = std::find_if(begin(), end(), [&](FactID ID) {

246 return

FM[

ID

].matches(CapE);

248 return

I != end() ? &FM[*I] :

nullptr

;

251 const

FactEntry *findLockUniv(FactManager &FM,

253 auto

I = std::find_if(begin(), end(), [&](FactID ID) ->

bool

{

254 return

FM[

ID

].matchesUniv(CapE);

256 return

I != end() ? &FM[*I] :

nullptr

;

259 const

FactEntry *findPartialMatch(FactManager &FM,

261 auto

I = std::find_if(begin(), end(), [&](FactID ID) ->

bool

{

262 return

FM[

ID

].partiallyMatches(CapE);

264 return

I != end() ? &FM[*I] :

nullptr

;

267 bool

containsMutexDecl(FactManager &FM,

const ValueDecl

* Vd)

const

{

268 auto

I = std::find_if(begin(), end(), [&](FactID ID) ->

bool

{

269 return

FM[

ID

].valueDecl() == Vd;

275class

ThreadSafetyAnalyzer;

280namespace

threadSafety {

290

BeforeInfo() =

default

;

291

BeforeInfo(BeforeInfo &&) =

default

;

295

llvm::DenseMap<const ValueDecl *, std::unique_ptr<BeforeInfo>>;

296 using

CycleMap = llvm::DenseMap<const ValueDecl *, bool>;

302

ThreadSafetyAnalyzer& Analyzer);

305

ThreadSafetyAnalyzer &Analyzer);

309

ThreadSafetyAnalyzer& Analyzer,

322class

LocalVariableMap;

324using

LocalVarContext = llvm::ImmutableMap<const NamedDecl *, unsigned>;

327enum

CFGBlockSide { CBS_Entry, CBS_Exit };

340

LocalVarContext EntryContext;

343

LocalVarContext ExitContext;

355 bool

Reachable =

false

;

357 const

FactSet &getSet(CFGBlockSide Side)

const

{

358 return

Side == CBS_Entry ? EntrySet : ExitSet;

362 return

Side == CBS_Entry ? EntryLoc : ExitLoc;

366

CFGBlockInfo(LocalVarContext EmptyCtx)

367

: EntryContext(EmptyCtx), ExitContext(EmptyCtx) {}

370 static

CFGBlockInfo getEmptyBlockInfo(LocalVariableMap &M);

386class

LocalVariableMap {

388 using

Context = LocalVarContext;

394 struct

VarDefinition {

396 friend class

LocalVariableMap;

402 const Expr

*Exp =

nullptr

;

410 bool

isReference()

const

{

return

!Exp; }

415

:

Dec

(

D

), Exp(

E

), Ctx(

C

) {}

418

VarDefinition(

const NamedDecl

*

D

,

unsigned

R, Context

C

)

419

:

Dec

(

D

), Ref(R), Ctx(

C

) {}

423

Context::Factory ContextFactory;

424

std::vector<VarDefinition> VarDefinitions;

425

std::vector<std::pair<const Stmt *, Context>> SavedContexts;

430

VarDefinitions.push_back(VarDefinition(

nullptr

, 0u, getEmptyContext()));

434 const

VarDefinition* lookup(

const NamedDecl

*

D

, Context Ctx) {

435 const unsigned

*i = Ctx.lookup(

D

);

438

assert(*i < VarDefinitions.size());

439 return

&VarDefinitions[*i];

446 const unsigned

*

P

= Ctx.lookup(

D

);

452 if

(VarDefinitions[i].Exp) {

453

Ctx = VarDefinitions[i].Ctx;

454 return

VarDefinitions[i].Exp;

456

i = VarDefinitions[i].Ref;

461

Context getEmptyContext() {

return

ContextFactory.getEmptyMap(); }

466

Context getNextContext(

unsigned

&CtxIndex,

const Stmt

*S, Context

C

) {

467 if

(SavedContexts[CtxIndex+1].first == S) {

469

Context Result = SavedContexts[CtxIndex].second;

475 void

dumpVarDefinitionName(

unsigned

i) {

477

llvm::errs() <<

"Undefined"

;

482

llvm::errs() <<

"<<NULL>>"

;

485 Dec

->printName(llvm::errs());

486

llvm::errs() <<

"."

<< i <<

" "

<< ((

const void

*) Dec);

491 for

(

unsigned

i = 1, e = VarDefinitions.size(); i < e; ++i) {

492 const Expr

*Exp = VarDefinitions[i].Exp;

493 unsigned

Ref = VarDefinitions[i].Ref;

495

dumpVarDefinitionName(i);

496

llvm::errs() <<

" = "

;

497 if

(Exp) Exp->

dump

();

499

dumpVarDefinitionName(Ref);

500

llvm::errs() <<

"\n"

;

506 void

dumpContext(Context

C

) {

507 for

(Context::iterator I =

C

.begin(),

E

=

C

.end(); I !=

E

; ++I) {

509 D

->printName(llvm::errs());

510

llvm::errs() <<

" -> "

;

511

dumpVarDefinitionName(I.getData());

512

llvm::errs() <<

"\n"

;

518

std::vector<CFGBlockInfo> &BlockInfo);

521 friend class

VarMapBuilder;

524 unsigned

getContextIndex() {

return

SavedContexts.size()-1; }

527 void

saveContext(

const Stmt

*S, Context

C

) {

528

SavedContexts.push_back(std::make_pair(S,

C

));

533

Context addDefinition(

const NamedDecl

*

D

,

const Expr

*Exp, Context Ctx) {

534

assert(!Ctx.contains(

D

));

535 unsigned

newID = VarDefinitions.size();

536

Context NewCtx = ContextFactory.add(Ctx,

D

, newID);

537

VarDefinitions.push_back(VarDefinition(

D

, Exp, Ctx));

542

Context addReference(

const NamedDecl

*

D

,

unsigned

i, Context Ctx) {

543 unsigned

newID = VarDefinitions.size();

544

Context NewCtx = ContextFactory.add(Ctx,

D

, newID);

545

VarDefinitions.push_back(VarDefinition(

D

, i, Ctx));

551

Context updateDefinition(

const NamedDecl

*

D

,

Expr

*Exp, Context Ctx) {

552 if

(Ctx.contains(

D

)) {

553 unsigned

newID = VarDefinitions.size();

554

Context NewCtx = ContextFactory.remove(Ctx,

D

);

555

NewCtx = ContextFactory.add(NewCtx,

D

, newID);

556

VarDefinitions.push_back(VarDefinition(

D

, Exp, Ctx));

564

Context clearDefinition(

const NamedDecl

*

D

, Context Ctx) {

565

Context NewCtx = Ctx;

566 if

(NewCtx.contains(

D

)) {

567

NewCtx = ContextFactory.remove(NewCtx,

D

);

568

NewCtx = ContextFactory.add(NewCtx,

D

, 0);

574

Context removeDefinition(

const NamedDecl

*

D

, Context Ctx) {

575

Context NewCtx = Ctx;

576 if

(NewCtx.contains(

D

)) {

577

NewCtx = ContextFactory.remove(NewCtx,

D

);

582

Context intersectContexts(Context C1, Context C2);

583

Context createReferenceContext(Context

C

);

584 void

intersectBackEdge(Context C1, Context C2);

590

CFGBlockInfo CFGBlockInfo::getEmptyBlockInfo(LocalVariableMap &M) {

591 return

CFGBlockInfo(M.getEmptyContext());

599

LocalVariableMap* VMap;

600

LocalVariableMap::Context Ctx;

602

VarMapBuilder(LocalVariableMap *VM, LocalVariableMap::Context

C

)

603

: VMap(VM), Ctx(

C

) {}

605 void

VisitDeclStmt(

const DeclStmt

*S);

612void

VarMapBuilder::VisitDeclStmt(

const DeclStmt

*S) {

613 bool

modifiedCtx =

false

;

615 for

(

const auto

*

D

: DGrp) {

616 if

(

const auto

*VD = dyn_cast_or_null<VarDecl>(

D

)) {

617 const Expr

*

E

= VD->getInit();

621 if

(

T

.isTrivialType(VD->getASTContext())) {

622

Ctx = VMap->addDefinition(VD,

E

, Ctx);

628

VMap->saveContext(S, Ctx);

632void

VarMapBuilder::VisitBinaryOperator(

const BinaryOperator

*BO) {

639 if

(

const auto

*DRE = dyn_cast<DeclRefExpr>(LHSExp)) {

641 if

(Ctx.lookup(VDec)) {

643

Ctx = VMap->updateDefinition(VDec, BO->

getRHS

(), Ctx);

646

Ctx = VMap->clearDefinition(VDec, Ctx);

647

VMap->saveContext(BO, Ctx);

655

LocalVariableMap::Context

656

LocalVariableMap::intersectContexts(Context C1, Context C2) {

658 for

(

const auto

&

P

: C1) {

660 const unsigned

*i2 = C2.lookup(Dec);

662

Result = removeDefinition(Dec, Result);

663 else if

(*i2 !=

P

.second)

664

Result = clearDefinition(Dec, Result);

672

LocalVariableMap::Context LocalVariableMap::createReferenceContext(Context

C

) {

673

Context Result = getEmptyContext();

674 for

(

const auto

&

P

:

C

)

675

Result = addReference(

P

.first,

P

.second, Result);

682void

LocalVariableMap::intersectBackEdge(Context C1, Context C2) {

683 for

(

const auto

&

P

: C1) {

684 unsigned

i1 =

P

.second;

685

VarDefinition *VDef = &VarDefinitions[i1];

686

assert(VDef->isReference());

688 const unsigned

*i2 = C2.lookup(

P

.first);

689 if

(!i2 || (*i2 != i1))

731void

LocalVariableMap::traverseCFG(

CFG

*CFGraph,

733

std::vector<CFGBlockInfo> &BlockInfo) {

736 for

(

const auto

*CurrBlock : *SortedGraph) {

737 unsigned

CurrBlockID = CurrBlock->getBlockID();

738

CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlockID];

740

VisitedBlocks.insert(CurrBlock);

743 bool

HasBackEdges =

false

;

744 bool

CtxInit =

true

;

746

PE = CurrBlock->pred_end(); PI != PE; ++PI) {

748 if

(*PI ==

nullptr

|| !VisitedBlocks.alreadySet(*PI)) {

749

HasBackEdges =

true

;

753 unsigned

PrevBlockID = (*PI)->getBlockID();

754

CFGBlockInfo *PrevBlockInfo = &BlockInfo[PrevBlockID];

757

CurrBlockInfo->EntryContext = PrevBlockInfo->ExitContext;

761

CurrBlockInfo->EntryContext =

762

intersectContexts(CurrBlockInfo->EntryContext,

763

PrevBlockInfo->ExitContext);

770

CurrBlockInfo->EntryContext =

771

createReferenceContext(CurrBlockInfo->EntryContext);

774

saveContext(

nullptr

, CurrBlockInfo->EntryContext);

775

CurrBlockInfo->EntryIndex = getContextIndex();

778

VarMapBuilder VMapBuilder(

this

, CurrBlockInfo->EntryContext);

779 for

(

const auto

&BI : *CurrBlock) {

780 switch

(BI.getKind()) {

783

VMapBuilder.Visit(CS.

getStmt

());

790

CurrBlockInfo->ExitContext = VMapBuilder.Ctx;

794

SE = CurrBlock->succ_end(); SI != SE; ++SI) {

796 if

(*SI ==

nullptr

|| !VisitedBlocks.alreadySet(*SI))

800

Context LoopBegin = BlockInfo[FirstLoopBlock->

getBlockID

()].EntryContext;

801

Context LoopEnd = CurrBlockInfo->ExitContext;

802

intersectBackEdge(LoopBegin, LoopEnd);

808

saveContext(

nullptr

, BlockInfo[exitID].ExitContext);

815

std::vector<CFGBlockInfo> &BlockInfo) {

816 for

(

const auto

*CurrBlock : *SortedGraph) {

817

CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlock->getBlockID()];

821 if

(

const Stmt

*S = CurrBlock->getTerminatorStmt()) {

822

CurrBlockInfo->EntryLoc = CurrBlockInfo->ExitLoc = S->getBeginLoc();

825

BE = CurrBlock->rend(); BI != BE; ++BI) {

827 if

(std::optional<CFGStmt> CS = BI->getAs<

CFGStmt

>()) {

828

CurrBlockInfo->ExitLoc = CS->getStmt()->getBeginLoc();

834 if

(CurrBlockInfo->ExitLoc.isValid()) {

837 for

(

const auto

&BI : *CurrBlock) {

839 if

(std::optional<CFGStmt> CS = BI.getAs<

CFGStmt

>()) {

840

CurrBlockInfo->EntryLoc = CS->getStmt()->getBeginLoc();

844

}

else if

(CurrBlock->pred_size() == 1 && *CurrBlock->pred_begin() &&

845

CurrBlock != &CFGraph->

getExit

()) {

848

CurrBlockInfo->EntryLoc = CurrBlockInfo->ExitLoc =

849

BlockInfo[(*CurrBlock->pred_begin())->getBlockID()].ExitLoc;

850

}

else if

(CurrBlock->succ_size() == 1 && *CurrBlock->succ_begin()) {

853

CurrBlockInfo->EntryLoc = CurrBlockInfo->ExitLoc =

854

BlockInfo[(*CurrBlock->succ_begin())->getBlockID()].EntryLoc;

861class

LockableFactEntry :

public

FactEntry {

864

SourceKind Src = Acquired)

865

: FactEntry(Lockable, CE, LK,

Loc

, Src) {}

868

handleRemovalFromIntersection(

const

FactSet &FSet, FactManager &FactMan,

871 if

(!asserted() && !negative() && !isUniversal()) {

877 void

handleLock(FactSet &FSet, FactManager &FactMan,

const

FactEntry &entry,

883 void

handleUnlock(FactSet &FSet, FactManager &FactMan,

887

FSet.removeLock(FactMan, Cp);

889

FSet.addLock(FactMan, std::make_unique<LockableFactEntry>(

894 static bool

classof(

const

FactEntry *A) {

895 return

A->getFactEntryKind() == Lockable;

899class

ScopedLockableFactEntry :

public

FactEntry {

901 enum

UnderlyingCapabilityKind {

904

UCK_ReleasedExclusive,

907 struct

UnderlyingCapability {

909

UnderlyingCapabilityKind

Kind

;

919

CapExprSet getUnderlyingMutexes()

const

{

920

CapExprSet UnderlyingMutexesSet;

921 for

(

const

UnderlyingCapability &UnderlyingMutex : UnderlyingMutexes)

922

UnderlyingMutexesSet.push_back(UnderlyingMutex.Cap);

923 return

UnderlyingMutexesSet;

927

UnderlyingMutexes.push_back(UnderlyingCapability{M, UCK_Acquired});

931

UnderlyingMutexes.push_back(UnderlyingCapability{M, UCK_ReleasedExclusive});

935

UnderlyingMutexes.push_back(UnderlyingCapability{M, UCK_ReleasedShared});

939

handleRemovalFromIntersection(

const

FactSet &FSet, FactManager &FactMan,

945 for

(

const auto

&UnderlyingMutex : UnderlyingMutexes) {

946 const auto

*Entry = FSet.findLock(FactMan, UnderlyingMutex.Cap);

947 if

((UnderlyingMutex.Kind == UCK_Acquired && Entry) ||

948

(UnderlyingMutex.Kind != UCK_Acquired && !Entry)) {

952

UnderlyingMutex.Cap.toString(), loc(),

958 void

handleLock(FactSet &FSet, FactManager &FactMan,

const

FactEntry &entry,

960 for

(

const auto

&UnderlyingMutex : UnderlyingMutexes) {

961 if

(UnderlyingMutex.Kind == UCK_Acquired)

962

lock(FSet, FactMan, UnderlyingMutex.Cap, entry.kind(), entry.loc(),

965

unlock(FSet, FactMan, UnderlyingMutex.Cap, entry.loc(), &Handler);

969 void

handleUnlock(FactSet &FSet, FactManager &FactMan,

973

assert(!Cp.

negative

() &&

"Managing object cannot be negative."

);

974 for

(

const auto

&UnderlyingMutex : UnderlyingMutexes) {

978 if

(UnderlyingMutex.Kind == UCK_Acquired) {

979

unlock(FSet, FactMan, UnderlyingMutex.Cap, UnlockLoc, TSHandler);

981 LockKind kind

= UnderlyingMutex.Kind == UCK_ReleasedShared

984

lock(FSet, FactMan, UnderlyingMutex.Cap, kind, UnlockLoc, TSHandler);

988

FSet.removeLock(FactMan, Cp);

991 static bool

classof(

const

FactEntry *A) {

992 return

A->getFactEntryKind() == ScopedLockable;

996 void

lock(FactSet &FSet, FactManager &FactMan,

const CapabilityExpr

&Cp,

999 if

(

const

FactEntry *Fact = FSet.findLock(FactMan, Cp)) {

1004

FSet.removeLock(FactMan, !Cp);

1005

FSet.addLock(FactMan,

1006

std::make_unique<LockableFactEntry>(Cp, kind, loc, Managed));

1010 void

unlock(FactSet &FSet, FactManager &FactMan,

const CapabilityExpr

&Cp,

1012 if

(FSet.findLock(FactMan, Cp)) {

1013

FSet.removeLock(FactMan, Cp);

1014

FSet.addLock(FactMan, std::make_unique<LockableFactEntry>(

1016

}

else if

(Handler) {

1018 if

(

const

FactEntry *Neg = FSet.findLock(FactMan, !Cp))

1019

PrevLoc =

Neg

->loc();

1026class

ThreadSafetyAnalyzer {

1027 friend class

BuildLockset;

1030

llvm::BumpPtrAllocator Bpa;

1036

LocalVariableMap LocalVarMap;

1038

llvm::SmallDenseMap<const Expr *, til::LiteralPtr *> ConstructedObjects;

1039

FactManager FactMan;

1040

std::vector<CFGBlockInfo> BlockInfo;

1046

: Arena(&Bpa), SxBuilder(Arena), Handler(H), GlobalBeforeSet(Bset) {}

1050 void

addLock(FactSet &FSet, std::unique_ptr<FactEntry> Entry,

1051 bool

ReqAttr =

false

);

1055 template

<

typename

AttrType>

1056 void

getMutexIDs(CapExprSet &Mtxs, AttrType *

Attr

,

const Expr

*Exp,

1059 template

<

class

AttrType>

1060 void

getMutexIDs(CapExprSet &Mtxs, AttrType *

Attr

,

const Expr

*Exp,

1063 Expr

*BrE,

bool

Neg);

1065 const CallExpr

* getTrylockCallExpr(

const Stmt

*Cond, LocalVarContext

C

,

1068 void

getEdgeLockset(FactSet &Result,

const

FactSet &ExitSet,

1072 bool

join(

const

FactEntry &a,

const

FactEntry &

b

,

bool

CanModify);

1074 void

intersectAndWarn(FactSet &EntrySet,

const

FactSet &ExitSet,

1078 void

intersectAndWarn(FactSet &EntrySet,

const

FactSet &ExitSet,

1080

intersectAndWarn(EntrySet, ExitSet, JoinLoc, LEK, LEK);

1085 void

warnIfMutexNotHeld(

const

FactSet &FSet,

const NamedDecl

*

D

,

1089 void

warnIfMutexHeld(

const

FactSet &FSet,

const NamedDecl

*

D

,

const Expr

*Exp,

1093 void

checkAccess(

const

FactSet &FSet,

const Expr

*Exp,

AccessKind

AK,

1095 void

checkPtAccess(

const

FactSet &FSet,

const Expr

*Exp,

AccessKind

AK,

1103

ThreadSafetyAnalyzer& Analyzer) {

1105

BeforeInfo *Info =

nullptr

;

1109

std::unique_ptr<BeforeInfo> &InfoPtr = BMap[Vd];

1111

InfoPtr.reset(

new

BeforeInfo());

1112

Info = InfoPtr.get();

1115 for

(

const auto

*At : Vd->

attrs

()) {

1116 switch

(At->getKind()) {

1117 case

attr::AcquiredBefore: {

1118 const auto

*A = cast<AcquiredBeforeAttr>(At);

1121 for

(

const auto

*Arg : A->args()) {

1123

Analyzer.SxBuilder.translateAttrExpr(Arg,

nullptr

);

1125

Info->Vect.push_back(Cpvd);

1126 const auto

It = BMap.find(Cpvd);

1127 if

(It == BMap.end())

1133 case

attr::AcquiredAfter: {

1134 const auto

*A = cast<AcquiredAfterAttr>(At);

1137 for

(

const auto

*Arg : A->args()) {

1139

Analyzer.SxBuilder.translateAttrExpr(Arg,

nullptr

);

1143

ArgInfo->Vect.push_back(Vd);

1156

BeforeSet::BeforeInfo *

1158

ThreadSafetyAnalyzer &Analyzer) {

1159 auto

It = BMap.find(Vd);

1160

BeforeInfo *Info =

nullptr

;

1161 if

(It == BMap.end())

1164

Info = It->second.get();

1165

assert(Info &&

"BMap contained nullptr?"

);

1171 const

FactSet& FSet,

1172

ThreadSafetyAnalyzer& Analyzer,

1184 if

(Info->Visited == 1)

1187 if

(Info->Visited == 2)

1190 if

(Info->Vect.empty())

1193

InfoVect.push_back(Info);

1195 for

(

const auto

*Vdb : Info->Vect) {

1197 if

(FSet.containsMutexDecl(Analyzer.FactMan, Vdb)) {

1198

StringRef L1 = StartVd->

getName

();

1199

StringRef L2 = Vdb->getName();

1200

Analyzer.Handler.handleLockAcquiredBefore(CapKind, L1, L2,

Loc

);

1204 if

(CycMap.try_emplace(Vd,

true

).second) {

1205

StringRef L1 = Vd->

getName

();

1206

Analyzer.Handler.handleBeforeAfterCycle(L1, Vd->

getLocation

());

1216 for

(

auto

*Info : InfoVect)

1222 if

(

const auto

*CE = dyn_cast<ImplicitCastExpr>(Exp))

1225 if

(

const auto

*DR = dyn_cast<DeclRefExpr>(Exp))

1226 return

DR->getDecl();

1228 if

(

const auto

*ME = dyn_cast<MemberExpr>(Exp))

1229 return

ME->getMemberDecl();

1236template

<

typename

Ty>

1237class

has_arg_iterator_range {

1238 using

yes =

char

[1];

1239 using

no =

char

[2];

1241 template

<

typename

Inner>

1242 static

yes& test(Inner *I,

decltype

(I->args()) * =

nullptr

);

1244 template

<

typename

>

1245 static

no& test(...);

1248 static const bool

value =

sizeof

(test<Ty>(

nullptr

)) ==

sizeof

(yes);

1253bool

ThreadSafetyAnalyzer::inCurrentScope(

const CapabilityExpr

&CapE) {

1255

assert(SExp &&

"Null expressions should be ignored"

);

1257 if

(

const auto

*LP = dyn_cast<til::LiteralPtr>(SExp)) {

1270 if

(

const auto

*

P

= dyn_cast<til::Project>(SExp)) {

1271 if

(!isa_and_nonnull<CXXMethodDecl>(CurrentFunction))

1282void

ThreadSafetyAnalyzer::addLock(FactSet &FSet,

1283

std::unique_ptr<FactEntry> Entry,

1285 if

(Entry->shouldIgnore())

1288 if

(!ReqAttr && !Entry->negative()) {

1291 const

FactEntry *Nen = FSet.findLock(FactMan, NegC);

1293

FSet.removeLock(FactMan, NegC);

1296 if

(inCurrentScope(*Entry) && !Entry->asserted())

1304

!Entry->asserted() && !Entry->declared()) {

1305

GlobalBeforeSet->checkBeforeAfter(Entry->valueDecl(), FSet, *

this

,

1306

Entry->loc(), Entry->getKind());

1310 if

(

const

FactEntry *Cp = FSet.findLock(FactMan, *Entry)) {

1311 if

(!Entry->asserted())

1312

Cp->handleLock(FSet, FactMan, *Entry, Handler);

1314

FSet.addLock(FactMan, std::move(Entry));

1320void

ThreadSafetyAnalyzer::removeLock(FactSet &FSet,

const CapabilityExpr

&Cp,

1322 bool

FullyRemove,

LockKind

ReceivedKind) {

1326 const

FactEntry *LDat = FSet.findLock(FactMan, Cp);

1329 if

(

const

FactEntry *Neg = FSet.findLock(FactMan, !Cp))

1330

PrevLoc =

Neg

->loc();

1338 if

(ReceivedKind !=

LK_Generic

&& LDat->kind() != ReceivedKind) {

1340

ReceivedKind, LDat->loc(), UnlockLoc);

1343

LDat->handleUnlock(FSet, FactMan, Cp, UnlockLoc, FullyRemove, Handler);

1348template

<

typename

AttrType>

1349void

ThreadSafetyAnalyzer::getMutexIDs(CapExprSet &Mtxs, AttrType *

Attr

,

1352 if

(

Attr

->args_size() == 0) {

1361

Mtxs.push_back_nodup(Cp);

1365 for

(

const auto

*Arg :

Attr

->args()) {

1373

Mtxs.push_back_nodup(Cp);

1380template

<

class

AttrType>

1381void

ThreadSafetyAnalyzer::getMutexIDs(CapExprSet &Mtxs, AttrType *

Attr

,

1385 Expr

*BrE,

bool

Neg) {

1387 bool

branch =

false

;

1388 if

(

const auto

*BLE = dyn_cast_or_null<CXXBoolLiteralExpr>(BrE))

1389

branch = BLE->getValue();

1390 else if

(

const auto

*ILE = dyn_cast_or_null<IntegerLiteral>(BrE))

1391

branch = ILE->getValue().getBoolValue();

1393 int

branchnum = branch ? 0 : 1;

1395

branchnum = !branchnum;

1400

SE = PredBlock->

succ_end

(); SI != SE && i < 2; ++SI, ++i) {

1401 if

(*SI == CurrBlock && i == branchnum)

1402

getMutexIDs(Mtxs,

Attr

, Exp,

D

);

1407 if

(isa<CXXNullPtrLiteralExpr>(

E

) || isa<GNUNullExpr>(

E

)) {

1410

}

else if

(

const auto

*BLE = dyn_cast<CXXBoolLiteralExpr>(

E

)) {

1411

TCond = BLE->getValue();

1413

}

else if

(

const auto

*ILE = dyn_cast<IntegerLiteral>(

E

)) {

1414

TCond = ILE->getValue().getBoolValue();

1416

}

else if

(

auto

*CE = dyn_cast<ImplicitCastExpr>(

E

))

1424const CallExpr

* ThreadSafetyAnalyzer::getTrylockCallExpr(

const Stmt

*Cond,

1430 if

(

const auto

*CallExp = dyn_cast<CallExpr>(Cond)) {

1431 if

(CallExp->getBuiltinCallee() == Builtin::BI__builtin_expect)

1432 return

getTrylockCallExpr(CallExp->getArg(0),

C

, Negate);

1435 else if

(

const auto

*PE = dyn_cast<ParenExpr>(Cond))

1436 return

getTrylockCallExpr(PE->getSubExpr(),

C

, Negate);

1437 else if

(

const auto

*CE = dyn_cast<ImplicitCastExpr>(Cond))

1438 return

getTrylockCallExpr(CE->getSubExpr(),

C

, Negate);

1439 else if

(

const auto

*FE = dyn_cast<FullExpr>(Cond))

1440 return

getTrylockCallExpr(FE->getSubExpr(),

C

, Negate);

1441 else if

(

const auto

*DRE = dyn_cast<DeclRefExpr>(Cond)) {

1442 const Expr

*

E

= LocalVarMap.lookupExpr(DRE->getDecl(),

C

);

1443 return

getTrylockCallExpr(

E

,

C

, Negate);

1445 else if

(

const auto

*UOP = dyn_cast<UnaryOperator>(Cond)) {

1446 if

(UOP->getOpcode() == UO_LNot) {

1448 return

getTrylockCallExpr(UOP->getSubExpr(),

C

, Negate);

1452 else if

(

const auto

*BOP = dyn_cast<BinaryOperator>(Cond)) {

1453 if

(BOP->getOpcode() == BO_EQ || BOP->getOpcode() == BO_NE) {

1454 if

(BOP->getOpcode() == BO_NE)

1457 bool

TCond =

false

;

1459 if

(!TCond) Negate = !Negate;

1460 return

getTrylockCallExpr(BOP->getLHS(),

C

, Negate);

1464 if

(!TCond) Negate = !Negate;

1465 return

getTrylockCallExpr(BOP->getRHS(),

C

, Negate);

1469 if

(BOP->getOpcode() == BO_LAnd) {

1471 return

getTrylockCallExpr(BOP->getRHS(),

C

, Negate);

1473 if

(BOP->getOpcode() == BO_LOr)

1474 return

getTrylockCallExpr(BOP->getRHS(),

C

, Negate);

1476

}

else if

(

const auto

*COP = dyn_cast<ConditionalOperator>(Cond)) {

1480 if

(TCond && !FCond)

1481 return

getTrylockCallExpr(COP->getCond(),

C

, Negate);

1482 if

(!TCond && FCond) {

1484 return

getTrylockCallExpr(COP->getCond(),

C

, Negate);

1494void

ThreadSafetyAnalyzer::getEdgeLockset(FactSet&

Result

,

1495 const

FactSet &ExitSet,

1505 bool

Negate =

false

;

1506 const

CFGBlockInfo *PredBlockInfo = &BlockInfo[PredBlock->

getBlockID

()];

1507 const

LocalVarContext &LVarCtx = PredBlockInfo->ExitContext;

1509 const auto

*Exp = getTrylockCallExpr(Cond, LVarCtx, Negate);

1513 auto

*FunDecl = dyn_cast_or_null<NamedDecl>(Exp->getCalleeDecl());

1514 if

(!FunDecl || !FunDecl->hasAttrs())

1517

CapExprSet ExclusiveLocksToAdd;

1518

CapExprSet SharedLocksToAdd;

1521 for

(

const auto

*

Attr

: FunDecl->attrs()) {

1523 case

attr::TryAcquireCapability: {

1524 auto

*A = cast<TryAcquireCapabilityAttr>(

Attr

);

1525

getMutexIDs(A->isShared() ? SharedLocksToAdd : ExclusiveLocksToAdd, A,

1526

Exp, FunDecl, PredBlock, CurrBlock, A->getSuccessValue(),

1530 case

attr::ExclusiveTrylockFunction: {

1531 const auto

*A = cast<ExclusiveTrylockFunctionAttr>(

Attr

);

1532

getMutexIDs(ExclusiveLocksToAdd, A, Exp, FunDecl, PredBlock, CurrBlock,

1533

A->getSuccessValue(), Negate);

1536 case

attr::SharedTrylockFunction: {

1537 const auto

*A = cast<SharedTrylockFunctionAttr>(

Attr

);

1538

getMutexIDs(SharedLocksToAdd, A, Exp, FunDecl, PredBlock, CurrBlock,

1539

A->getSuccessValue(), Negate);

1549 for

(

const auto

&ExclusiveLockToAdd : ExclusiveLocksToAdd)

1550

addLock(

Result

, std::make_unique<LockableFactEntry>(ExclusiveLockToAdd,

1552 for

(

const auto

&SharedLockToAdd : SharedLocksToAdd)

1553

addLock(

Result

, std::make_unique<LockableFactEntry>(SharedLockToAdd,

1565 friend class

ThreadSafetyAnalyzer;

1567

ThreadSafetyAnalyzer *Analyzer;

1570 const

FactSet &FunctionExitFSet;

1571

LocalVariableMap::Context LVarCtx;

1578

Analyzer->checkAccess(FSet, Exp, AK, POK);

1582

Analyzer->checkPtAccess(FSet, Exp, AK, POK);

1591 bool

SkipFirstParam =

false

);

1594

BuildLockset(ThreadSafetyAnalyzer *Anlzr, CFGBlockInfo &Info,

1595 const

FactSet &FunctionExitFSet)

1597

FunctionExitFSet(FunctionExitFSet), LVarCtx(Info.EntryContext),

1598

CtxIndex(Info.EntryIndex) {}

1602 void

VisitCastExpr(

const CastExpr

*CE);

1603 void

VisitCallExpr(

const CallExpr

*Exp);

1605 void

VisitDeclStmt(

const DeclStmt

*S);

1614void

ThreadSafetyAnalyzer::warnIfMutexNotHeld(

1629 const

FactEntry *LDat = FSet.findLock(FactMan, !Cp);

1632

(!Cp).toString(),

Loc

);

1638 if

(!inCurrentScope(Cp))

1642

LDat = FSet.findLock(FactMan, Cp);

1649 const

FactEntry *LDat = FSet.findLockUniv(FactMan, Cp);

1650 bool

NoError =

true

;

1653

LDat = FSet.findPartialMatch(FactMan, Cp);

1656

std::string PartMatchStr = LDat->toString();

1657

StringRef PartMatchName(PartMatchStr);

1667 if

(NoError && LDat && !LDat->isAtLeast(LK)) {

1673void

ThreadSafetyAnalyzer::warnIfMutexHeld(

const

FactSet &FSet,

1686 const

FactEntry *LDat = FSet.findLock(FactMan, Cp);

1698void

ThreadSafetyAnalyzer::checkAccess(

const

FactSet &FSet,

const Expr

*Exp,

1707 while

(

const auto

*DRE = dyn_cast<DeclRefExpr>(Exp)) {

1708 const auto

*VD = dyn_cast<VarDecl>(DRE->getDecl()->getCanonicalDecl());

1710 if

(

const auto

*

E

= VD->getInit()) {

1721 if

(

const auto

*UO = dyn_cast<UnaryOperator>(Exp)) {

1723 if

(UO->getOpcode() == UO_Deref)

1724

checkPtAccess(FSet, UO->getSubExpr(), AK, POK);

1728 if

(

const auto

*BO = dyn_cast<BinaryOperator>(Exp)) {

1731 return

checkAccess(FSet, BO->

getLHS

(), AK, POK);

1733 return

checkPtAccess(FSet, BO->

getLHS

(), AK, POK);

1739 if

(

const auto

*AE = dyn_cast<ArraySubscriptExpr>(Exp)) {

1740

checkPtAccess(FSet, AE->getLHS(), AK, POK);

1744 if

(

const auto

*ME = dyn_cast<MemberExpr>(Exp)) {

1746

checkPtAccess(FSet, ME->getBase(), AK, POK);

1748

checkAccess(FSet, ME->getBase(), AK, POK);

1755 if

(

D

->

hasAttr

<GuardedVarAttr>() && FSet.isEmpty(FactMan)) {

1760

warnIfMutexNotHeld(FSet,

D

, Exp, AK, I->getArg(), POK,

nullptr

,

Loc

);

1765void

ThreadSafetyAnalyzer::checkPtAccess(

const

FactSet &FSet,

const Expr

*Exp,

1769 if

(

const auto

*PE = dyn_cast<ParenExpr>(Exp)) {

1770

Exp = PE->getSubExpr();

1773 if

(

const auto

*CE = dyn_cast<CastExpr>(Exp)) {

1774 if

(CE->getCastKind() == CK_ArrayToPointerDecay) {

1777

checkAccess(FSet, CE->getSubExpr(), AK, POK);

1780

Exp = CE->getSubExpr();

1796 if

(

D

->

hasAttr

<PtGuardedVarAttr>() && FSet.isEmpty(FactMan))

1800

warnIfMutexNotHeld(FSet,

D

, Exp, AK, I->getArg(), PtPOK,

nullptr

,

1819void

BuildLockset::handleCall(

const Expr

*Exp,

const NamedDecl

*

D

,

1821

CapExprSet ExclusiveLocksToAdd, SharedLocksToAdd;

1822

CapExprSet ExclusiveLocksToRemove, SharedLocksToRemove, GenericLocksToRemove;

1823

CapExprSet ScopedReqsAndExcludes;

1831

std::pair<til::LiteralPtr *, StringRef> Placeholder =

1832

Analyzer->SxBuilder.createThisPlaceholder(Exp);

1833

[[maybe_unused]]

auto

inserted =

1834

Analyzer->ConstructedObjects.insert({Exp, Placeholder.first});

1835

assert(inserted.second &&

"Are we visiting the same expression again?"

);

1836 if

(isa<CXXConstructExpr>(Exp))

1837 Self

= Placeholder.first;

1838 if

(TagT->getDecl()->hasAttr<ScopedLockableAttr>())

1839

Scp =

CapabilityExpr

(Placeholder.first, Placeholder.second,

false

);

1847 switch

(At->getKind()) {

1850 case

attr::AcquireCapability: {

1851 const auto

*A = cast<AcquireCapabilityAttr>(At);

1852

Analyzer->getMutexIDs(A->isShared() ? SharedLocksToAdd

1853

: ExclusiveLocksToAdd,

1861 case

attr::AssertExclusiveLock: {

1862 const auto

*A = cast<AssertExclusiveLockAttr>(At);

1864

CapExprSet AssertLocks;

1865

Analyzer->getMutexIDs(AssertLocks, A, Exp,

D

,

Self

);

1866 for

(

const auto

&AssertLock : AssertLocks)

1868

FSet, std::make_unique<LockableFactEntry>(

1872 case

attr::AssertSharedLock: {

1873 const auto

*A = cast<AssertSharedLockAttr>(At);

1875

CapExprSet AssertLocks;

1876

Analyzer->getMutexIDs(AssertLocks, A, Exp,

D

,

Self

);

1877 for

(

const auto

&AssertLock : AssertLocks)

1879

FSet, std::make_unique<LockableFactEntry>(

1884 case

attr::AssertCapability: {

1885 const auto

*A = cast<AssertCapabilityAttr>(At);

1886

CapExprSet AssertLocks;

1887

Analyzer->getMutexIDs(AssertLocks, A, Exp,

D

,

Self

);

1888 for

(

const auto

&AssertLock : AssertLocks)

1889

Analyzer->addLock(FSet, std::make_unique<LockableFactEntry>(

1892 Loc

, FactEntry::Asserted));

1898 case

attr::ReleaseCapability: {

1899 const auto

*A = cast<ReleaseCapabilityAttr>(At);

1901

Analyzer->getMutexIDs(GenericLocksToRemove, A, Exp,

D

,

Self

);

1902 else if

(A->isShared())

1903

Analyzer->getMutexIDs(SharedLocksToRemove, A, Exp,

D

,

Self

);

1905

Analyzer->getMutexIDs(ExclusiveLocksToRemove, A, Exp,

D

,

Self

);

1909 case

attr::RequiresCapability: {

1910 const auto

*A = cast<RequiresCapabilityAttr>(At);

1911 for

(

auto

*Arg : A->args()) {

1912

Analyzer->warnIfMutexNotHeld(FSet,

D

, Exp,

1917

Analyzer->getMutexIDs(ScopedReqsAndExcludes, A, Exp,

D

,

Self

);

1922 case

attr::LocksExcluded: {

1923 const auto

*A = cast<LocksExcludedAttr>(At);

1924 for

(

auto

*Arg : A->args()) {

1925

Analyzer->warnIfMutexHeld(FSet,

D

, Exp, Arg,

Self

,

Loc

);

1928

Analyzer->getMutexIDs(ScopedReqsAndExcludes, A, Exp,

D

,

Self

);

1939

std::optional<CallExpr::const_arg_range> Args;

1941 if

(

const auto

*CE = dyn_cast<CallExpr>(Exp))

1942

Args = CE->arguments();

1943 else if

(

const auto

*CE = dyn_cast<CXXConstructExpr>(Exp))

1944

Args = CE->arguments();

1946

llvm_unreachable(

"Unknown call kind"

);

1948 const auto

*CalledFunction = dyn_cast<FunctionDecl>(

D

);

1949 if

(CalledFunction && Args.has_value()) {

1950 for

(

auto

[Param, Arg] : zip(CalledFunction->parameters(), *Args)) {

1951

CapExprSet DeclaredLocks;

1952 for

(

const Attr

*At : Param->attrs()) {

1953 switch

(At->getKind()) {

1954 case

attr::AcquireCapability: {

1955 const auto

*A = cast<AcquireCapabilityAttr>(At);

1956

Analyzer->getMutexIDs(A->isShared() ? SharedLocksToAdd

1957

: ExclusiveLocksToAdd,

1959

Analyzer->getMutexIDs(DeclaredLocks, A, Exp,

D

,

Self

);

1963 case

attr::ReleaseCapability: {

1964 const auto

*A = cast<ReleaseCapabilityAttr>(At);

1966

Analyzer->getMutexIDs(GenericLocksToRemove, A, Exp,

D

,

Self

);

1967 else if

(A->isShared())

1968

Analyzer->getMutexIDs(SharedLocksToRemove, A, Exp,

D

,

Self

);

1970

Analyzer->getMutexIDs(ExclusiveLocksToRemove, A, Exp,

D

,

Self

);

1971

Analyzer->getMutexIDs(DeclaredLocks, A, Exp,

D

,

Self

);

1975 case

attr::RequiresCapability: {

1976 const auto

*A = cast<RequiresCapabilityAttr>(At);

1977 for

(

auto

*Arg : A->args())

1978

Analyzer->warnIfMutexNotHeld(FSet,

D

, Exp,

1981

Analyzer->getMutexIDs(DeclaredLocks, A, Exp,

D

,

Self

);

1985 case

attr::LocksExcluded: {

1986 const auto

*A = cast<LocksExcludedAttr>(At);

1987 for

(

auto

*Arg : A->args())

1988

Analyzer->warnIfMutexHeld(FSet,

D

, Exp, Arg,

Self

,

Loc

);

1989

Analyzer->getMutexIDs(DeclaredLocks, A, Exp,

D

,

Self

);

1997 if

(DeclaredLocks.empty())

2000

StringRef(

"mutex"

),

false

);

2001 if

(

const auto

*CBTE = dyn_cast<CXXBindTemporaryExpr>(Arg->IgnoreCasts());

2003 if

(

auto

Object = Analyzer->ConstructedObjects.find(CBTE->getSubExpr());

2004

Object != Analyzer->ConstructedObjects.end())

2007 const

FactEntry *Fact = FSet.findLock(Analyzer->FactMan, Cp);

2014 const auto

*

Scope

= cast<ScopedLockableFactEntry>(Fact);

2015 for

(

const auto

&[a,

b

] :

2016

zip_longest(DeclaredLocks,

Scope

->getUnderlyingMutexes())) {

2017 if

(!a.has_value()) {

2018

Analyzer->Handler.handleExpectFewerUnderlyingMutexes(

2020 b

.value().getKind(),

b

.value().toString());

2021

}

else if

(!

b

.has_value()) {

2022

Analyzer->Handler.handleExpectMoreUnderlyingMutexes(

2024

a.value().getKind(), a.value().toString());

2025

}

else if

(!a.value().equals(

b

.value())) {

2026

Analyzer->Handler.handleUnmatchedUnderlyingMutexes(

2028

a.value().getKind(), a.value().toString(),

b

.value().toString());

2036 bool

Dtor = isa<CXXDestructorDecl>(

D

);

2037 for

(

const auto

&M : ExclusiveLocksToRemove)

2039 for

(

const auto

&M : SharedLocksToRemove)

2041 for

(

const auto

&M : GenericLocksToRemove)

2045

FactEntry::SourceKind Source =

2046

!Scp.

shouldIgnore

() ? FactEntry::Managed : FactEntry::Acquired;

2047 for

(

const auto

&M : ExclusiveLocksToAdd)

2048

Analyzer->addLock(FSet, std::make_unique<LockableFactEntry>(M,

LK_Exclusive

,

2050 for

(

const auto

&M : SharedLocksToAdd)

2052

FSet, std::make_unique<LockableFactEntry>(M,

LK_Shared

,

Loc

, Source));

2056 auto

ScopedEntry = std::make_unique<ScopedLockableFactEntry>(

2057

Scp,

Loc

, FactEntry::Acquired);

2058 for

(

const auto

&M : ExclusiveLocksToAdd)

2059

ScopedEntry->addLock(M);

2060 for

(

const auto

&M : SharedLocksToAdd)

2061

ScopedEntry->addLock(M);

2062 for

(

const auto

&M : ScopedReqsAndExcludes)

2063

ScopedEntry->addLock(M);

2064 for

(

const auto

&M : ExclusiveLocksToRemove)

2065

ScopedEntry->addExclusiveUnlock(M);

2066 for

(

const auto

&M : SharedLocksToRemove)

2067

ScopedEntry->addSharedUnlock(M);

2068

Analyzer->addLock(FSet, std::move(ScopedEntry));

2075void

BuildLockset::VisitUnaryOperator(

const UnaryOperator

*UO) {

2091void

BuildLockset::VisitBinaryOperator(

const BinaryOperator

*BO) {

2096

LVarCtx = Analyzer->LocalVarMap.getNextContext(CtxIndex, BO, LVarCtx);

2104void

BuildLockset::VisitCastExpr(

const CastExpr

*CE) {

2110void

BuildLockset::examineArguments(

const FunctionDecl

*FD,

2113 bool

SkipFirstParam) {

2123 if

(FD->

hasAttr

<NoThreadSafetyAnalysisAttr>())

2127 auto

Param = Params.begin();

2132 for

(

auto

Arg = ArgBegin; Param != Params.end() && Arg != ArgEnd;

2140void

BuildLockset::VisitCallExpr(

const CallExpr

*Exp) {

2141 if

(

const auto

*CE = dyn_cast<CXXMemberCallExpr>(Exp)) {

2142 const auto

*ME = dyn_cast<MemberExpr>(CE->getCallee());

2147 if

(ME->isArrow()) {

2149

checkPtAccess(CE->getImplicitObjectArgument(),

AK_Read

);

2152

checkAccess(CE->getImplicitObjectArgument(),

AK_Read

);

2156

examineArguments(CE->getDirectCallee(), CE->arg_begin(), CE->arg_end());

2157

}

else if

(

const auto

*OE = dyn_cast<CXXOperatorCallExpr>(Exp)) {

2165 case

OO_PercentEqual:

2169 case

OO_LessLessEqual:

2170 case

OO_GreaterGreaterEqual:

2171

checkAccess(OE->getArg(1),

AK_Read

);

2181 if

(!(OEop == OO_Star && OE->getNumArgs() > 1)) {

2183

checkPtAccess(OE->getArg(0),

AK_Read

);

2188 const Expr

*Obj = OE->getArg(0);

2194

examineArguments(FD, std::next(OE->arg_begin()), OE->arg_end(),

2195

!isa<CXXMethodDecl>(FD));

2206

handleCall(Exp,

D

);

2211 if

(

D

&&

D

->isCopyConstructor()) {

2213

checkAccess(Source,

AK_Read

);

2218

handleCall(Exp,

D

);

2222 if

(

auto

*CE = dyn_cast<CastExpr>(

E

))

2225 if

(

auto

*CE = dyn_cast<CastExpr>(

E

))

2226 if

(CE->

getCastKind

() == CK_ConstructorConversion ||

2229 if

(

auto

*BTE = dyn_cast<CXXBindTemporaryExpr>(

E

))

2230 E

= BTE->getSubExpr();

2234void

BuildLockset::VisitDeclStmt(

const DeclStmt

*S) {

2236

LVarCtx = Analyzer->LocalVarMap.getNextContext(CtxIndex, S, LVarCtx);

2238 for

(

auto

*

D

: S->getDeclGroup()) {

2239 if

(

auto

*VD = dyn_cast_or_null<VarDecl>(

D

)) {

2240 const Expr

*

E

= VD->getInit();

2246 if

(

auto

*EWC = dyn_cast<ExprWithCleanups>(

E

))

2250 if

(

auto

Object = Analyzer->ConstructedObjects.find(

E

);

2251

Object != Analyzer->ConstructedObjects.end()) {

2252 Object

->second->setClangDecl(VD);

2253

Analyzer->ConstructedObjects.erase(Object);

2259void

BuildLockset::VisitMaterializeTemporaryExpr(

2262 if

(

auto

Object = Analyzer->ConstructedObjects.find(

2264

Object != Analyzer->ConstructedObjects.end()) {

2265 Object

->second->setClangDecl(ExtD);

2266

Analyzer->ConstructedObjects.erase(Object);

2271void

BuildLockset::VisitReturnStmt(

const ReturnStmt

*S) {

2272 if

(Analyzer->CurrentFunction ==

nullptr

)

2274 const Expr

*RetVal = S->getRetValue();

2281

Analyzer->CurrentFunction->getReturnType().getCanonicalType();

2283

Analyzer->checkAccess(

2284

FunctionExitFSet, RetVal,

2295bool

ThreadSafetyAnalyzer::join(

const

FactEntry &A,

const

FactEntry &B,

2297 if

(A.kind() != B.kind()) {

2300 if

((A.managed() || A.asserted()) && (B.managed() || B.asserted())) {

2302 bool

ShouldTakeB = B.kind() ==

LK_Shared

;

2303 if

(CanModify || !ShouldTakeB)

2312 return

CanModify && A.asserted() && !B.asserted();

2330void

ThreadSafetyAnalyzer::intersectAndWarn(FactSet &EntrySet,

2331 const

FactSet &ExitSet,

2335

FactSet EntrySetOrig = EntrySet;

2338 for

(

const auto

&Fact : ExitSet) {

2339 const

FactEntry &ExitFact = FactMan[Fact];

2341

FactSet::iterator EntryIt = EntrySet.findLockIter(FactMan, ExitFact);

2342 if

(EntryIt != EntrySet.end()) {

2343 if

(join(FactMan[*EntryIt], ExitFact,

2347

ExitFact.handleRemovalFromIntersection(ExitSet, FactMan, JoinLoc,

2353 for

(

const auto

&Fact : EntrySetOrig) {

2354 const

FactEntry *EntryFact = &FactMan[Fact];

2355 const

FactEntry *ExitFact = ExitSet.findLock(FactMan, *EntryFact);

2360

EntryFact->handleRemovalFromIntersection(EntrySetOrig, FactMan, JoinLoc,

2363

EntrySet.removeLock(FactMan, *EntryFact);

2376 if

(std::optional<CFGStmt> S =

Last

.getAs<

CFGStmt

>()) {

2377 if

(isa<CXXThrowExpr>(S->getStmt()))

2392 if

(!walker.

init

(AC))

2400

CurrentFunction = dyn_cast<FunctionDecl>(

D

);

2402 if

(

D

->

hasAttr

<NoThreadSafetyAnalysisAttr>())

2409 if

(isa<CXXConstructorDecl>(

D

))

2411 if

(isa<CXXDestructorDecl>(

D

))

2417

CFGBlockInfo::getEmptyBlockInfo(LocalVarMap));

2429

Initial.Reachable =

true

;

2432

LocalVarMap.traverseCFG(CFGraph, SortedGraph, BlockInfo);

2437

CapExprSet ExclusiveLocksAcquired;

2438

CapExprSet SharedLocksAcquired;

2439

CapExprSet LocksReleased;

2444 if

(!SortedGraph->

empty

()) {

2446

FactSet &InitialLockset = Initial.EntrySet;

2448

CapExprSet ExclusiveLocksToAdd;

2449

CapExprSet SharedLocksToAdd;

2454 if

(

const auto

*A = dyn_cast<RequiresCapabilityAttr>(

Attr

)) {

2455

getMutexIDs(A->isShared() ? SharedLocksToAdd : ExclusiveLocksToAdd, A,

2457

}

else if

(

const auto

*A = dyn_cast<ReleaseCapabilityAttr>(

Attr

)) {

2460 if

(A->args_size() == 0)

2462

getMutexIDs(A->isShared() ? SharedLocksToAdd : ExclusiveLocksToAdd, A,

2464

getMutexIDs(LocksReleased, A,

nullptr

,

D

);

2465

}

else if

(

const auto

*A = dyn_cast<AcquireCapabilityAttr>(

Attr

)) {

2466 if

(A->args_size() == 0)

2468

getMutexIDs(A->isShared() ? SharedLocksAcquired

2469

: ExclusiveLocksAcquired,

2471

}

else if

(isa<ExclusiveTrylockFunctionAttr>(

Attr

)) {

2474

}

else if

(isa<SharedTrylockFunctionAttr>(

Attr

)) {

2477

}

else if

(isa<TryAcquireCapabilityAttr>(

Attr

)) {

2483 if

(CurrentFunction)

2485 else if

(

auto

CurrentMethod = dyn_cast<ObjCMethodDecl>(

D

))

2486

Params = CurrentMethod->getCanonicalDecl()->parameters();

2488

llvm_unreachable(

"Unknown function kind"

);

2490

CapExprSet UnderlyingLocks;

2491 for

(

const auto

*

Attr

: Param->attrs()) {

2493 if

(

const auto

*A = dyn_cast<ReleaseCapabilityAttr>(

Attr

)) {

2494

getMutexIDs(A->isShared() ? SharedLocksToAdd : ExclusiveLocksToAdd, A,

2496

getMutexIDs(LocksReleased, A,

nullptr

, Param);

2497

getMutexIDs(UnderlyingLocks, A,

nullptr

, Param);

2498

}

else if

(

const auto

*A = dyn_cast<RequiresCapabilityAttr>(

Attr

)) {

2499

getMutexIDs(A->isShared() ? SharedLocksToAdd : ExclusiveLocksToAdd, A,

2501

getMutexIDs(UnderlyingLocks, A,

nullptr

, Param);

2502

}

else if

(

const auto

*A = dyn_cast<AcquireCapabilityAttr>(

Attr

)) {

2503

getMutexIDs(A->isShared() ? SharedLocksAcquired

2504

: ExclusiveLocksAcquired,

2505

A,

nullptr

, Param);

2506

getMutexIDs(UnderlyingLocks, A,

nullptr

, Param);

2507

}

else if

(

const auto

*A = dyn_cast<LocksExcludedAttr>(

Attr

)) {

2508

getMutexIDs(UnderlyingLocks, A,

nullptr

, Param);

2511 if

(UnderlyingLocks.empty())

2513 CapabilityExpr

Cp(SxBuilder.createVariable(Param), StringRef(),

false

);

2514 auto

ScopedEntry = std::make_unique<ScopedLockableFactEntry>(

2515

Cp, Param->getLocation(), FactEntry::Declared);

2517

ScopedEntry->addLock(M);

2518

addLock(InitialLockset, std::move(ScopedEntry),

true

);

2522 for

(

const auto

&Mu : ExclusiveLocksToAdd) {

2523 auto

Entry = std::make_unique<LockableFactEntry>(Mu,

LK_Exclusive

,

Loc

,

2524

FactEntry::Declared);

2525

addLock(InitialLockset, std::move(Entry),

true

);

2527 for

(

const auto

&Mu : SharedLocksToAdd) {

2528 auto

Entry = std::make_unique<LockableFactEntry>(Mu,

LK_Shared

,

Loc

,

2529

FactEntry::Declared);

2530

addLock(InitialLockset, std::move(Entry),

true

);

2536

FactSet ExpectedFunctionExitSet = Initial.EntrySet;

2542 for

(

const auto

&Lock : ExclusiveLocksAcquired)

2543

ExpectedFunctionExitSet.addLock(

2544

FactMan, std::make_unique<LockableFactEntry>(Lock,

LK_Exclusive

,

2546 for

(

const auto

&Lock : SharedLocksAcquired)

2547

ExpectedFunctionExitSet.addLock(

2550 for

(

const auto

&Lock : LocksReleased)

2551

ExpectedFunctionExitSet.removeLock(FactMan, Lock);

2553 for

(

const auto

*CurrBlock : *SortedGraph) {

2554 unsigned

CurrBlockID = CurrBlock->

getBlockID

();

2555

CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlockID];

2558

VisitedBlocks.insert(CurrBlock);

2573 bool

LocksetInitialized =

false

;

2575

PE = CurrBlock->

pred_end

(); PI != PE; ++PI) {

2577 if

(*PI ==

nullptr

|| !VisitedBlocks.alreadySet(*PI))

2580 unsigned

PrevBlockID = (*PI)->getBlockID();

2581

CFGBlockInfo *PrevBlockInfo = &BlockInfo[PrevBlockID];

2588

CurrBlockInfo->Reachable =

true

;

2590

FactSet PrevLockset;

2591

getEdgeLockset(PrevLockset, PrevBlockInfo->ExitSet, *PI, CurrBlock);

2593 if

(!LocksetInitialized) {

2594

CurrBlockInfo->EntrySet = PrevLockset;

2595

LocksetInitialized =

true

;

2601

CurrBlockInfo->EntrySet, PrevLockset, CurrBlockInfo->EntryLoc,

2602

isa_and_nonnull<ContinueStmt>((*PI)->getTerminatorStmt())

2609 if

(!CurrBlockInfo->Reachable)

2612

BuildLockset LocksetBuilder(

this

, *CurrBlockInfo, ExpectedFunctionExitSet);

2615 for

(

const auto

&BI : *CurrBlock) {

2616 switch

(BI.getKind()) {

2619

LocksetBuilder.Visit(CS.

getStmt

());

2626 if

(!DD->hasAttrs())

2629

LocksetBuilder.handleCall(

nullptr

, DD,

2637

LocksetBuilder.handleCall(

nullptr

,

CF

.getFunctionDecl(),

2638

SxBuilder.createVariable(

CF

.getVarDecl()),

2639 CF

.getVarDecl()->getLocation());

2648 if

(

auto

Object = ConstructedObjects.find(

2649

TD.getBindTemporaryExpr()->getSubExpr());

2650 Object

!= ConstructedObjects.end()) {

2654

LocksetBuilder.handleCall(

nullptr

, DD,

Object

->second,

2655

TD.getBindTemporaryExpr()->getEndLoc());

2656

ConstructedObjects.erase(Object);

2664

CurrBlockInfo->ExitSet = LocksetBuilder.FSet;

2671

SE = CurrBlock->succ_end(); SI != SE; ++SI) {

2673 if

(*SI ==

nullptr

|| !VisitedBlocks.alreadySet(*SI))

2677

CFGBlockInfo *PreLoop = &BlockInfo[FirstLoopBlock->

getBlockID

()];

2678

CFGBlockInfo *LoopEnd = &BlockInfo[CurrBlockID];

2679

intersectAndWarn(PreLoop->EntrySet, LoopEnd->ExitSet, PreLoop->EntryLoc,

2685 if

(!Final.Reachable)

2689

intersectAndWarn(ExpectedFunctionExitSet, Final.ExitSet, Final.ExitLoc,

2705

ThreadSafetyAnalyzer Analyzer(Handler, *BSet);

2706

Analyzer.runAnalysis(AC);

2720

llvm_unreachable(

"Unknown AccessKind"

);

This file defines AnalysisDeclContext, a class that manages the analysis context data for context sen...

Defines enum values for all the target-independent builtin functions.

enum clang::sema::@1704::IndirectLocalPathEntry::EntryKind Kind

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

static Decl::Kind getKind(const Decl *D)

Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate....

Defines the clang::Expr interface and subclasses for C++ expressions.

llvm::DenseSet< const void * > Visited

Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified.

Defines an enumeration for C++ overloaded operators.

static std::string toString(const clang::SanitizerSet &Sanitizers)

Produce a string containing comma-separated names of sanitizers in Sanitizers set.

Defines the clang::SourceLocation class and associated facilities.

Defines various enumerations that describe declaration and type specifiers.

static void warnInvalidLock(ThreadSafetyHandler &Handler, const Expr *MutexExp, const NamedDecl *D, const Expr *DeclExp, StringRef Kind)

Issue a warning about an invalid lock expression.

static bool getStaticBooleanValue(Expr *E, bool &TCond)

static bool neverReturns(const CFGBlock *B)

static void findBlockLocations(CFG *CFGraph, const PostOrderCFGView *SortedGraph, std::vector< CFGBlockInfo > &BlockInfo)

Find the appropriate source locations to use when producing diagnostics for each block in the CFG.

static const ValueDecl * getValueDecl(const Expr *Exp)

Gets the value decl pointer from DeclRefExprs or MemberExprs.

static const Expr * UnpackConstruction(const Expr *E)

C Language Family Type Representation.

AnalysisDeclContext contains the context data for the function, method or block under analysis.

Attr - This represents one attribute.

attr::Kind getKind() const

SourceLocation getLocation() const

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

static bool isAssignmentOp(Opcode Opc)

Represents C++ object destructor implicitly generated for automatic object or temporary bound to cons...

const VarDecl * getVarDecl() const

const Stmt * getTriggerStmt() const

Represents a single basic block in a source-level CFG.

bool hasNoReturnElement() const

succ_iterator succ_begin()

Stmt * getTerminatorStmt()

AdjacentBlocks::const_iterator const_pred_iterator

pred_iterator pred_begin()

unsigned getBlockID() const

Stmt * getTerminatorCondition(bool StripParens=true)

AdjacentBlocks::const_iterator const_succ_iterator

Represents a top-level expression in a basic block.

T castAs() const

Convert to the specified CFGElement type, asserting that this CFGElement is of the desired type.

const CXXDestructorDecl * getDestructorDecl(ASTContext &astContext) const

const Stmt * getStmt() const

Represents C++ object destructor implicitly generated at the end of full expression for temporary obj...

Represents a source-level, intra-procedural CFG that represents the control-flow of a Stmt.

unsigned getNumBlockIDs() const

Returns the total number of BlockIDs allocated (which start at 0).

Represents a call to a C++ constructor.

Expr * getArg(unsigned Arg)

Return the specified argument.

CXXConstructorDecl * getConstructor() const

Get the constructor that this expression will (ultimately) call.

Represents a C++ constructor within a class.

Represents a static or instance method of a struct/union/class.

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

FunctionDecl * getDirectCallee()

If the callee is a FunctionDecl, return it. Otherwise return null.

CastExpr - Base class for type casts, including both implicit casts (ImplicitCastExpr) and explicit c...

CastKind getCastKind() const

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

DeclStmt - Adaptor class for mixing declarations with statements and expressions.

llvm::iterator_range< specific_attr_iterator< T > > specific_attrs() const

SourceLocation getLocation() const

bool isDefinedOutsideFunctionOrMethod() const

isDefinedOutsideFunctionOrMethod - This predicate returns true if this scoped decl is defined outside...

DeclContext * getDeclContext()

This represents one expression.

Expr * IgnoreParenCasts() LLVM_READONLY

Skip past any parentheses and casts which might surround this expression until reaching a fixed point...

Expr * IgnoreImplicit() LLVM_READONLY

Skip past any implicit AST nodes which might surround this expression until reaching a fixed point.

Expr * IgnoreParens() LLVM_READONLY

Skip past any parentheses which might surround this expression until reaching a fixed point.

SourceLocation getExprLoc() const LLVM_READONLY

getExprLoc - Return the preferred location for the arrow when diagnosing a problem with a generic exp...

Represents a function declaration or definition.

ArrayRef< ParmVarDecl * > parameters() const

FunctionDecl * getCanonicalDecl() override

Retrieves the "canonical" declaration of the given declaration.

Represents a prvalue temporary that is written into memory so that a reference can bind to it.

Expr * getSubExpr() const

Retrieve the temporary-generating subexpression whose value will be materialized into a glvalue.

ValueDecl * getExtendingDecl()

Get the declaration which triggered the lifetime-extension of this temporary, if any.

This represents a decl that may have a name.

StringRef getName() const

Get the name of identifier for this declaration as a StringRef.

Represents a parameter to a function.

Implements a set of CFGBlocks using a BitVector.

A (possibly-)qualified type.

bool isConstQualified() const

Determine whether this type is const-qualified.

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

Scope - A scope is a transient data structure that is used while parsing the program.

Encodes a location in the source.

bool isValid() const

Return true if this is a valid SourceLocation object.

Stmt - This represents one statement.

SourceLocation getEndLoc() const LLVM_READONLY

void dump() const

Dumps the specified AST fragment and all subtrees to llvm::errs().

bool isReferenceType() const

QualType getPointeeType() const

If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee.

bool isLValueReferenceType() const

const T * getAs() const

Member-template getAs<specific type>'.

UnaryOperator - This represents the unary-expression's (except sizeof and alignof),...

Expr * getSubExpr() const

Represent the declaration of a variable (in which case it is an lvalue) a function (in which case it ...

void checkBeforeAfter(const ValueDecl *Vd, const FactSet &FSet, ThreadSafetyAnalyzer &Analyzer, SourceLocation Loc, StringRef CapKind)

Return true if any mutexes in FSet are in the acquired_before set of Vd.

BeforeInfo * insertAttrExprs(const ValueDecl *Vd, ThreadSafetyAnalyzer &Analyzer)

Process acquired_before and acquired_after attributes on Vd.

BeforeInfo * getBeforeInfoForDecl(const ValueDecl *Vd, ThreadSafetyAnalyzer &Analyzer)

const PostOrderCFGView * getSortedGraph() const

const NamedDecl * getDecl() const

bool init(AnalysisDeclContext &AC)

const CFG * getGraph() const

bool shouldIgnore() const

bool equals(const CapabilityExpr &other) const

const til::SExpr * sexpr() const

std::string toString() const

const ValueDecl * valueDecl() const

StringRef getKind() const

Handler class for thread safety warnings.

virtual ~ThreadSafetyHandler()

virtual void handleInvalidLockExp(SourceLocation Loc)

Warn about lock expressions which fail to resolve to lockable objects.

virtual void enterFunction(const FunctionDecl *FD)

Called by the analysis when starting analysis of a function.

virtual void handleIncorrectUnlockKind(StringRef Kind, Name LockName, LockKind Expected, LockKind Received, SourceLocation LocLocked, SourceLocation LocUnlock)

Warn about an unlock function call that attempts to unlock a lock with the incorrect lock kind.

virtual void leaveFunction(const FunctionDecl *FD)

Called by the analysis when finishing analysis of a function.

virtual void handleExclusiveAndShared(StringRef Kind, Name LockName, SourceLocation Loc1, SourceLocation Loc2)

Warn when a mutex is held exclusively and shared at the same point.

virtual void handleMutexNotHeld(StringRef Kind, const NamedDecl *D, ProtectedOperationKind POK, Name LockName, LockKind LK, SourceLocation Loc, Name *PossibleMatch=nullptr)

Warn when a protected operation occurs while the specific mutex protecting the operation is not locke...

virtual void handleFunExcludesLock(StringRef Kind, Name FunName, Name LockName, SourceLocation Loc)

Warn when a function is called while an excluded mutex is locked.

virtual void handleNoMutexHeld(const NamedDecl *D, ProtectedOperationKind POK, AccessKind AK, SourceLocation Loc)

Warn when a protected operation occurs while no locks are held.

virtual void handleUnmatchedUnlock(StringRef Kind, Name LockName, SourceLocation Loc, SourceLocation LocPreviousUnlock)

Warn about unlock function calls that do not have a prior matching lock expression.

virtual void handleNegativeNotHeld(StringRef Kind, Name LockName, Name Neg, SourceLocation Loc)

Warn when acquiring a lock that the negative capability is not held.

virtual void handleMutexHeldEndOfScope(StringRef Kind, Name LockName, SourceLocation LocLocked, SourceLocation LocEndOfScope, LockErrorKind LEK)

Warn about situations where a mutex is sometimes held and sometimes not.

virtual void handleDoubleLock(StringRef Kind, Name LockName, SourceLocation LocLocked, SourceLocation LocDoubleLock)

Warn about lock function calls for locks which are already held.

A Literal pointer to an object allocated in memory.

Base class for AST nodes in the typed intermediate language.

internal::Matcher< T > traverse(TraversalKind TK, const internal::Matcher< T > &InnerMatcher)

Causes all nested matchers to be matched with the specified traversal kind.

unsigned kind

All of the diagnostics that can be emitted by the frontend.

@ CF

Indicates that the tracked object is a CF object.

bool Dec(InterpState &S, CodePtr OpPC)

1) Pops a pointer from the stack 2) Load the value from the pointer 3) Writes the value decreased by ...

bool Neg(InterpState &S, CodePtr OpPC)

bool matches(const til::SExpr *E1, const til::SExpr *E2)

LockKind getLockKindFromAccessKind(AccessKind AK)

Helper function that returns a LockKind required for the given level of access.

@ LEK_NotLockedAtEndOfFunction

@ LEK_LockedSomePredecessors

@ LEK_LockedAtEndOfFunction

@ LEK_LockedSomeLoopIterations

void threadSafetyCleanup(BeforeSet *Cache)

AccessKind

This enum distinguishes between different ways to access (read or write) a variable.

@ AK_Written

Writing a variable.

@ AK_Read

Reading a variable.

LockKind

This enum distinguishes between different kinds of lock actions.

@ LK_Shared

Shared/reader lock of a mutex.

@ LK_Exclusive

Exclusive/writer lock of a mutex.

@ LK_Generic

Can be either Shared or Exclusive.

void runThreadSafetyAnalysis(AnalysisDeclContext &AC, ThreadSafetyHandler &Handler, BeforeSet **Bset)

Check a function's CFG for thread-safety violations.

ProtectedOperationKind

This enum distinguishes between different kinds of operations that may need to be protected by locks.

@ POK_PtPassByRef

Passing a pt-guarded variable by reference.

@ POK_VarDereference

Dereferencing a variable (e.g. p in *p = 5;)

@ POK_PassByRef

Passing a guarded variable by reference.

@ POK_ReturnByRef

Returning a guarded variable by reference.

@ POK_VarAccess

Reading or writing a variable (e.g. x in x = 5;)

@ POK_FunctionCall

Making a function call (e.g. fool())

@ POK_PtReturnByRef

Returning a pt-guarded variable by reference.

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

OverloadedOperatorKind

Enumeration specifying the different kinds of C++ overloaded operators.

@ Self

'self' clause, allowed on Compute and Combined Constructs, plus 'update'.

@ Result

The result type of a method or function.

const FunctionProtoType * T

Iterator for iterating over Stmt * arrays that contain only T *.


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