2424#include " llvm/Support/TimeProfiler.h"
2525#include < cstdint>
2626
27- namespace clang ::lifetimes {
28- namespace internal {
27+ namespace clang {
2928namespace {
30- template <typename Tag>
31- inline llvm::raw_ostream &operator <<(llvm::raw_ostream &OS, ID<Tag> ID) {
32- return OS << ID.Value ;
33- }
34- } // namespace
3529
3630// / Represents the storage location being borrowed, e.g., a specific stack
3731// / variable.
@@ -42,6 +36,32 @@ struct AccessPath {
4236 AccessPath (const clang::ValueDecl *D) : D(D) {}
4337};
4438
39+ // / A generic, type-safe wrapper for an ID, distinguished by its `Tag` type.
40+ // / Used for giving ID to loans and origins.
41+ template <typename Tag> struct ID {
42+ uint32_t Value = 0 ;
43+
44+ bool operator ==(const ID<Tag> &Other) const { return Value == Other.Value ; }
45+ bool operator !=(const ID<Tag> &Other) const { return !(*this == Other); }
46+ bool operator <(const ID<Tag> &Other) const { return Value < Other.Value ; }
47+ ID<Tag> operator ++(int ) {
48+ ID<Tag> Tmp = *this ;
49+ ++Value;
50+ return Tmp;
51+ }
52+ void Profile (llvm::FoldingSetNodeID &IDBuilder) const {
53+ IDBuilder.AddInteger (Value);
54+ }
55+ };
56+
57+ template <typename Tag>
58+ inline llvm::raw_ostream &operator <<(llvm::raw_ostream &OS, ID<Tag> ID) {
59+ return OS << ID.Value ;
60+ }
61+
62+ using LoanID = ID<struct LoanTag >;
63+ using OriginID = ID<struct OriginTag >;
64+
4565// / Information about a single borrow, or "Loan". A loan is created when a
4666// / reference or pointer is created.
4767struct Loan {
@@ -203,9 +223,7 @@ class Fact {
203223 // / An origin is propagated from a source to a destination (e.g., p = q).
204224 AssignOrigin,
205225 // / An origin escapes the function by flowing into the return value.
206- ReturnOfOrigin,
207- // / A marker for a specific point in the code, for testing.
208- TestPoint,
226+ ReturnOfOrigin
209227 };
210228
211229private:
@@ -292,24 +310,6 @@ class ReturnOfOriginFact : public Fact {
292310 }
293311};
294312
295- // / A dummy-fact used to mark a specific point in the code for testing.
296- // / It is generated by recognizing a `void("__lifetime_test_point_...")` cast.
297- class TestPointFact : public Fact {
298- StringRef Annotation;
299-
300- public:
301- static bool classof (const Fact *F) { return F->getKind () == Kind::TestPoint; }
302-
303- explicit TestPointFact (StringRef Annotation)
304- : Fact(Kind::TestPoint), Annotation(Annotation) {}
305-
306- StringRef getAnnotation () const { return Annotation; }
307-
308- void dump (llvm::raw_ostream &OS) const override {
309- OS << " TestPoint (Annotation: \" " << getAnnotation () << " \" )\n " ;
310- }
311- };
312-
313313class FactManager {
314314public:
315315 llvm::ArrayRef<const Fact *> getFacts (const CFGBlock *B) const {
@@ -363,7 +363,6 @@ class FactManager {
363363};
364364
365365class FactGenerator : public ConstStmtVisitor <FactGenerator> {
366- using Base = ConstStmtVisitor<FactGenerator>;
367366
368367public:
369368 FactGenerator (FactManager &FactMgr, AnalysisDeclContext &AC)
@@ -459,15 +458,6 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
459458 }
460459 }
461460
462- void VisitCXXFunctionalCastExpr (const CXXFunctionalCastExpr *FCE) {
463- // Check if this is a test point marker. If so, we are done with this
464- // expression.
465- if (VisitTestPoint (FCE))
466- return ;
467- // Visit as normal otherwise.
468- Base::VisitCXXFunctionalCastExpr (FCE);
469- }
470-
471461private:
472462 // Check if a type has an origin.
473463 bool hasOrigin (QualType QT) { return QT->isPointerOrReferenceType (); }
@@ -501,27 +491,6 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
501491 }
502492 }
503493
504- // / Checks if the expression is a `void("__lifetime_test_point_...")` cast.
505- // / If so, creates a `TestPointFact` and returns true.
506- bool VisitTestPoint (const CXXFunctionalCastExpr *FCE) {
507- if (!FCE->getType ()->isVoidType ())
508- return false ;
509-
510- const auto *SubExpr = FCE->getSubExpr ()->IgnoreParenImpCasts ();
511- if (const auto *SL = dyn_cast<StringLiteral>(SubExpr)) {
512- llvm::StringRef LiteralValue = SL->getString ();
513- const std::string Prefix = " __lifetime_test_point_" ;
514-
515- if (LiteralValue.starts_with (Prefix)) {
516- StringRef Annotation = LiteralValue.drop_front (Prefix.length ());
517- CurrentBlockFacts.push_back (
518- FactMgr.createFact <TestPointFact>(Annotation));
519- return true ;
520- }
521- }
522- return false ;
523- }
524-
525494 FactManager &FactMgr;
526495 AnalysisDeclContext &AC;
527496 llvm::SmallVector<Fact *> CurrentBlockFacts;
@@ -668,8 +637,6 @@ class DataflowAnalysis {
668637 return D->transfer (In, *F->getAs <AssignOriginFact>());
669638 case Fact::Kind::ReturnOfOrigin:
670639 return D->transfer (In, *F->getAs <ReturnOfOriginFact>());
671- case Fact::Kind::TestPoint:
672- return D->transfer (In, *F->getAs <TestPointFact>());
673640 }
674641 llvm_unreachable (" Unknown fact kind" );
675642 }
@@ -679,16 +646,14 @@ class DataflowAnalysis {
679646 Lattice transfer (Lattice In, const ExpireFact &) { return In; }
680647 Lattice transfer (Lattice In, const AssignOriginFact &) { return In; }
681648 Lattice transfer (Lattice In, const ReturnOfOriginFact &) { return In; }
682- Lattice transfer (Lattice In, const TestPointFact &) { return In; }
683649};
684650
685651namespace utils {
686652
687653// / Computes the union of two ImmutableSets.
688654template <typename T>
689- static llvm::ImmutableSet<T> join (llvm::ImmutableSet<T> A,
690- llvm::ImmutableSet<T> B,
691- typename llvm::ImmutableSet<T>::Factory &F) {
655+ llvm::ImmutableSet<T> join (llvm::ImmutableSet<T> A, llvm::ImmutableSet<T> B,
656+ typename llvm::ImmutableSet<T>::Factory &F) {
692657 if (A.getHeight () < B.getHeight ())
693658 std::swap (A, B);
694659 for (const T &E : B)
@@ -701,7 +666,7 @@ static llvm::ImmutableSet<T> join(llvm::ImmutableSet<T> A,
701666// efficient merge could be implemented using a Patricia Trie or HAMT
702667// instead of the current AVL-tree-based ImmutableMap.
703668template <typename K, typename V, typename Joiner>
704- static llvm::ImmutableMap<K, V>
669+ llvm::ImmutableMap<K, V>
705670join (llvm::ImmutableMap<K, V> A, llvm::ImmutableMap<K, V> B,
706671 typename llvm::ImmutableMap<K, V>::Factory &F, Joiner joinValues) {
707672 if (A.getHeight () < B.getHeight ())
@@ -725,6 +690,10 @@ join(llvm::ImmutableMap<K, V> A, llvm::ImmutableMap<K, V> B,
725690// Loan Propagation Analysis
726691// ========================================================================= //
727692
693+ // Using LLVM's immutable collections is efficient for dataflow analysis
694+ // as it avoids deep copies during state transitions.
695+ // TODO(opt): Consider using a bitset to represent the set of loans.
696+ using LoanSet = llvm::ImmutableSet<LoanID>;
728697using OriginLoanMap = llvm::ImmutableMap<OriginID, LoanSet>;
729698
730699// / An object to hold the factories for immutable collections, ensuring
@@ -838,28 +807,17 @@ class LoanPropagationAnalysis
838807// - Modify origin liveness analysis to answer `bool isLive(Origin O, Point P)`
839808// - Using the above three to perform the final error reporting.
840809// ========================================================================= //
810+ } // anonymous namespace
841811
842- // ========================================================================= //
843- // LifetimeSafetyAnalysis Class Implementation
844- // ========================================================================= //
845-
846- // We need this here for unique_ptr with forward declared class.
847- LifetimeSafetyAnalysis::~LifetimeSafetyAnalysis () = default ;
848-
849- LifetimeSafetyAnalysis::LifetimeSafetyAnalysis (AnalysisDeclContext &AC)
850- : AC(AC), Factory(std::make_unique<LifetimeFactory>()),
851- FactMgr (std::make_unique<FactManager>()) {}
852-
853- void LifetimeSafetyAnalysis::run () {
812+ void runLifetimeSafetyAnalysis (const DeclContext &DC, const CFG &Cfg,
813+ AnalysisDeclContext &AC) {
854814 llvm::TimeTraceScope TimeProfile (" LifetimeSafetyAnalysis" );
855-
856- const CFG &Cfg = *AC.getCFG ();
857815 DEBUG_WITH_TYPE (" PrintCFG" , Cfg.dump (AC.getASTContext ().getLangOpts (),
858816 /* ShowColors=*/ true ));
859-
860- FactGenerator FactGen (* FactMgr, AC);
817+ FactManager FactMgr;
818+ FactGenerator FactGen (FactMgr, AC);
861819 FactGen.run ();
862- DEBUG_WITH_TYPE (" LifetimeFacts" , FactMgr-> dump (Cfg, AC));
820+ DEBUG_WITH_TYPE (" LifetimeFacts" , FactMgr. dump (Cfg, AC));
863821
864822 // / TODO(opt): Consider optimizing individual blocks before running the
865823 // / dataflow analysis.
@@ -870,56 +828,9 @@ void LifetimeSafetyAnalysis::run() {
870828 // / blocks; only Decls are visible. Therefore, loans in a block that
871829 // / never reach an Origin associated with a Decl can be safely dropped by
872830 // / the analysis.
873- LoanPropagation =
874- std::make_unique<LoanPropagationAnalysis>(Cfg, AC, *FactMgr, *Factory);
875- LoanPropagation->run ();
876- }
877-
878- LoanSet LifetimeSafetyAnalysis::getLoansAtPoint (OriginID OID,
879- ProgramPoint PP) const {
880- assert (LoanPropagation && " Analysis has not been run." );
881- return LoanPropagation->getLoans (OID, PP);
882- }
883-
884- std::optional<OriginID>
885- LifetimeSafetyAnalysis::getOriginIDForDecl (const ValueDecl *D) const {
886- assert (FactMgr && " FactManager not initialized" );
887- // This assumes the OriginManager's `get` can find an existing origin.
888- // We might need a `find` method on OriginManager to avoid `getOrCreate` logic
889- // in a const-query context if that becomes an issue.
890- return FactMgr->getOriginMgr ().get (*D);
891- }
892-
893- std::vector<LoanID>
894- LifetimeSafetyAnalysis::getLoanIDForVar (const VarDecl *VD) const {
895- assert (FactMgr && " FactManager not initialized" );
896- std::vector<LoanID> Result;
897- for (const Loan &L : FactMgr->getLoanMgr ().getLoans ())
898- if (L.Path .D == VD)
899- Result.push_back (L.ID );
900- return Result;
901- }
902-
903- llvm::StringMap<ProgramPoint> LifetimeSafetyAnalysis::getTestPoints () const {
904- assert (FactMgr && " FactManager not initialized" );
905- llvm::StringMap<ProgramPoint> AnnotationToPointMap;
906- for (const CFGBlock *Block : *AC.getCFG ()) {
907- for (const Fact *F : FactMgr->getFacts (Block)) {
908- if (const auto *TPF = F->getAs <TestPointFact>()) {
909- StringRef PointName = TPF->getAnnotation ();
910- assert (AnnotationToPointMap.find (PointName) ==
911- AnnotationToPointMap.end () &&
912- " more than one test points with the same name" );
913- AnnotationToPointMap[PointName] = F;
914- }
915- }
916- }
917- return AnnotationToPointMap;
918- }
919- } // namespace internal
920-
921- void runLifetimeSafetyAnalysis (AnalysisDeclContext &AC) {
922- internal::LifetimeSafetyAnalysis Analysis (AC);
923- Analysis.run ();
831+ LifetimeFactory Factory;
832+ LoanPropagationAnalysis LoanPropagation (Cfg, AC, FactMgr, Factory);
833+ LoanPropagation.run ();
834+ DEBUG_WITH_TYPE (" LifetimeLoanPropagation" , LoanPropagation.dump ());
924835}
925- } // namespace clang::lifetimes
836+ } // namespace clang
0 commit comments