xref: /freebsd/contrib/llvm-project/clang/lib/Analysis/LifetimeSafety.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
1 //===- LifetimeSafety.cpp - C++ Lifetime Safety Analysis -*--------- C++-*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 #include "clang/Analysis/Analyses/LifetimeSafety.h"
9 #include "clang/AST/Decl.h"
10 #include "clang/AST/Expr.h"
11 #include "clang/AST/StmtVisitor.h"
12 #include "clang/AST/Type.h"
13 #include "clang/Analysis/Analyses/PostOrderCFGView.h"
14 #include "clang/Analysis/AnalysisDeclContext.h"
15 #include "clang/Analysis/CFG.h"
16 #include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
17 #include "llvm/ADT/FoldingSet.h"
18 #include "llvm/ADT/ImmutableMap.h"
19 #include "llvm/ADT/ImmutableSet.h"
20 #include "llvm/ADT/PointerUnion.h"
21 #include "llvm/ADT/SmallVector.h"
22 #include "llvm/Support/Debug.h"
23 #include "llvm/Support/TimeProfiler.h"
24 #include <cstdint>
25 
26 namespace clang {
27 namespace {
28 
29 /// Represents the storage location being borrowed, e.g., a specific stack
30 /// variable.
31 /// TODO: Model access paths of other types, e.g., s.field, heap and globals.
32 struct AccessPath {
33   const clang::ValueDecl *D;
34 
AccessPathclang::__anon2e25c8880111::AccessPath35   AccessPath(const clang::ValueDecl *D) : D(D) {}
36 };
37 
38 /// A generic, type-safe wrapper for an ID, distinguished by its `Tag` type.
39 /// Used for giving ID to loans and origins.
40 template <typename Tag> struct ID {
41   uint32_t Value = 0;
42 
operator ==clang::__anon2e25c8880111::ID43   bool operator==(const ID<Tag> &Other) const { return Value == Other.Value; }
operator !=clang::__anon2e25c8880111::ID44   bool operator!=(const ID<Tag> &Other) const { return !(*this == Other); }
operator <clang::__anon2e25c8880111::ID45   bool operator<(const ID<Tag> &Other) const { return Value < Other.Value; }
operator ++clang::__anon2e25c8880111::ID46   ID<Tag> operator++(int) {
47     ID<Tag> Tmp = *this;
48     ++Value;
49     return Tmp;
50   }
Profileclang::__anon2e25c8880111::ID51   void Profile(llvm::FoldingSetNodeID &IDBuilder) const {
52     IDBuilder.AddInteger(Value);
53   }
54 };
55 
56 template <typename Tag>
operator <<(llvm::raw_ostream & OS,ID<Tag> ID)57 inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, ID<Tag> ID) {
58   return OS << ID.Value;
59 }
60 
61 using LoanID = ID<struct LoanTag>;
62 using OriginID = ID<struct OriginTag>;
63 
64 /// Information about a single borrow, or "Loan". A loan is created when a
65 /// reference or pointer is created.
66 struct Loan {
67   /// TODO: Represent opaque loans.
68   /// TODO: Represent nullptr: loans to no path. Accessing it UB! Currently it
69   /// is represented as empty LoanSet
70   LoanID ID;
71   AccessPath Path;
72   SourceLocation IssueLoc;
73 
Loanclang::__anon2e25c8880111::Loan74   Loan(LoanID id, AccessPath path, SourceLocation loc)
75       : ID(id), Path(path), IssueLoc(loc) {}
76 };
77 
78 /// An Origin is a symbolic identifier that represents the set of possible
79 /// loans a pointer-like object could hold at any given time.
80 /// TODO: Enhance the origin model to handle complex types, pointer
81 /// indirection and reborrowing. The plan is to move from a single origin per
82 /// variable/expression to a "list of origins" governed by the Type.
83 /// For example, the type 'int**' would have two origins.
84 /// See discussion:
85 /// https://github.com/llvm/llvm-project/pull/142313/commits/0cd187b01e61b200d92ca0b640789c1586075142#r2137644238
86 struct Origin {
87   OriginID ID;
88   /// A pointer to the AST node that this origin represents. This union
89   /// distinguishes between origins from declarations (variables or parameters)
90   /// and origins from expressions.
91   llvm::PointerUnion<const clang::ValueDecl *, const clang::Expr *> Ptr;
92 
Originclang::__anon2e25c8880111::Origin93   Origin(OriginID ID, const clang::ValueDecl *D) : ID(ID), Ptr(D) {}
Originclang::__anon2e25c8880111::Origin94   Origin(OriginID ID, const clang::Expr *E) : ID(ID), Ptr(E) {}
95 
getDeclclang::__anon2e25c8880111::Origin96   const clang::ValueDecl *getDecl() const {
97     return Ptr.dyn_cast<const clang::ValueDecl *>();
98   }
getExprclang::__anon2e25c8880111::Origin99   const clang::Expr *getExpr() const {
100     return Ptr.dyn_cast<const clang::Expr *>();
101   }
102 };
103 
104 /// Manages the creation, storage and retrieval of loans.
105 class LoanManager {
106 public:
107   LoanManager() = default;
108 
addLoan(AccessPath Path,SourceLocation Loc)109   Loan &addLoan(AccessPath Path, SourceLocation Loc) {
110     AllLoans.emplace_back(getNextLoanID(), Path, Loc);
111     return AllLoans.back();
112   }
113 
getLoan(LoanID ID) const114   const Loan &getLoan(LoanID ID) const {
115     assert(ID.Value < AllLoans.size());
116     return AllLoans[ID.Value];
117   }
getLoans() const118   llvm::ArrayRef<Loan> getLoans() const { return AllLoans; }
119 
120 private:
getNextLoanID()121   LoanID getNextLoanID() { return NextLoanID++; }
122 
123   LoanID NextLoanID{0};
124   /// TODO(opt): Profile and evaluate the usefullness of small buffer
125   /// optimisation.
126   llvm::SmallVector<Loan> AllLoans;
127 };
128 
129 /// Manages the creation, storage, and retrieval of origins for pointer-like
130 /// variables and expressions.
131 class OriginManager {
132 public:
133   OriginManager() = default;
134 
addOrigin(OriginID ID,const clang::ValueDecl & D)135   Origin &addOrigin(OriginID ID, const clang::ValueDecl &D) {
136     AllOrigins.emplace_back(ID, &D);
137     return AllOrigins.back();
138   }
addOrigin(OriginID ID,const clang::Expr & E)139   Origin &addOrigin(OriginID ID, const clang::Expr &E) {
140     AllOrigins.emplace_back(ID, &E);
141     return AllOrigins.back();
142   }
143 
get(const Expr & E)144   OriginID get(const Expr &E) {
145     // Origin of DeclRefExpr is that of the declaration it refers to.
146     if (const auto *DRE = dyn_cast<DeclRefExpr>(&E))
147       return get(*DRE->getDecl());
148     auto It = ExprToOriginID.find(&E);
149     // TODO: This should be an assert(It != ExprToOriginID.end()). The current
150     // implementation falls back to getOrCreate to avoid crashing on
151     // yet-unhandled pointer expressions, creating an empty origin for them.
152     if (It == ExprToOriginID.end())
153       return getOrCreate(E);
154 
155     return It->second;
156   }
157 
get(const ValueDecl & D)158   OriginID get(const ValueDecl &D) {
159     auto It = DeclToOriginID.find(&D);
160     // TODO: This should be an assert(It != DeclToOriginID.end()). The current
161     // implementation falls back to getOrCreate to avoid crashing on
162     // yet-unhandled pointer expressions, creating an empty origin for them.
163     if (It == DeclToOriginID.end())
164       return getOrCreate(D);
165 
166     return It->second;
167   }
168 
getOrCreate(const Expr & E)169   OriginID getOrCreate(const Expr &E) {
170     auto It = ExprToOriginID.find(&E);
171     if (It != ExprToOriginID.end())
172       return It->second;
173 
174     if (const auto *DRE = dyn_cast<DeclRefExpr>(&E)) {
175       // Origin of DeclRefExpr is that of the declaration it refers to.
176       return getOrCreate(*DRE->getDecl());
177     }
178     OriginID NewID = getNextOriginID();
179     addOrigin(NewID, E);
180     ExprToOriginID[&E] = NewID;
181     return NewID;
182   }
183 
getOrigin(OriginID ID) const184   const Origin &getOrigin(OriginID ID) const {
185     assert(ID.Value < AllOrigins.size());
186     return AllOrigins[ID.Value];
187   }
188 
getOrigins() const189   llvm::ArrayRef<Origin> getOrigins() const { return AllOrigins; }
190 
getOrCreate(const ValueDecl & D)191   OriginID getOrCreate(const ValueDecl &D) {
192     auto It = DeclToOriginID.find(&D);
193     if (It != DeclToOriginID.end())
194       return It->second;
195     OriginID NewID = getNextOriginID();
196     addOrigin(NewID, D);
197     DeclToOriginID[&D] = NewID;
198     return NewID;
199   }
200 
201 private:
getNextOriginID()202   OriginID getNextOriginID() { return NextOriginID++; }
203 
204   OriginID NextOriginID{0};
205   /// TODO(opt): Profile and evaluate the usefullness of small buffer
206   /// optimisation.
207   llvm::SmallVector<Origin> AllOrigins;
208   llvm::DenseMap<const clang::ValueDecl *, OriginID> DeclToOriginID;
209   llvm::DenseMap<const clang::Expr *, OriginID> ExprToOriginID;
210 };
211 
212 /// An abstract base class for a single, atomic lifetime-relevant event.
213 class Fact {
214 
215 public:
216   enum class Kind : uint8_t {
217     /// A new loan is issued from a borrow expression (e.g., &x).
218     Issue,
219     /// A loan expires as its underlying storage is freed (e.g., variable goes
220     /// out of scope).
221     Expire,
222     /// An origin is propagated from a source to a destination (e.g., p = q).
223     AssignOrigin,
224     /// An origin escapes the function by flowing into the return value.
225     ReturnOfOrigin
226   };
227 
228 private:
229   Kind K;
230 
231 protected:
Fact(Kind K)232   Fact(Kind K) : K(K) {}
233 
234 public:
235   virtual ~Fact() = default;
getKind() const236   Kind getKind() const { return K; }
237 
getAs() const238   template <typename T> const T *getAs() const {
239     if (T::classof(this))
240       return static_cast<const T *>(this);
241     return nullptr;
242   }
243 
dump(llvm::raw_ostream & OS) const244   virtual void dump(llvm::raw_ostream &OS) const {
245     OS << "Fact (Kind: " << static_cast<int>(K) << ")\n";
246   }
247 };
248 
249 class IssueFact : public Fact {
250   LoanID LID;
251   OriginID OID;
252 
253 public:
classof(const Fact * F)254   static bool classof(const Fact *F) { return F->getKind() == Kind::Issue; }
255 
IssueFact(LoanID LID,OriginID OID)256   IssueFact(LoanID LID, OriginID OID) : Fact(Kind::Issue), LID(LID), OID(OID) {}
getLoanID() const257   LoanID getLoanID() const { return LID; }
getOriginID() const258   OriginID getOriginID() const { return OID; }
dump(llvm::raw_ostream & OS) const259   void dump(llvm::raw_ostream &OS) const override {
260     OS << "Issue (LoanID: " << getLoanID() << ", OriginID: " << getOriginID()
261        << ")\n";
262   }
263 };
264 
265 class ExpireFact : public Fact {
266   LoanID LID;
267 
268 public:
classof(const Fact * F)269   static bool classof(const Fact *F) { return F->getKind() == Kind::Expire; }
270 
ExpireFact(LoanID LID)271   ExpireFact(LoanID LID) : Fact(Kind::Expire), LID(LID) {}
getLoanID() const272   LoanID getLoanID() const { return LID; }
dump(llvm::raw_ostream & OS) const273   void dump(llvm::raw_ostream &OS) const override {
274     OS << "Expire (LoanID: " << getLoanID() << ")\n";
275   }
276 };
277 
278 class AssignOriginFact : public Fact {
279   OriginID OIDDest;
280   OriginID OIDSrc;
281 
282 public:
classof(const Fact * F)283   static bool classof(const Fact *F) {
284     return F->getKind() == Kind::AssignOrigin;
285   }
286 
AssignOriginFact(OriginID OIDDest,OriginID OIDSrc)287   AssignOriginFact(OriginID OIDDest, OriginID OIDSrc)
288       : Fact(Kind::AssignOrigin), OIDDest(OIDDest), OIDSrc(OIDSrc) {}
getDestOriginID() const289   OriginID getDestOriginID() const { return OIDDest; }
getSrcOriginID() const290   OriginID getSrcOriginID() const { return OIDSrc; }
dump(llvm::raw_ostream & OS) const291   void dump(llvm::raw_ostream &OS) const override {
292     OS << "AssignOrigin (DestID: " << getDestOriginID()
293        << ", SrcID: " << getSrcOriginID() << ")\n";
294   }
295 };
296 
297 class ReturnOfOriginFact : public Fact {
298   OriginID OID;
299 
300 public:
classof(const Fact * F)301   static bool classof(const Fact *F) {
302     return F->getKind() == Kind::ReturnOfOrigin;
303   }
304 
ReturnOfOriginFact(OriginID OID)305   ReturnOfOriginFact(OriginID OID) : Fact(Kind::ReturnOfOrigin), OID(OID) {}
getReturnedOriginID() const306   OriginID getReturnedOriginID() const { return OID; }
dump(llvm::raw_ostream & OS) const307   void dump(llvm::raw_ostream &OS) const override {
308     OS << "ReturnOfOrigin (OriginID: " << getReturnedOriginID() << ")\n";
309   }
310 };
311 
312 class FactManager {
313 public:
getFacts(const CFGBlock * B) const314   llvm::ArrayRef<const Fact *> getFacts(const CFGBlock *B) const {
315     auto It = BlockToFactsMap.find(B);
316     if (It != BlockToFactsMap.end())
317       return It->second;
318     return {};
319   }
320 
addBlockFacts(const CFGBlock * B,llvm::ArrayRef<Fact * > NewFacts)321   void addBlockFacts(const CFGBlock *B, llvm::ArrayRef<Fact *> NewFacts) {
322     if (!NewFacts.empty())
323       BlockToFactsMap[B].assign(NewFacts.begin(), NewFacts.end());
324   }
325 
326   template <typename FactType, typename... Args>
createFact(Args &&...args)327   FactType *createFact(Args &&...args) {
328     void *Mem = FactAllocator.Allocate<FactType>();
329     return new (Mem) FactType(std::forward<Args>(args)...);
330   }
331 
dump(const CFG & Cfg,AnalysisDeclContext & AC) const332   void dump(const CFG &Cfg, AnalysisDeclContext &AC) const {
333     llvm::dbgs() << "==========================================\n";
334     llvm::dbgs() << "       Lifetime Analysis Facts:\n";
335     llvm::dbgs() << "==========================================\n";
336     if (const Decl *D = AC.getDecl())
337       if (const auto *ND = dyn_cast<NamedDecl>(D))
338         llvm::dbgs() << "Function: " << ND->getQualifiedNameAsString() << "\n";
339     // Print blocks in the order as they appear in code for a stable ordering.
340     for (const CFGBlock *B : *AC.getAnalysis<PostOrderCFGView>()) {
341       llvm::dbgs() << "  Block B" << B->getBlockID() << ":\n";
342       auto It = BlockToFactsMap.find(B);
343       if (It != BlockToFactsMap.end()) {
344         for (const Fact *F : It->second) {
345           llvm::dbgs() << "    ";
346           F->dump(llvm::dbgs());
347         }
348       }
349       llvm::dbgs() << "  End of Block\n";
350     }
351   }
352 
getLoanMgr()353   LoanManager &getLoanMgr() { return LoanMgr; }
getOriginMgr()354   OriginManager &getOriginMgr() { return OriginMgr; }
355 
356 private:
357   LoanManager LoanMgr;
358   OriginManager OriginMgr;
359   llvm::DenseMap<const clang::CFGBlock *, llvm::SmallVector<const Fact *>>
360       BlockToFactsMap;
361   llvm::BumpPtrAllocator FactAllocator;
362 };
363 
364 class FactGenerator : public ConstStmtVisitor<FactGenerator> {
365 
366 public:
FactGenerator(FactManager & FactMgr,AnalysisDeclContext & AC)367   FactGenerator(FactManager &FactMgr, AnalysisDeclContext &AC)
368       : FactMgr(FactMgr), AC(AC) {}
369 
run()370   void run() {
371     llvm::TimeTraceScope TimeProfile("FactGenerator");
372     // Iterate through the CFG blocks in reverse post-order to ensure that
373     // initializations and destructions are processed in the correct sequence.
374     for (const CFGBlock *Block : *AC.getAnalysis<PostOrderCFGView>()) {
375       CurrentBlockFacts.clear();
376       for (unsigned I = 0; I < Block->size(); ++I) {
377         const CFGElement &Element = Block->Elements[I];
378         if (std::optional<CFGStmt> CS = Element.getAs<CFGStmt>())
379           Visit(CS->getStmt());
380         else if (std::optional<CFGAutomaticObjDtor> DtorOpt =
381                      Element.getAs<CFGAutomaticObjDtor>())
382           handleDestructor(*DtorOpt);
383       }
384       FactMgr.addBlockFacts(Block, CurrentBlockFacts);
385     }
386   }
387 
VisitDeclStmt(const DeclStmt * DS)388   void VisitDeclStmt(const DeclStmt *DS) {
389     for (const Decl *D : DS->decls())
390       if (const auto *VD = dyn_cast<VarDecl>(D))
391         if (hasOrigin(VD->getType()))
392           if (const Expr *InitExpr = VD->getInit())
393             addAssignOriginFact(*VD, *InitExpr);
394   }
395 
VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr * N)396   void VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *N) {
397     /// TODO: Handle nullptr expr as a special 'null' loan. Uninitialized
398     /// pointers can use the same type of loan.
399     FactMgr.getOriginMgr().getOrCreate(*N);
400   }
401 
VisitImplicitCastExpr(const ImplicitCastExpr * ICE)402   void VisitImplicitCastExpr(const ImplicitCastExpr *ICE) {
403     if (!hasOrigin(ICE->getType()))
404       return;
405     Visit(ICE->getSubExpr());
406     // An ImplicitCastExpr node itself gets an origin, which flows from the
407     // origin of its sub-expression (after stripping its own parens/casts).
408     // TODO: Consider if this is actually useful in practice. Alternatively, we
409     // could directly use the sub-expression's OriginID instead of creating a
410     // new one.
411     addAssignOriginFact(*ICE, *ICE->getSubExpr());
412   }
413 
VisitUnaryOperator(const UnaryOperator * UO)414   void VisitUnaryOperator(const UnaryOperator *UO) {
415     if (UO->getOpcode() == UO_AddrOf) {
416       const Expr *SubExpr = UO->getSubExpr();
417       if (const auto *DRE = dyn_cast<DeclRefExpr>(SubExpr)) {
418         if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
419           // Check if it's a local variable.
420           if (VD->hasLocalStorage()) {
421             OriginID OID = FactMgr.getOriginMgr().getOrCreate(*UO);
422             AccessPath AddrOfLocalVarPath(VD);
423             const Loan &L = FactMgr.getLoanMgr().addLoan(AddrOfLocalVarPath,
424                                                          UO->getOperatorLoc());
425             CurrentBlockFacts.push_back(
426                 FactMgr.createFact<IssueFact>(L.ID, OID));
427           }
428         }
429       }
430     }
431   }
432 
VisitReturnStmt(const ReturnStmt * RS)433   void VisitReturnStmt(const ReturnStmt *RS) {
434     if (const Expr *RetExpr = RS->getRetValue()) {
435       if (hasOrigin(RetExpr->getType())) {
436         OriginID OID = FactMgr.getOriginMgr().getOrCreate(*RetExpr);
437         CurrentBlockFacts.push_back(
438             FactMgr.createFact<ReturnOfOriginFact>(OID));
439       }
440     }
441   }
442 
VisitBinaryOperator(const BinaryOperator * BO)443   void VisitBinaryOperator(const BinaryOperator *BO) {
444     if (BO->isAssignmentOp()) {
445       const Expr *LHSExpr = BO->getLHS();
446       const Expr *RHSExpr = BO->getRHS();
447 
448       // We are interested in assignments like `ptr1 = ptr2` or `ptr = &var`
449       // LHS must be a pointer/reference type that can be an origin.
450       // RHS must also represent an origin (either another pointer/ref or an
451       // address-of).
452       if (const auto *DRE_LHS = dyn_cast<DeclRefExpr>(LHSExpr))
453         if (const auto *VD_LHS =
454                 dyn_cast<ValueDecl>(DRE_LHS->getDecl()->getCanonicalDecl());
455             VD_LHS && hasOrigin(VD_LHS->getType()))
456           addAssignOriginFact(*VD_LHS, *RHSExpr);
457     }
458   }
459 
460 private:
461   // Check if a type has an origin.
hasOrigin(QualType QT)462   bool hasOrigin(QualType QT) { return QT->isPointerOrReferenceType(); }
463 
464   template <typename Destination, typename Source>
addAssignOriginFact(const Destination & D,const Source & S)465   void addAssignOriginFact(const Destination &D, const Source &S) {
466     OriginID DestOID = FactMgr.getOriginMgr().getOrCreate(D);
467     OriginID SrcOID = FactMgr.getOriginMgr().get(S);
468     CurrentBlockFacts.push_back(
469         FactMgr.createFact<AssignOriginFact>(DestOID, SrcOID));
470   }
471 
handleDestructor(const CFGAutomaticObjDtor & DtorOpt)472   void handleDestructor(const CFGAutomaticObjDtor &DtorOpt) {
473     /// TODO: Also handle trivial destructors (e.g., for `int`
474     /// variables) which will never have a CFGAutomaticObjDtor node.
475     /// TODO: Handle loans to temporaries.
476     /// TODO: Consider using clang::CFG::BuildOptions::AddLifetime to reuse the
477     /// lifetime ends.
478     const VarDecl *DestructedVD = DtorOpt.getVarDecl();
479     if (!DestructedVD)
480       return;
481     // Iterate through all loans to see if any expire.
482     /// TODO(opt): Do better than a linear search to find loans associated with
483     /// 'DestructedVD'.
484     for (const Loan &L : FactMgr.getLoanMgr().getLoans()) {
485       const AccessPath &LoanPath = L.Path;
486       // Check if the loan is for a stack variable and if that variable
487       // is the one being destructed.
488       if (LoanPath.D == DestructedVD)
489         CurrentBlockFacts.push_back(FactMgr.createFact<ExpireFact>(L.ID));
490     }
491   }
492 
493   FactManager &FactMgr;
494   AnalysisDeclContext &AC;
495   llvm::SmallVector<Fact *> CurrentBlockFacts;
496 };
497 
498 // ========================================================================= //
499 //                              The Dataflow Lattice
500 // ========================================================================= //
501 
502 // Using LLVM's immutable collections is efficient for dataflow analysis
503 // as it avoids deep copies during state transitions.
504 // TODO(opt): Consider using a bitset to represent the set of loans.
505 using LoanSet = llvm::ImmutableSet<LoanID>;
506 using OriginLoanMap = llvm::ImmutableMap<OriginID, LoanSet>;
507 
508 /// An object to hold the factories for immutable collections, ensuring
509 /// that all created states share the same underlying memory management.
510 struct LifetimeFactory {
511   OriginLoanMap::Factory OriginMapFactory;
512   LoanSet::Factory LoanSetFact;
513 
514   /// Creates a singleton set containing only the given loan ID.
createLoanSetclang::__anon2e25c8880111::LifetimeFactory515   LoanSet createLoanSet(LoanID LID) {
516     return LoanSetFact.add(LoanSetFact.getEmptySet(), LID);
517   }
518 };
519 
520 /// LifetimeLattice represents the state of our analysis at a given program
521 /// point. It is an immutable object, and all operations produce a new
522 /// instance rather than modifying the existing one.
523 struct LifetimeLattice {
524   /// The map from an origin to the set of loans it contains.
525   /// The lattice has a finite height: An origin's loan set is bounded by the
526   /// total number of loans in the function.
527   /// TODO(opt): To reduce the lattice size, propagate origins of declarations,
528   /// not expressions, because expressions are not visible across blocks.
529   OriginLoanMap Origins = OriginLoanMap(nullptr);
530 
LifetimeLatticeclang::__anon2e25c8880111::LifetimeLattice531   explicit LifetimeLattice(const OriginLoanMap &S) : Origins(S) {}
532   LifetimeLattice() = default;
533 
operator ==clang::__anon2e25c8880111::LifetimeLattice534   bool operator==(const LifetimeLattice &Other) const {
535     return Origins == Other.Origins;
536   }
operator !=clang::__anon2e25c8880111::LifetimeLattice537   bool operator!=(const LifetimeLattice &Other) const {
538     return !(*this == Other);
539   }
540 
getLoansclang::__anon2e25c8880111::LifetimeLattice541   LoanSet getLoans(OriginID OID) const {
542     if (auto *Loans = Origins.lookup(OID))
543       return *Loans;
544     return LoanSet(nullptr);
545   }
546 
547   /// Computes the union of two lattices by performing a key-wise join of
548   /// their OriginLoanMaps.
549   // TODO(opt): This key-wise join is a performance bottleneck. A more
550   // efficient merge could be implemented using a Patricia Trie or HAMT
551   // instead of the current AVL-tree-based ImmutableMap.
552   // TODO(opt): Keep the state small by removing origins which become dead.
joinclang::__anon2e25c8880111::LifetimeLattice553   LifetimeLattice join(const LifetimeLattice &Other,
554                        LifetimeFactory &Factory) const {
555     /// Merge the smaller map into the larger one ensuring we iterate over the
556     /// smaller map.
557     if (Origins.getHeight() < Other.Origins.getHeight())
558       return Other.join(*this, Factory);
559 
560     OriginLoanMap JoinedState = Origins;
561     // For each origin in the other map, union its loan set with ours.
562     for (const auto &Entry : Other.Origins) {
563       OriginID OID = Entry.first;
564       LoanSet OtherLoanSet = Entry.second;
565       JoinedState = Factory.OriginMapFactory.add(
566           JoinedState, OID, join(getLoans(OID), OtherLoanSet, Factory));
567     }
568     return LifetimeLattice(JoinedState);
569   }
570 
joinclang::__anon2e25c8880111::LifetimeLattice571   LoanSet join(LoanSet a, LoanSet b, LifetimeFactory &Factory) const {
572     /// Merge the smaller set into the larger one ensuring we iterate over the
573     /// smaller set.
574     if (a.getHeight() < b.getHeight())
575       std::swap(a, b);
576     LoanSet Result = a;
577     for (LoanID LID : b) {
578       /// TODO(opt): Profiling shows that this loop is a major performance
579       /// bottleneck. Investigate using a BitVector to represent the set of
580       /// loans for improved join performance.
581       Result = Factory.LoanSetFact.add(Result, LID);
582     }
583     return Result;
584   }
585 
dumpclang::__anon2e25c8880111::LifetimeLattice586   void dump(llvm::raw_ostream &OS) const {
587     OS << "LifetimeLattice State:\n";
588     if (Origins.isEmpty())
589       OS << "  <empty>\n";
590     for (const auto &Entry : Origins) {
591       if (Entry.second.isEmpty())
592         OS << "  Origin " << Entry.first << " contains no loans\n";
593       for (const LoanID &LID : Entry.second)
594         OS << "  Origin " << Entry.first << " contains Loan " << LID << "\n";
595     }
596   }
597 };
598 
599 // ========================================================================= //
600 //                              The Transfer Function
601 // ========================================================================= //
602 class Transferer {
603   FactManager &AllFacts;
604   LifetimeFactory &Factory;
605 
606 public:
Transferer(FactManager & F,LifetimeFactory & Factory)607   explicit Transferer(FactManager &F, LifetimeFactory &Factory)
608       : AllFacts(F), Factory(Factory) {}
609 
610   /// Computes the exit state of a block by applying all its facts sequentially
611   /// to a given entry state.
612   /// TODO: We might need to store intermediate states per-fact in the block for
613   /// later analysis.
transferBlock(const CFGBlock * Block,LifetimeLattice EntryState)614   LifetimeLattice transferBlock(const CFGBlock *Block,
615                                 LifetimeLattice EntryState) {
616     LifetimeLattice BlockState = EntryState;
617     llvm::ArrayRef<const Fact *> Facts = AllFacts.getFacts(Block);
618 
619     for (const Fact *F : Facts) {
620       BlockState = transferFact(BlockState, F);
621     }
622     return BlockState;
623   }
624 
625 private:
transferFact(LifetimeLattice In,const Fact * F)626   LifetimeLattice transferFact(LifetimeLattice In, const Fact *F) {
627     switch (F->getKind()) {
628     case Fact::Kind::Issue:
629       return transfer(In, *F->getAs<IssueFact>());
630     case Fact::Kind::AssignOrigin:
631       return transfer(In, *F->getAs<AssignOriginFact>());
632     // Expire and ReturnOfOrigin facts don't modify the Origins and the State.
633     case Fact::Kind::Expire:
634     case Fact::Kind::ReturnOfOrigin:
635       return In;
636     }
637     llvm_unreachable("Unknown fact kind");
638   }
639 
640   /// A new loan is issued to the origin. Old loans are erased.
transfer(LifetimeLattice In,const IssueFact & F)641   LifetimeLattice transfer(LifetimeLattice In, const IssueFact &F) {
642     OriginID OID = F.getOriginID();
643     LoanID LID = F.getLoanID();
644     return LifetimeLattice(Factory.OriginMapFactory.add(
645         In.Origins, OID, Factory.createLoanSet(LID)));
646   }
647 
648   /// The destination origin's loan set is replaced by the source's.
649   /// This implicitly "resets" the old loans of the destination.
transfer(LifetimeLattice InState,const AssignOriginFact & F)650   LifetimeLattice transfer(LifetimeLattice InState, const AssignOriginFact &F) {
651     OriginID DestOID = F.getDestOriginID();
652     OriginID SrcOID = F.getSrcOriginID();
653     LoanSet SrcLoans = InState.getLoans(SrcOID);
654     return LifetimeLattice(
655         Factory.OriginMapFactory.add(InState.Origins, DestOID, SrcLoans));
656   }
657 };
658 
659 // ========================================================================= //
660 //                              Dataflow analysis
661 // ========================================================================= //
662 
663 /// Drives the intra-procedural dataflow analysis.
664 ///
665 /// Orchestrates the analysis by iterating over the CFG using a worklist
666 /// algorithm. It computes a fixed point by propagating the LifetimeLattice
667 /// state through each block until the state no longer changes.
668 /// TODO: Maybe use the dataflow framework! The framework might need changes
669 /// to support the current comparison done at block-entry.
670 class LifetimeDataflow {
671   const CFG &Cfg;
672   AnalysisDeclContext &AC;
673   LifetimeFactory LifetimeFact;
674 
675   Transferer Xfer;
676 
677   /// Stores the merged analysis state at the entry of each CFG block.
678   llvm::DenseMap<const CFGBlock *, LifetimeLattice> BlockEntryStates;
679   /// Stores the analysis state at the exit of each CFG block, after the
680   /// transfer function has been applied.
681   llvm::DenseMap<const CFGBlock *, LifetimeLattice> BlockExitStates;
682 
683 public:
LifetimeDataflow(const CFG & C,FactManager & FS,AnalysisDeclContext & AC)684   LifetimeDataflow(const CFG &C, FactManager &FS, AnalysisDeclContext &AC)
685       : Cfg(C), AC(AC), Xfer(FS, LifetimeFact) {}
686 
run()687   void run() {
688     llvm::TimeTraceScope TimeProfile("Lifetime Dataflow");
689     ForwardDataflowWorklist Worklist(Cfg, AC);
690     const CFGBlock *Entry = &Cfg.getEntry();
691     BlockEntryStates[Entry] = LifetimeLattice{};
692     Worklist.enqueueBlock(Entry);
693     while (const CFGBlock *B = Worklist.dequeue()) {
694       LifetimeLattice EntryState = getEntryState(B);
695       LifetimeLattice ExitState = Xfer.transferBlock(B, EntryState);
696       BlockExitStates[B] = ExitState;
697 
698       for (const CFGBlock *Successor : B->succs()) {
699         auto SuccIt = BlockEntryStates.find(Successor);
700         LifetimeLattice OldSuccEntryState = (SuccIt != BlockEntryStates.end())
701                                                 ? SuccIt->second
702                                                 : LifetimeLattice{};
703         LifetimeLattice NewSuccEntryState =
704             OldSuccEntryState.join(ExitState, LifetimeFact);
705         // Enqueue the successor if its entry state has changed.
706         // TODO(opt): Consider changing 'join' to report a change if !=
707         // comparison is found expensive.
708         if (SuccIt == BlockEntryStates.end() ||
709             NewSuccEntryState != OldSuccEntryState) {
710           BlockEntryStates[Successor] = NewSuccEntryState;
711           Worklist.enqueueBlock(Successor);
712         }
713       }
714     }
715   }
716 
dump() const717   void dump() const {
718     llvm::dbgs() << "==========================================\n";
719     llvm::dbgs() << "       Dataflow results:\n";
720     llvm::dbgs() << "==========================================\n";
721     const CFGBlock &B = Cfg.getExit();
722     getExitState(&B).dump(llvm::dbgs());
723   }
724 
getEntryState(const CFGBlock * B) const725   LifetimeLattice getEntryState(const CFGBlock *B) const {
726     return BlockEntryStates.lookup(B);
727   }
728 
getExitState(const CFGBlock * B) const729   LifetimeLattice getExitState(const CFGBlock *B) const {
730     return BlockExitStates.lookup(B);
731   }
732 };
733 
734 // ========================================================================= //
735 //  TODO: Analysing dataflow results and error reporting.
736 // ========================================================================= //
737 } // anonymous namespace
738 
runLifetimeSafetyAnalysis(const DeclContext & DC,const CFG & Cfg,AnalysisDeclContext & AC)739 void runLifetimeSafetyAnalysis(const DeclContext &DC, const CFG &Cfg,
740                                AnalysisDeclContext &AC) {
741   llvm::TimeTraceScope TimeProfile("LifetimeSafetyAnalysis");
742   DEBUG_WITH_TYPE("PrintCFG", Cfg.dump(AC.getASTContext().getLangOpts(),
743                                        /*ShowColors=*/true));
744   FactManager FactMgr;
745   FactGenerator FactGen(FactMgr, AC);
746   FactGen.run();
747   DEBUG_WITH_TYPE("LifetimeFacts", FactMgr.dump(Cfg, AC));
748 
749   /// TODO(opt): Consider optimizing individual blocks before running the
750   /// dataflow analysis.
751   /// 1. Expression Origins: These are assigned once and read at most once,
752   ///    forming simple chains. These chains can be compressed into a single
753   ///    assignment.
754   /// 2. Block-Local Loans: Origins of expressions are never read by other
755   ///    blocks; only Decls are visible.  Therefore, loans in a block that
756   ///    never reach an Origin associated with a Decl can be safely dropped by
757   ///    the analysis.
758   LifetimeDataflow Dataflow(Cfg, FactMgr, AC);
759   Dataflow.run();
760   DEBUG_WITH_TYPE("LifetimeDataflow", Dataflow.dump());
761 }
762 } // namespace clang
763