forked from llvm-mirror/clang
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathThreadSafety.cpp
2404 lines (2075 loc) · 84.9 KB
/
ThreadSafety.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
//===- ThreadSafety.cpp ----------------------------------------*- C++ --*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// A intra-procedural analysis for thread safety (e.g. deadlocks and race
// conditions), based off of an annotation system.
//
// See http://clang.llvm.org/docs/ThreadSafetyAnalysis.html
// for more information.
//
//===----------------------------------------------------------------------===//
#include "clang/Analysis/Analyses/ThreadSafety.h"
#include "clang/AST/Attr.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/StmtCXX.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/Analysis/Analyses/PostOrderCFGView.h"
#include "clang/Analysis/Analyses/ThreadSafetyCommon.h"
#include "clang/Analysis/Analyses/ThreadSafetyLogical.h"
#include "clang/Analysis/Analyses/ThreadSafetyTIL.h"
#include "clang/Analysis/Analyses/ThreadSafetyTraverse.h"
#include "clang/Analysis/AnalysisContext.h"
#include "clang/Analysis/CFG.h"
#include "clang/Analysis/CFGStmtMap.h"
#include "clang/Basic/OperatorKinds.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
#include "llvm/ADT/ImmutableMap.h"
#include "llvm/ADT/PostOrderIterator.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <ostream>
#include <sstream>
#include <utility>
#include <vector>
using namespace clang;
using namespace threadSafety;
// Key method definition
ThreadSafetyHandler::~ThreadSafetyHandler() {}
namespace {
class TILPrinter :
public til::PrettyPrinter<TILPrinter, llvm::raw_ostream> {};
/// Issue a warning about an invalid lock expression
static void warnInvalidLock(ThreadSafetyHandler &Handler,
const Expr *MutexExp, const NamedDecl *D,
const Expr *DeclExp, StringRef Kind) {
SourceLocation Loc;
if (DeclExp)
Loc = DeclExp->getExprLoc();
// FIXME: add a note about the attribute location in MutexExp or D
if (Loc.isValid())
Handler.handleInvalidLockExp(Kind, Loc);
}
/// \brief A set of CapabilityInfo objects, which are compiled from the
/// requires attributes on a function.
class CapExprSet : public SmallVector<CapabilityExpr, 4> {
public:
/// \brief Push M onto list, but discard duplicates.
void push_back_nodup(const CapabilityExpr &CapE) {
iterator It = std::find_if(begin(), end(),
[=](const CapabilityExpr &CapE2) {
return CapE.equals(CapE2);
});
if (It == end())
push_back(CapE);
}
};
class FactManager;
class FactSet;
/// \brief This is a helper class that stores a fact that is known at a
/// particular point in program execution. Currently, a fact is a capability,
/// along with additional information, such as where it was acquired, whether
/// it is exclusive or shared, etc.
///
/// FIXME: this analysis does not currently support either re-entrant
/// locking or lock "upgrading" and "downgrading" between exclusive and
/// shared.
class FactEntry : public CapabilityExpr {
private:
LockKind LKind; ///< exclusive or shared
SourceLocation AcquireLoc; ///< where it was acquired.
bool Asserted; ///< true if the lock was asserted
bool Declared; ///< true if the lock was declared
public:
FactEntry(const CapabilityExpr &CE, LockKind LK, SourceLocation Loc,
bool Asrt, bool Declrd = false)
: CapabilityExpr(CE), LKind(LK), AcquireLoc(Loc), Asserted(Asrt),
Declared(Declrd) {}
virtual ~FactEntry() {}
LockKind kind() const { return LKind; }
SourceLocation loc() const { return AcquireLoc; }
bool asserted() const { return Asserted; }
bool declared() const { return Declared; }
void setDeclared(bool D) { Declared = D; }
virtual void
handleRemovalFromIntersection(const FactSet &FSet, FactManager &FactMan,
SourceLocation JoinLoc, LockErrorKind LEK,
ThreadSafetyHandler &Handler) const = 0;
virtual void handleUnlock(FactSet &FSet, FactManager &FactMan,
const CapabilityExpr &Cp, SourceLocation UnlockLoc,
bool FullyRemove, ThreadSafetyHandler &Handler,
StringRef DiagKind) const = 0;
// Return true if LKind >= LK, where exclusive > shared
bool isAtLeast(LockKind LK) {
return (LKind == LK_Exclusive) || (LK == LK_Shared);
}
};
typedef unsigned short FactID;
/// \brief FactManager manages the memory for all facts that are created during
/// the analysis of a single routine.
class FactManager {
private:
std::vector<std::unique_ptr<FactEntry>> Facts;
public:
FactID newFact(std::unique_ptr<FactEntry> Entry) {
Facts.push_back(std::move(Entry));
return static_cast<unsigned short>(Facts.size() - 1);
}
const FactEntry &operator[](FactID F) const { return *Facts[F]; }
FactEntry &operator[](FactID F) { return *Facts[F]; }
};
/// \brief A FactSet is the set of facts that are known to be true at a
/// particular program point. FactSets must be small, because they are
/// frequently copied, and are thus implemented as a set of indices into a
/// table maintained by a FactManager. A typical FactSet only holds 1 or 2
/// locks, so we can get away with doing a linear search for lookup. Note
/// that a hashtable or map is inappropriate in this case, because lookups
/// may involve partial pattern matches, rather than exact matches.
class FactSet {
private:
typedef SmallVector<FactID, 4> FactVec;
FactVec FactIDs;
public:
typedef FactVec::iterator iterator;
typedef FactVec::const_iterator const_iterator;
iterator begin() { return FactIDs.begin(); }
const_iterator begin() const { return FactIDs.begin(); }
iterator end() { return FactIDs.end(); }
const_iterator end() const { return FactIDs.end(); }
bool isEmpty() const { return FactIDs.size() == 0; }
// Return true if the set contains only negative facts
bool isEmpty(FactManager &FactMan) const {
for (FactID FID : *this) {
if (!FactMan[FID].negative())
return false;
}
return true;
}
void addLockByID(FactID ID) { FactIDs.push_back(ID); }
FactID addLock(FactManager &FM, std::unique_ptr<FactEntry> Entry) {
FactID F = FM.newFact(std::move(Entry));
FactIDs.push_back(F);
return F;
}
bool removeLock(FactManager& FM, const CapabilityExpr &CapE) {
unsigned n = FactIDs.size();
if (n == 0)
return false;
for (unsigned i = 0; i < n-1; ++i) {
if (FM[FactIDs[i]].matches(CapE)) {
FactIDs[i] = FactIDs[n-1];
FactIDs.pop_back();
return true;
}
}
if (FM[FactIDs[n-1]].matches(CapE)) {
FactIDs.pop_back();
return true;
}
return false;
}
iterator findLockIter(FactManager &FM, const CapabilityExpr &CapE) {
return std::find_if(begin(), end(), [&](FactID ID) {
return FM[ID].matches(CapE);
});
}
FactEntry *findLock(FactManager &FM, const CapabilityExpr &CapE) const {
auto I = std::find_if(begin(), end(), [&](FactID ID) {
return FM[ID].matches(CapE);
});
return I != end() ? &FM[*I] : nullptr;
}
FactEntry *findLockUniv(FactManager &FM, const CapabilityExpr &CapE) const {
auto I = std::find_if(begin(), end(), [&](FactID ID) -> bool {
return FM[ID].matchesUniv(CapE);
});
return I != end() ? &FM[*I] : nullptr;
}
FactEntry *findPartialMatch(FactManager &FM,
const CapabilityExpr &CapE) const {
auto I = std::find_if(begin(), end(), [&](FactID ID) -> bool {
return FM[ID].partiallyMatches(CapE);
});
return I != end() ? &FM[*I] : nullptr;
}
bool containsMutexDecl(FactManager &FM, const ValueDecl* Vd) const {
auto I = std::find_if(begin(), end(), [&](FactID ID) -> bool {
return FM[ID].valueDecl() == Vd;
});
return I != end();
}
};
class ThreadSafetyAnalyzer;
} // namespace
namespace clang {
namespace threadSafety {
class BeforeSet {
private:
typedef SmallVector<const ValueDecl*, 4> BeforeVect;
struct BeforeInfo {
BeforeInfo() : Visited(0) {}
BeforeInfo(BeforeInfo &&) = default;
BeforeVect Vect;
int Visited;
};
typedef llvm::DenseMap<const ValueDecl *, std::unique_ptr<BeforeInfo>>
BeforeMap;
typedef llvm::DenseMap<const ValueDecl*, bool> CycleMap;
public:
BeforeSet() { }
BeforeInfo* insertAttrExprs(const ValueDecl* Vd,
ThreadSafetyAnalyzer& Analyzer);
BeforeInfo *getBeforeInfoForDecl(const ValueDecl *Vd,
ThreadSafetyAnalyzer &Analyzer);
void checkBeforeAfter(const ValueDecl* Vd,
const FactSet& FSet,
ThreadSafetyAnalyzer& Analyzer,
SourceLocation Loc, StringRef CapKind);
private:
BeforeMap BMap;
CycleMap CycMap;
};
} // end namespace threadSafety
} // end namespace clang
namespace {
typedef llvm::ImmutableMap<const NamedDecl*, unsigned> LocalVarContext;
class LocalVariableMap;
/// A side (entry or exit) of a CFG node.
enum CFGBlockSide { CBS_Entry, CBS_Exit };
/// CFGBlockInfo is a struct which contains all the information that is
/// maintained for each block in the CFG. See LocalVariableMap for more
/// information about the contexts.
struct CFGBlockInfo {
FactSet EntrySet; // Lockset held at entry to block
FactSet ExitSet; // Lockset held at exit from block
LocalVarContext EntryContext; // Context held at entry to block
LocalVarContext ExitContext; // Context held at exit from block
SourceLocation EntryLoc; // Location of first statement in block
SourceLocation ExitLoc; // Location of last statement in block.
unsigned EntryIndex; // Used to replay contexts later
bool Reachable; // Is this block reachable?
const FactSet &getSet(CFGBlockSide Side) const {
return Side == CBS_Entry ? EntrySet : ExitSet;
}
SourceLocation getLocation(CFGBlockSide Side) const {
return Side == CBS_Entry ? EntryLoc : ExitLoc;
}
private:
CFGBlockInfo(LocalVarContext EmptyCtx)
: EntryContext(EmptyCtx), ExitContext(EmptyCtx), Reachable(false)
{ }
public:
static CFGBlockInfo getEmptyBlockInfo(LocalVariableMap &M);
};
// A LocalVariableMap maintains a map from local variables to their currently
// valid definitions. It provides SSA-like functionality when traversing the
// CFG. Like SSA, each definition or assignment to a variable is assigned a
// unique name (an integer), which acts as the SSA name for that definition.
// The total set of names is shared among all CFG basic blocks.
// Unlike SSA, we do not rewrite expressions to replace local variables declrefs
// with their SSA-names. Instead, we compute a Context for each point in the
// code, which maps local variables to the appropriate SSA-name. This map
// changes with each assignment.
//
// The map is computed in a single pass over the CFG. Subsequent analyses can
// then query the map to find the appropriate Context for a statement, and use
// that Context to look up the definitions of variables.
class LocalVariableMap {
public:
typedef LocalVarContext Context;
/// A VarDefinition consists of an expression, representing the value of the
/// variable, along with the context in which that expression should be
/// interpreted. A reference VarDefinition does not itself contain this
/// information, but instead contains a pointer to a previous VarDefinition.
struct VarDefinition {
public:
friend class LocalVariableMap;
const NamedDecl *Dec; // The original declaration for this variable.
const Expr *Exp; // The expression for this variable, OR
unsigned Ref; // Reference to another VarDefinition
Context Ctx; // The map with which Exp should be interpreted.
bool isReference() { return !Exp; }
private:
// Create ordinary variable definition
VarDefinition(const NamedDecl *D, const Expr *E, Context C)
: Dec(D), Exp(E), Ref(0), Ctx(C)
{ }
// Create reference to previous definition
VarDefinition(const NamedDecl *D, unsigned R, Context C)
: Dec(D), Exp(nullptr), Ref(R), Ctx(C)
{ }
};
private:
Context::Factory ContextFactory;
std::vector<VarDefinition> VarDefinitions;
std::vector<unsigned> CtxIndices;
std::vector<std::pair<Stmt*, Context> > SavedContexts;
public:
LocalVariableMap() {
// index 0 is a placeholder for undefined variables (aka phi-nodes).
VarDefinitions.push_back(VarDefinition(nullptr, 0u, getEmptyContext()));
}
/// Look up a definition, within the given context.
const VarDefinition* lookup(const NamedDecl *D, Context Ctx) {
const unsigned *i = Ctx.lookup(D);
if (!i)
return nullptr;
assert(*i < VarDefinitions.size());
return &VarDefinitions[*i];
}
/// Look up the definition for D within the given context. Returns
/// NULL if the expression is not statically known. If successful, also
/// modifies Ctx to hold the context of the return Expr.
const Expr* lookupExpr(const NamedDecl *D, Context &Ctx) {
const unsigned *P = Ctx.lookup(D);
if (!P)
return nullptr;
unsigned i = *P;
while (i > 0) {
if (VarDefinitions[i].Exp) {
Ctx = VarDefinitions[i].Ctx;
return VarDefinitions[i].Exp;
}
i = VarDefinitions[i].Ref;
}
return nullptr;
}
Context getEmptyContext() { return ContextFactory.getEmptyMap(); }
/// Return the next context after processing S. This function is used by
/// clients of the class to get the appropriate context when traversing the
/// CFG. It must be called for every assignment or DeclStmt.
Context getNextContext(unsigned &CtxIndex, Stmt *S, Context C) {
if (SavedContexts[CtxIndex+1].first == S) {
CtxIndex++;
Context Result = SavedContexts[CtxIndex].second;
return Result;
}
return C;
}
void dumpVarDefinitionName(unsigned i) {
if (i == 0) {
llvm::errs() << "Undefined";
return;
}
const NamedDecl *Dec = VarDefinitions[i].Dec;
if (!Dec) {
llvm::errs() << "<<NULL>>";
return;
}
Dec->printName(llvm::errs());
llvm::errs() << "." << i << " " << ((const void*) Dec);
}
/// Dumps an ASCII representation of the variable map to llvm::errs()
void dump() {
for (unsigned i = 1, e = VarDefinitions.size(); i < e; ++i) {
const Expr *Exp = VarDefinitions[i].Exp;
unsigned Ref = VarDefinitions[i].Ref;
dumpVarDefinitionName(i);
llvm::errs() << " = ";
if (Exp) Exp->dump();
else {
dumpVarDefinitionName(Ref);
llvm::errs() << "\n";
}
}
}
/// Dumps an ASCII representation of a Context to llvm::errs()
void dumpContext(Context C) {
for (Context::iterator I = C.begin(), E = C.end(); I != E; ++I) {
const NamedDecl *D = I.getKey();
D->printName(llvm::errs());
const unsigned *i = C.lookup(D);
llvm::errs() << " -> ";
dumpVarDefinitionName(*i);
llvm::errs() << "\n";
}
}
/// Builds the variable map.
void traverseCFG(CFG *CFGraph, const PostOrderCFGView *SortedGraph,
std::vector<CFGBlockInfo> &BlockInfo);
protected:
// Get the current context index
unsigned getContextIndex() { return SavedContexts.size()-1; }
// Save the current context for later replay
void saveContext(Stmt *S, Context C) {
SavedContexts.push_back(std::make_pair(S,C));
}
// Adds a new definition to the given context, and returns a new context.
// This method should be called when declaring a new variable.
Context addDefinition(const NamedDecl *D, const Expr *Exp, Context Ctx) {
assert(!Ctx.contains(D));
unsigned newID = VarDefinitions.size();
Context NewCtx = ContextFactory.add(Ctx, D, newID);
VarDefinitions.push_back(VarDefinition(D, Exp, Ctx));
return NewCtx;
}
// Add a new reference to an existing definition.
Context addReference(const NamedDecl *D, unsigned i, Context Ctx) {
unsigned newID = VarDefinitions.size();
Context NewCtx = ContextFactory.add(Ctx, D, newID);
VarDefinitions.push_back(VarDefinition(D, i, Ctx));
return NewCtx;
}
// Updates a definition only if that definition is already in the map.
// This method should be called when assigning to an existing variable.
Context updateDefinition(const NamedDecl *D, Expr *Exp, Context Ctx) {
if (Ctx.contains(D)) {
unsigned newID = VarDefinitions.size();
Context NewCtx = ContextFactory.remove(Ctx, D);
NewCtx = ContextFactory.add(NewCtx, D, newID);
VarDefinitions.push_back(VarDefinition(D, Exp, Ctx));
return NewCtx;
}
return Ctx;
}
// Removes a definition from the context, but keeps the variable name
// as a valid variable. The index 0 is a placeholder for cleared definitions.
Context clearDefinition(const NamedDecl *D, Context Ctx) {
Context NewCtx = Ctx;
if (NewCtx.contains(D)) {
NewCtx = ContextFactory.remove(NewCtx, D);
NewCtx = ContextFactory.add(NewCtx, D, 0);
}
return NewCtx;
}
// Remove a definition entirely frmo the context.
Context removeDefinition(const NamedDecl *D, Context Ctx) {
Context NewCtx = Ctx;
if (NewCtx.contains(D)) {
NewCtx = ContextFactory.remove(NewCtx, D);
}
return NewCtx;
}
Context intersectContexts(Context C1, Context C2);
Context createReferenceContext(Context C);
void intersectBackEdge(Context C1, Context C2);
friend class VarMapBuilder;
};
// This has to be defined after LocalVariableMap.
CFGBlockInfo CFGBlockInfo::getEmptyBlockInfo(LocalVariableMap &M) {
return CFGBlockInfo(M.getEmptyContext());
}
/// Visitor which builds a LocalVariableMap
class VarMapBuilder : public StmtVisitor<VarMapBuilder> {
public:
LocalVariableMap* VMap;
LocalVariableMap::Context Ctx;
VarMapBuilder(LocalVariableMap *VM, LocalVariableMap::Context C)
: VMap(VM), Ctx(C) {}
void VisitDeclStmt(DeclStmt *S);
void VisitBinaryOperator(BinaryOperator *BO);
};
// Add new local variables to the variable map
void VarMapBuilder::VisitDeclStmt(DeclStmt *S) {
bool modifiedCtx = false;
DeclGroupRef DGrp = S->getDeclGroup();
for (const auto *D : DGrp) {
if (const auto *VD = dyn_cast_or_null<VarDecl>(D)) {
const Expr *E = VD->getInit();
// Add local variables with trivial type to the variable map
QualType T = VD->getType();
if (T.isTrivialType(VD->getASTContext())) {
Ctx = VMap->addDefinition(VD, E, Ctx);
modifiedCtx = true;
}
}
}
if (modifiedCtx)
VMap->saveContext(S, Ctx);
}
// Update local variable definitions in variable map
void VarMapBuilder::VisitBinaryOperator(BinaryOperator *BO) {
if (!BO->isAssignmentOp())
return;
Expr *LHSExp = BO->getLHS()->IgnoreParenCasts();
// Update the variable map and current context.
if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(LHSExp)) {
ValueDecl *VDec = DRE->getDecl();
if (Ctx.lookup(VDec)) {
if (BO->getOpcode() == BO_Assign)
Ctx = VMap->updateDefinition(VDec, BO->getRHS(), Ctx);
else
// FIXME -- handle compound assignment operators
Ctx = VMap->clearDefinition(VDec, Ctx);
VMap->saveContext(BO, Ctx);
}
}
}
// Computes the intersection of two contexts. The intersection is the
// set of variables which have the same definition in both contexts;
// variables with different definitions are discarded.
LocalVariableMap::Context
LocalVariableMap::intersectContexts(Context C1, Context C2) {
Context Result = C1;
for (const auto &P : C1) {
const NamedDecl *Dec = P.first;
const unsigned *i2 = C2.lookup(Dec);
if (!i2) // variable doesn't exist on second path
Result = removeDefinition(Dec, Result);
else if (*i2 != P.second) // variable exists, but has different definition
Result = clearDefinition(Dec, Result);
}
return Result;
}
// For every variable in C, create a new variable that refers to the
// definition in C. Return a new context that contains these new variables.
// (We use this for a naive implementation of SSA on loop back-edges.)
LocalVariableMap::Context LocalVariableMap::createReferenceContext(Context C) {
Context Result = getEmptyContext();
for (const auto &P : C)
Result = addReference(P.first, P.second, Result);
return Result;
}
// This routine also takes the intersection of C1 and C2, but it does so by
// altering the VarDefinitions. C1 must be the result of an earlier call to
// createReferenceContext.
void LocalVariableMap::intersectBackEdge(Context C1, Context C2) {
for (const auto &P : C1) {
unsigned i1 = P.second;
VarDefinition *VDef = &VarDefinitions[i1];
assert(VDef->isReference());
const unsigned *i2 = C2.lookup(P.first);
if (!i2 || (*i2 != i1))
VDef->Ref = 0; // Mark this variable as undefined
}
}
// Traverse the CFG in topological order, so all predecessors of a block
// (excluding back-edges) are visited before the block itself. At
// each point in the code, we calculate a Context, which holds the set of
// variable definitions which are visible at that point in execution.
// Visible variables are mapped to their definitions using an array that
// contains all definitions.
//
// At join points in the CFG, the set is computed as the intersection of
// the incoming sets along each edge, E.g.
//
// { Context | VarDefinitions }
// int x = 0; { x -> x1 | x1 = 0 }
// int y = 0; { x -> x1, y -> y1 | y1 = 0, x1 = 0 }
// if (b) x = 1; { x -> x2, y -> y1 | x2 = 1, y1 = 0, ... }
// else x = 2; { x -> x3, y -> y1 | x3 = 2, x2 = 1, ... }
// ... { y -> y1 (x is unknown) | x3 = 2, x2 = 1, ... }
//
// This is essentially a simpler and more naive version of the standard SSA
// algorithm. Those definitions that remain in the intersection are from blocks
// that strictly dominate the current block. We do not bother to insert proper
// phi nodes, because they are not used in our analysis; instead, wherever
// a phi node would be required, we simply remove that definition from the
// context (E.g. x above).
//
// The initial traversal does not capture back-edges, so those need to be
// handled on a separate pass. Whenever the first pass encounters an
// incoming back edge, it duplicates the context, creating new definitions
// that refer back to the originals. (These correspond to places where SSA
// might have to insert a phi node.) On the second pass, these definitions are
// set to NULL if the variable has changed on the back-edge (i.e. a phi
// node was actually required.) E.g.
//
// { Context | VarDefinitions }
// int x = 0, y = 0; { x -> x1, y -> y1 | y1 = 0, x1 = 0 }
// while (b) { x -> x2, y -> y1 | [1st:] x2=x1; [2nd:] x2=NULL; }
// x = x+1; { x -> x3, y -> y1 | x3 = x2 + 1, ... }
// ... { y -> y1 | x3 = 2, x2 = 1, ... }
//
void LocalVariableMap::traverseCFG(CFG *CFGraph,
const PostOrderCFGView *SortedGraph,
std::vector<CFGBlockInfo> &BlockInfo) {
PostOrderCFGView::CFGBlockSet VisitedBlocks(CFGraph);
CtxIndices.resize(CFGraph->getNumBlockIDs());
for (const auto *CurrBlock : *SortedGraph) {
int CurrBlockID = CurrBlock->getBlockID();
CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlockID];
VisitedBlocks.insert(CurrBlock);
// Calculate the entry context for the current block
bool HasBackEdges = false;
bool CtxInit = true;
for (CFGBlock::const_pred_iterator PI = CurrBlock->pred_begin(),
PE = CurrBlock->pred_end(); PI != PE; ++PI) {
// if *PI -> CurrBlock is a back edge, so skip it
if (*PI == nullptr || !VisitedBlocks.alreadySet(*PI)) {
HasBackEdges = true;
continue;
}
int PrevBlockID = (*PI)->getBlockID();
CFGBlockInfo *PrevBlockInfo = &BlockInfo[PrevBlockID];
if (CtxInit) {
CurrBlockInfo->EntryContext = PrevBlockInfo->ExitContext;
CtxInit = false;
}
else {
CurrBlockInfo->EntryContext =
intersectContexts(CurrBlockInfo->EntryContext,
PrevBlockInfo->ExitContext);
}
}
// Duplicate the context if we have back-edges, so we can call
// intersectBackEdges later.
if (HasBackEdges)
CurrBlockInfo->EntryContext =
createReferenceContext(CurrBlockInfo->EntryContext);
// Create a starting context index for the current block
saveContext(nullptr, CurrBlockInfo->EntryContext);
CurrBlockInfo->EntryIndex = getContextIndex();
// Visit all the statements in the basic block.
VarMapBuilder VMapBuilder(this, CurrBlockInfo->EntryContext);
for (CFGBlock::const_iterator BI = CurrBlock->begin(),
BE = CurrBlock->end(); BI != BE; ++BI) {
switch (BI->getKind()) {
case CFGElement::Statement: {
CFGStmt CS = BI->castAs<CFGStmt>();
VMapBuilder.Visit(const_cast<Stmt*>(CS.getStmt()));
break;
}
default:
break;
}
}
CurrBlockInfo->ExitContext = VMapBuilder.Ctx;
// Mark variables on back edges as "unknown" if they've been changed.
for (CFGBlock::const_succ_iterator SI = CurrBlock->succ_begin(),
SE = CurrBlock->succ_end(); SI != SE; ++SI) {
// if CurrBlock -> *SI is *not* a back edge
if (*SI == nullptr || !VisitedBlocks.alreadySet(*SI))
continue;
CFGBlock *FirstLoopBlock = *SI;
Context LoopBegin = BlockInfo[FirstLoopBlock->getBlockID()].EntryContext;
Context LoopEnd = CurrBlockInfo->ExitContext;
intersectBackEdge(LoopBegin, LoopEnd);
}
}
// Put an extra entry at the end of the indexed context array
unsigned exitID = CFGraph->getExit().getBlockID();
saveContext(nullptr, BlockInfo[exitID].ExitContext);
}
/// Find the appropriate source locations to use when producing diagnostics for
/// each block in the CFG.
static void findBlockLocations(CFG *CFGraph,
const PostOrderCFGView *SortedGraph,
std::vector<CFGBlockInfo> &BlockInfo) {
for (const auto *CurrBlock : *SortedGraph) {
CFGBlockInfo *CurrBlockInfo = &BlockInfo[CurrBlock->getBlockID()];
// Find the source location of the last statement in the block, if the
// block is not empty.
if (const Stmt *S = CurrBlock->getTerminator()) {
CurrBlockInfo->EntryLoc = CurrBlockInfo->ExitLoc = S->getLocStart();
} else {
for (CFGBlock::const_reverse_iterator BI = CurrBlock->rbegin(),
BE = CurrBlock->rend(); BI != BE; ++BI) {
// FIXME: Handle other CFGElement kinds.
if (Optional<CFGStmt> CS = BI->getAs<CFGStmt>()) {
CurrBlockInfo->ExitLoc = CS->getStmt()->getLocStart();
break;
}
}
}
if (CurrBlockInfo->ExitLoc.isValid()) {
// This block contains at least one statement. Find the source location
// of the first statement in the block.
for (CFGBlock::const_iterator BI = CurrBlock->begin(),
BE = CurrBlock->end(); BI != BE; ++BI) {
// FIXME: Handle other CFGElement kinds.
if (Optional<CFGStmt> CS = BI->getAs<CFGStmt>()) {
CurrBlockInfo->EntryLoc = CS->getStmt()->getLocStart();
break;
}
}
} else if (CurrBlock->pred_size() == 1 && *CurrBlock->pred_begin() &&
CurrBlock != &CFGraph->getExit()) {
// The block is empty, and has a single predecessor. Use its exit
// location.
CurrBlockInfo->EntryLoc = CurrBlockInfo->ExitLoc =
BlockInfo[(*CurrBlock->pred_begin())->getBlockID()].ExitLoc;
}
}
}
class LockableFactEntry : public FactEntry {
private:
bool Managed; ///< managed by ScopedLockable object
public:
LockableFactEntry(const CapabilityExpr &CE, LockKind LK, SourceLocation Loc,
bool Mng = false, bool Asrt = false)
: FactEntry(CE, LK, Loc, Asrt), Managed(Mng) {}
void
handleRemovalFromIntersection(const FactSet &FSet, FactManager &FactMan,
SourceLocation JoinLoc, LockErrorKind LEK,
ThreadSafetyHandler &Handler) const override {
if (!Managed && !asserted() && !negative() && !isUniversal()) {
Handler.handleMutexHeldEndOfScope("mutex", toString(), loc(), JoinLoc,
LEK);
}
}
void handleUnlock(FactSet &FSet, FactManager &FactMan,
const CapabilityExpr &Cp, SourceLocation UnlockLoc,
bool FullyRemove, ThreadSafetyHandler &Handler,
StringRef DiagKind) const override {
FSet.removeLock(FactMan, Cp);
if (!Cp.negative()) {
FSet.addLock(FactMan, llvm::make_unique<LockableFactEntry>(
!Cp, LK_Exclusive, UnlockLoc));
}
}
};
class ScopedLockableFactEntry : public FactEntry {
private:
SmallVector<const til::SExpr *, 4> UnderlyingMutexes;
public:
ScopedLockableFactEntry(const CapabilityExpr &CE, SourceLocation Loc,
const CapExprSet &Excl, const CapExprSet &Shrd)
: FactEntry(CE, LK_Exclusive, Loc, false) {
for (const auto &M : Excl)
UnderlyingMutexes.push_back(M.sexpr());
for (const auto &M : Shrd)
UnderlyingMutexes.push_back(M.sexpr());
}
void
handleRemovalFromIntersection(const FactSet &FSet, FactManager &FactMan,
SourceLocation JoinLoc, LockErrorKind LEK,
ThreadSafetyHandler &Handler) const override {
for (const til::SExpr *UnderlyingMutex : UnderlyingMutexes) {
if (FSet.findLock(FactMan, CapabilityExpr(UnderlyingMutex, false))) {
// If this scoped lock manages another mutex, and if the underlying
// mutex is still held, then warn about the underlying mutex.
Handler.handleMutexHeldEndOfScope(
"mutex", sx::toString(UnderlyingMutex), loc(), JoinLoc, LEK);
}
}
}
void handleUnlock(FactSet &FSet, FactManager &FactMan,
const CapabilityExpr &Cp, SourceLocation UnlockLoc,
bool FullyRemove, ThreadSafetyHandler &Handler,
StringRef DiagKind) const override {
assert(!Cp.negative() && "Managing object cannot be negative.");
for (const til::SExpr *UnderlyingMutex : UnderlyingMutexes) {
CapabilityExpr UnderCp(UnderlyingMutex, false);
auto UnderEntry = llvm::make_unique<LockableFactEntry>(
!UnderCp, LK_Exclusive, UnlockLoc);
if (FullyRemove) {
// We're destroying the managing object.
// Remove the underlying mutex if it exists; but don't warn.
if (FSet.findLock(FactMan, UnderCp)) {
FSet.removeLock(FactMan, UnderCp);
FSet.addLock(FactMan, std::move(UnderEntry));
}
} else {
// We're releasing the underlying mutex, but not destroying the
// managing object. Warn on dual release.
if (!FSet.findLock(FactMan, UnderCp)) {
Handler.handleUnmatchedUnlock(DiagKind, UnderCp.toString(),
UnlockLoc);
}
FSet.removeLock(FactMan, UnderCp);
FSet.addLock(FactMan, std::move(UnderEntry));
}
}
if (FullyRemove)
FSet.removeLock(FactMan, Cp);
}
};
/// \brief Class which implements the core thread safety analysis routines.
class ThreadSafetyAnalyzer {
friend class BuildLockset;
friend class threadSafety::BeforeSet;
llvm::BumpPtrAllocator Bpa;
threadSafety::til::MemRegionRef Arena;
threadSafety::SExprBuilder SxBuilder;
ThreadSafetyHandler &Handler;
const CXXMethodDecl *CurrentMethod;
LocalVariableMap LocalVarMap;
FactManager FactMan;
std::vector<CFGBlockInfo> BlockInfo;
BeforeSet* GlobalBeforeSet;
public:
ThreadSafetyAnalyzer(ThreadSafetyHandler &H, BeforeSet* Bset)
: Arena(&Bpa), SxBuilder(Arena), Handler(H), GlobalBeforeSet(Bset) {}
bool inCurrentScope(const CapabilityExpr &CapE);
void addLock(FactSet &FSet, std::unique_ptr<FactEntry> Entry,
StringRef DiagKind, bool ReqAttr = false);
void removeLock(FactSet &FSet, const CapabilityExpr &CapE,
SourceLocation UnlockLoc, bool FullyRemove, LockKind Kind,
StringRef DiagKind);
template <typename AttrType>
void getMutexIDs(CapExprSet &Mtxs, AttrType *Attr, Expr *Exp,
const NamedDecl *D, VarDecl *SelfDecl = nullptr);
template <class AttrType>
void getMutexIDs(CapExprSet &Mtxs, AttrType *Attr, Expr *Exp,
const NamedDecl *D,
const CFGBlock *PredBlock, const CFGBlock *CurrBlock,
Expr *BrE, bool Neg);
const CallExpr* getTrylockCallExpr(const Stmt *Cond, LocalVarContext C,
bool &Negate);
void getEdgeLockset(FactSet &Result, const FactSet &ExitSet,
const CFGBlock* PredBlock,
const CFGBlock *CurrBlock);
void intersectAndWarn(FactSet &FSet1, const FactSet &FSet2,
SourceLocation JoinLoc,
LockErrorKind LEK1, LockErrorKind LEK2,
bool Modify=true);
void intersectAndWarn(FactSet &FSet1, const FactSet &FSet2,
SourceLocation JoinLoc, LockErrorKind LEK1,
bool Modify=true) {
intersectAndWarn(FSet1, FSet2, JoinLoc, LEK1, LEK1, Modify);
}
void runAnalysis(AnalysisDeclContext &AC);
};
} // namespace
/// Process acquired_before and acquired_after attributes on Vd.
BeforeSet::BeforeInfo* BeforeSet::insertAttrExprs(const ValueDecl* Vd,
ThreadSafetyAnalyzer& Analyzer) {
// Create a new entry for Vd.
BeforeInfo *Info = nullptr;
{
// Keep InfoPtr in its own scope in case BMap is modified later and the
// reference becomes invalid.
std::unique_ptr<BeforeInfo> &InfoPtr = BMap[Vd];
if (!InfoPtr)
InfoPtr.reset(new BeforeInfo());
Info = InfoPtr.get();
}
for (Attr* At : Vd->attrs()) {
switch (At->getKind()) {
case attr::AcquiredBefore: {
auto *A = cast<AcquiredBeforeAttr>(At);
// Read exprs from the attribute, and add them to BeforeVect.
for (const auto *Arg : A->args()) {
CapabilityExpr Cp =
Analyzer.SxBuilder.translateAttrExpr(Arg, nullptr);
if (const ValueDecl *Cpvd = Cp.valueDecl()) {
Info->Vect.push_back(Cpvd);
auto It = BMap.find(Cpvd);
if (It == BMap.end())
insertAttrExprs(Cpvd, Analyzer);
}
}
break;
}
case attr::AcquiredAfter: {
auto *A = cast<AcquiredAfterAttr>(At);
// Read exprs from the attribute, and add them to BeforeVect.
for (const auto *Arg : A->args()) {