@@ -45,9 +45,10 @@ template <typename Tag> struct ID {
4545 bool operator ==(const ID<Tag> &Other) const { return Value == Other.Value ; }
4646 bool operator !=(const ID<Tag> &Other) const { return !(*this == Other); }
4747 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 ;
4950 ++Value;
50- return * this ;
51+ return Tmp ;
5152 }
5253 void Profile (llvm::FoldingSetNodeID &IDBuilder) const {
5354 IDBuilder.AddInteger (Value);
@@ -59,11 +60,8 @@ inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, ID<Tag> ID) {
5960 return OS << ID.Value ;
6061}
6162
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 >;
6765
6866// / Information about a single borrow, or "Loan". A loan is created when a
6967// / reference or pointer is taken.
@@ -81,7 +79,12 @@ struct Loan {
8179
8280// / An Origin is a symbolic identifier that represents the set of possible
8381// / 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
8588struct Origin {
8689 OriginID ID;
8790 llvm::PointerUnion<const clang::ValueDecl *, const clang::Expr *> Ptr;
@@ -101,19 +104,20 @@ class LoanManager {
101104public:
102105 LoanManager () = default ;
103106
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);
107109 return AllLoans.back ();
108110 }
109111
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 ];
113115 }
114116 llvm::ArrayRef<Loan> getLoans () const { return AllLoans; }
115117
116118private:
119+ LoanID getNextLoanID () { return NextLoanID++; }
120+
117121 LoanID NextLoanID{0 };
118122 // / TODO(opt): Profile and evaluate the usefullness of small buffer
119123 // / optimisation.
@@ -124,23 +128,27 @@ class OriginManager {
124128public:
125129 OriginManager () = default ;
126130
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);
130133 return AllOrigins.back ();
131134 }
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);
134137 return AllOrigins.back ();
135138 }
136139
137140 OriginID get (const Expr &E) {
141+ // Origin of DeclRefExpr is that of the declaration it refers to.
138142 if (const auto *DRE = dyn_cast<DeclRefExpr>(&E)) {
139- // Origin of DeclRefExpr is that of the declaration it refers to.
140143 return get (*DRE->getDecl ());
141144 }
142145 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+
144152 return It->second ;
145153 }
146154
@@ -183,6 +191,8 @@ class OriginManager {
183191 }
184192
185193private:
194+ OriginID getNextOriginID () { return NextOriginID++; }
195+
186196 OriginID NextOriginID{0 };
187197 // / TODO(opt): Profile and evaluate the usefullness of small buffer
188198 // / optimisation.
@@ -321,10 +331,7 @@ class FactManager {
321331 llvm::dbgs () << " Function: " << ND->getQualifiedNameAsString () << " \n " ;
322332 }
323333 // 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>()) {
328335 llvm::dbgs () << " Block B" << B->getBlockID () << " :\n " ;
329336 auto It = BlockToFactsMap.find (B);
330337 if (It != BlockToFactsMap.end ()) {
@@ -351,19 +358,14 @@ class FactManager {
351358class FactGenerator : public ConstStmtVisitor <FactGenerator> {
352359
353360public:
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) {}
356363
357364 void run () {
358365 llvm::TimeTraceScope TimeProfile (" FactGenerator" );
359366 // Iterate through the CFG blocks in reverse post-order to ensure that
360367 // 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>()) {
367369 CurrentBlockFacts.clear ();
368370 for (unsigned I = 0 ; I < Block->size (); ++I) {
369371 const CFGElement &Element = Block->Elements [I];
@@ -386,7 +388,7 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
386388 }
387389
388390 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
390392 // / pointers can use the same type of loan.
391393 FactMgr.getOriginMgr ().getOrCreate (*N);
392394 }
@@ -484,7 +486,6 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
484486 }
485487
486488 FactManager &FactMgr;
487- const CFG &Cfg;
488489 AnalysisDeclContext &AC;
489490 llvm::SmallVector<Fact *> CurrentBlockFacts;
490491};
@@ -537,6 +538,9 @@ struct LifetimeLattice {
537538
538539 // / Computes the union of two lattices by performing a key-wise join of
539540 // / 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.
540544 LifetimeLattice join (const LifetimeLattice &Other,
541545 LifetimeFactory &Factory) const {
542546 // / Merge the smaller map into the larger one ensuring we iterate over the
@@ -652,7 +656,8 @@ class Transferer {
652656// / Orchestrates the analysis by iterating over the CFG using a worklist
653657// / algorithm. It computes a fixed point by propagating the LifetimeLattice
654658// / 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.
656661class LifetimeDataflow {
657662 const CFG &Cfg;
658663 AnalysisDeclContext &AC;
@@ -688,6 +693,9 @@ class LifetimeDataflow {
688693 : LifetimeLattice{};
689694 LifetimeLattice NewSuccEntryState =
690695 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.
691699 if (SuccIt == BlockEntryStates.end () ||
692700 NewSuccEntryState != OldSuccEntryState) {
693701 BlockEntryStates[Successor] = NewSuccEntryState;
@@ -733,7 +741,7 @@ void runLifetimeAnalysis(const DeclContext &DC, const CFG &Cfg,
733741 DEBUG_WITH_TYPE (" PrintCFG" , Cfg.dump (AC.getASTContext ().getLangOpts (),
734742 /* ShowColors=*/ true ));
735743 FactManager FactMgr;
736- FactGenerator FactGen (Cfg, FactMgr, AC);
744+ FactGenerator FactGen (FactMgr, AC);
737745 FactGen.run ();
738746 DEBUG_WITH_TYPE (" LifetimeFacts" , FactMgr.dump (Cfg, AC));
739747
0 commit comments