@@ -45,9 +45,10 @@ template <typename Tag> struct ID {
45
45
bool operator ==(const ID<Tag> &Other) const { return Value == Other.Value ; }
46
46
bool operator !=(const ID<Tag> &Other) const { return !(*this == Other); }
47
47
bool operator <(const ID<Tag> &Other) const { return Value < Other.Value ; }
48
- ID<Tag> &operator ++() {
48
+ ID<Tag> operator ++(int ) {
49
+ ID<Tag> Tmp = *this ;
49
50
++Value;
50
- return * this ;
51
+ return Tmp ;
51
52
}
52
53
void Profile (llvm::FoldingSetNodeID &IDBuilder) const {
53
54
IDBuilder.AddInteger (Value);
@@ -59,11 +60,8 @@ inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, ID<Tag> ID) {
59
60
return OS << ID.Value ;
60
61
}
61
62
62
- struct LoanTag {};
63
- struct OriginTag {};
64
-
65
- using LoanID = ID<LoanTag>;
66
- using OriginID = ID<OriginTag>;
63
+ using LoanID = ID<struct LoanTag >;
64
+ using OriginID = ID<struct OriginTag >;
67
65
68
66
// / Information about a single borrow, or "Loan". A loan is created when a
69
67
// / reference or pointer is taken.
@@ -81,7 +79,12 @@ struct Loan {
81
79
82
80
// / An Origin is a symbolic identifier that represents the set of possible
83
81
// / loans a pointer-like object could hold at any given time.
84
- // / TODO: Also represent Origins of complex types (fields, inner types).
82
+ // / TODO: Enhance the origin model to handle complex types, pointer
83
+ // / indirection and reborrowing. The plan is to move from a single origin per
84
+ // / variable/expression to a "list of origins" governed by the Type.
85
+ // / For example, the type 'int**' would have two origins.
86
+ // / See discussion:
87
+ // / https://github.com/llvm/llvm-project/pull/142313/commits/0cd187b01e61b200d92ca0b640789c1586075142#r2137644238
85
88
struct Origin {
86
89
OriginID ID;
87
90
llvm::PointerUnion<const clang::ValueDecl *, const clang::Expr *> Ptr;
@@ -101,19 +104,20 @@ class LoanManager {
101
104
public:
102
105
LoanManager () = default ;
103
106
104
- Loan &addLoan (AccessPath path, SourceLocation loc) {
105
- ++NextLoanID;
106
- AllLoans.emplace_back (NextLoanID, path, loc);
107
+ Loan &addLoan (AccessPath Path, SourceLocation Loc) {
108
+ AllLoans.emplace_back (getNextLoanID (), Path, Loc);
107
109
return AllLoans.back ();
108
110
}
109
111
110
- const Loan &getLoan (LoanID id ) const {
111
- assert (id .Value < AllLoans.size ());
112
- return AllLoans[id .Value ];
112
+ const Loan &getLoan (LoanID ID ) const {
113
+ assert (ID .Value < AllLoans.size ());
114
+ return AllLoans[ID .Value ];
113
115
}
114
116
llvm::ArrayRef<Loan> getLoans () const { return AllLoans; }
115
117
116
118
private:
119
+ LoanID getNextLoanID () { return NextLoanID++; }
120
+
117
121
LoanID NextLoanID{0 };
118
122
// / TODO(opt): Profile and evaluate the usefullness of small buffer
119
123
// / optimisation.
@@ -124,23 +128,27 @@ class OriginManager {
124
128
public:
125
129
OriginManager () = default ;
126
130
127
- OriginID getNextOriginID () { return ++NextOriginID; }
128
- Origin &addOrigin (OriginID id, const clang::ValueDecl &D) {
129
- AllOrigins.emplace_back (id, &D);
131
+ Origin &addOrigin (OriginID ID, const clang::ValueDecl &D) {
132
+ AllOrigins.emplace_back (ID, &D);
130
133
return AllOrigins.back ();
131
134
}
132
- Origin &addOrigin (OriginID id , const clang::Expr &E) {
133
- AllOrigins.emplace_back (id , &E);
135
+ Origin &addOrigin (OriginID ID , const clang::Expr &E) {
136
+ AllOrigins.emplace_back (ID , &E);
134
137
return AllOrigins.back ();
135
138
}
136
139
137
140
OriginID get (const Expr &E) {
141
+ // Origin of DeclRefExpr is that of the declaration it refers to.
138
142
if (const auto *DRE = dyn_cast<DeclRefExpr>(&E)) {
139
- // Origin of DeclRefExpr is that of the declaration it refers to.
140
143
return get (*DRE->getDecl ());
141
144
}
142
145
auto It = ExprToOriginID.find (&E);
143
- assert (It != ExprToOriginID.end ());
146
+ // TODO: This should be an assert(It != ExprToOriginID.end()). The current
147
+ // implementation falls back to getOrCreate to avoid crashing on
148
+ // yet-unhandled pointer expressions, creating an empty origin for them.
149
+ if (It == ExprToOriginID.end ())
150
+ return getOrCreate (E);
151
+
144
152
return It->second ;
145
153
}
146
154
@@ -183,6 +191,8 @@ class OriginManager {
183
191
}
184
192
185
193
private:
194
+ OriginID getNextOriginID () { return NextOriginID++; }
195
+
186
196
OriginID NextOriginID{0 };
187
197
// / TODO(opt): Profile and evaluate the usefullness of small buffer
188
198
// / optimisation.
@@ -321,10 +331,7 @@ class FactManager {
321
331
llvm::dbgs () << " Function: " << ND->getQualifiedNameAsString () << " \n " ;
322
332
}
323
333
// Print blocks in the order as they appear in code for a stable ordering.
324
- ForwardDataflowWorklist worklist (Cfg, AC);
325
- for (const CFGBlock *B : Cfg.const_nodes ())
326
- worklist.enqueueBlock (B);
327
- while (const CFGBlock *B = worklist.dequeue ()) {
334
+ for (const CFGBlock *B : *AC.getAnalysis <PostOrderCFGView>()) {
328
335
llvm::dbgs () << " Block B" << B->getBlockID () << " :\n " ;
329
336
auto It = BlockToFactsMap.find (B);
330
337
if (It != BlockToFactsMap.end ()) {
@@ -351,19 +358,14 @@ class FactManager {
351
358
class FactGenerator : public ConstStmtVisitor <FactGenerator> {
352
359
353
360
public:
354
- FactGenerator (const CFG &Cfg, FactManager &FactMgr, AnalysisDeclContext &AC)
355
- : FactMgr(FactMgr), Cfg(Cfg), AC(AC) {}
361
+ FactGenerator (FactManager &FactMgr, AnalysisDeclContext &AC)
362
+ : FactMgr(FactMgr), AC(AC) {}
356
363
357
364
void run () {
358
365
llvm::TimeTraceScope TimeProfile (" FactGenerator" );
359
366
// Iterate through the CFG blocks in reverse post-order to ensure that
360
367
// initializations and destructions are processed in the correct sequence.
361
- // TODO: A reverse post-order traversal utility should be provided by
362
- // Dataflow framework.
363
- ForwardDataflowWorklist Worklist (Cfg, AC);
364
- for (const CFGBlock *B : Cfg.const_nodes ())
365
- Worklist.enqueueBlock (B);
366
- while (const CFGBlock *Block = Worklist.dequeue ()) {
368
+ for (const CFGBlock *Block : *AC.getAnalysis <PostOrderCFGView>()) {
367
369
CurrentBlockFacts.clear ();
368
370
for (unsigned I = 0 ; I < Block->size (); ++I) {
369
371
const CFGElement &Element = Block->Elements [I];
@@ -386,7 +388,7 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
386
388
}
387
389
388
390
void VisitCXXNullPtrLiteralExpr (const CXXNullPtrLiteralExpr *N) {
389
- // / TODO: Handle nullptr expr as a special 'null' loan. Uninintialed
391
+ // / TODO: Handle nullptr expr as a special 'null' loan. Uninitialized
390
392
// / pointers can use the same type of loan.
391
393
FactMgr.getOriginMgr ().getOrCreate (*N);
392
394
}
@@ -484,7 +486,6 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
484
486
}
485
487
486
488
FactManager &FactMgr;
487
- const CFG &Cfg;
488
489
AnalysisDeclContext &AC;
489
490
llvm::SmallVector<Fact *> CurrentBlockFacts;
490
491
};
@@ -537,6 +538,9 @@ struct LifetimeLattice {
537
538
538
539
// / Computes the union of two lattices by performing a key-wise join of
539
540
// / their OriginLoanMaps.
541
+ // TODO(opt): This key-wise join is a performance bottleneck. A more
542
+ // efficient merge could be implemented using a Patricia Trie or HAMT
543
+ // instead of the current AVL-tree-based ImmutableMap.
540
544
LifetimeLattice join (const LifetimeLattice &Other,
541
545
LifetimeFactory &Factory) const {
542
546
// / Merge the smaller map into the larger one ensuring we iterate over the
@@ -652,7 +656,8 @@ class Transferer {
652
656
// / Orchestrates the analysis by iterating over the CFG using a worklist
653
657
// / algorithm. It computes a fixed point by propagating the LifetimeLattice
654
658
// / state through each block until the state no longer changes.
655
- // / TODO: Maybe use the dataflow framework!
659
+ // / TODO: Maybe use the dataflow framework! The framework might need changes
660
+ // / to support the current comparison done at block-entry.
656
661
class LifetimeDataflow {
657
662
const CFG &Cfg;
658
663
AnalysisDeclContext &AC;
@@ -688,6 +693,9 @@ class LifetimeDataflow {
688
693
: LifetimeLattice{};
689
694
LifetimeLattice NewSuccEntryState =
690
695
OldSuccEntryState.join (ExitState, LifetimeFact);
696
+ // Enqueue the successor if its entry state has changed.
697
+ // TODO(opt): Consider changing 'join' to report a change if !=
698
+ // comparison is found expensive.
691
699
if (SuccIt == BlockEntryStates.end () ||
692
700
NewSuccEntryState != OldSuccEntryState) {
693
701
BlockEntryStates[Successor] = NewSuccEntryState;
@@ -733,7 +741,7 @@ void runLifetimeAnalysis(const DeclContext &DC, const CFG &Cfg,
733
741
DEBUG_WITH_TYPE (" PrintCFG" , Cfg.dump (AC.getASTContext ().getLangOpts (),
734
742
/* ShowColors=*/ true ));
735
743
FactManager FactMgr;
736
- FactGenerator FactGen (Cfg, FactMgr, AC);
744
+ FactGenerator FactGen (FactMgr, AC);
737
745
FactGen.run ();
738
746
DEBUG_WITH_TYPE (" LifetimeFacts" , FactMgr.dump (Cfg, AC));
739
747
0 commit comments