https://github.com/usx95 updated 
https://github.com/llvm/llvm-project/pull/148222

>From eb33d75e1ad1faf621f90d0b8f6ac3deab267084 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <u...@google.com>
Date: Fri, 11 Jul 2025 11:11:47 +0000
Subject: [PATCH 1/3] [LifetimeSafety] Add expired loans analysis

---
 clang/lib/Analysis/LifetimeSafety.cpp | 140 ++++++++++++++++++++++++++
 1 file changed, 140 insertions(+)

diff --git a/clang/lib/Analysis/LifetimeSafety.cpp 
b/clang/lib/Analysis/LifetimeSafety.cpp
index 20f3285249ac2..a6de79cffb371 100644
--- a/clang/lib/Analysis/LifetimeSafety.cpp
+++ b/clang/lib/Analysis/LifetimeSafety.cpp
@@ -729,6 +729,142 @@ class LifetimeDataflow {
   }
 };
 
+// ========================================================================= //
+//                         Expired Loans Analysis
+// ========================================================================= //
+
+/// The lattice for tracking expired loans. It is a set of loan IDs.
+struct ExpiredLattice {
+  LoanSet Expired;
+
+  ExpiredLattice() = default;
+  explicit ExpiredLattice(LoanSet S) : Expired(S) {}
+
+  bool operator==(const ExpiredLattice &Other) const {
+    return Expired == Other.Expired;
+  }
+  bool operator!=(const ExpiredLattice &Other) const {
+    return !(*this == Other);
+  }
+
+  /// Computes the union of two lattices.
+  ExpiredLattice join(const ExpiredLattice &Other,
+                      LoanSet::Factory &Factory) const {
+    LoanSet JoinedSet = Expired;
+    for (LoanID LID : Other.Expired)
+      JoinedSet = Factory.add(JoinedSet, LID);
+    return ExpiredLattice(JoinedSet);
+  }
+
+  void dump(llvm::raw_ostream &OS) const {
+    OS << "ExpiredLattice State:\n";
+    if (Expired.isEmpty())
+      OS << "  <empty>\n";
+    for (const LoanID &LID : Expired)
+      OS << "  Loan " << LID << " is expired\n";
+  }
+};
+
+/// Transfer function for the expired loans analysis.
+class ExpiredLoansTransferer {
+  FactManager &AllFacts;
+  LoanSet::Factory &SetFactory;
+
+public:
+  explicit ExpiredLoansTransferer(FactManager &F, LoanSet::Factory &SF)
+      : AllFacts(F), SetFactory(SF) {}
+
+  /// Computes the exit state of a block by applying all its facts sequentially
+  /// to a given entry state.
+  ExpiredLattice transferBlock(const CFGBlock *Block,
+                                ExpiredLattice EntryState) {
+    ExpiredLattice BlockState = EntryState;
+    llvm::ArrayRef<const Fact *> Facts = AllFacts.getFacts(Block);
+
+    for (const Fact *F : Facts) {
+      BlockState = transferFact(BlockState, F);
+    }
+    return BlockState;
+  }
+
+private:
+  ExpiredLattice transferFact(ExpiredLattice In, const Fact *F) {
+    if (const auto *EF = F->getAs<ExpireFact>())
+      return ExpiredLattice(SetFactory.add(In.Expired, EF->getLoanID()));
+
+    if (const auto *IF = F->getAs<IssueFact>())
+      return ExpiredLattice(SetFactory.remove(In.Expired, IF->getLoanID()));
+
+    return In;
+  }
+};
+
+/// Dataflow analysis driver for tracking expired loans.
+class ExpiredLoansAnalysis {
+  const CFG &Cfg;
+  AnalysisDeclContext &AC;
+  LoanSet::Factory SetFactory;
+  ExpiredLoansTransferer Xfer;
+
+  llvm::DenseMap<const CFGBlock *, ExpiredLattice> BlockEntryStates;
+  llvm::DenseMap<const CFGBlock *, ExpiredLattice> BlockExitStates;
+
+public:
+  ExpiredLoansAnalysis(const CFG &C, FactManager &FS, AnalysisDeclContext &AC)
+      : Cfg(C), AC(AC), Xfer(FS, SetFactory) {}
+
+  void run() {
+    llvm::TimeTraceScope TimeProfile("Expired Loans Analysis");
+    ForwardDataflowWorklist Worklist(Cfg, AC);
+    const CFGBlock *Entry = &Cfg.getEntry();
+    BlockEntryStates[Entry] = ExpiredLattice(SetFactory.getEmptySet());
+    Worklist.enqueueBlock(Entry);
+    while (const CFGBlock *B = Worklist.dequeue()) {
+      ExpiredLattice EntryState = getEntryState(B);
+      ExpiredLattice ExitState = Xfer.transferBlock(B, EntryState);
+      BlockExitStates[B] = ExitState;
+
+      for (const CFGBlock *Successor : B->succs()) {
+        auto SuccIt = BlockEntryStates.find(Successor);
+        ExpiredLattice OldSuccEntryState = (SuccIt != BlockEntryStates.end())
+                                                ? SuccIt->second
+                                                : ExpiredLattice{};
+        ExpiredLattice NewSuccEntryState =
+            OldSuccEntryState.join(ExitState, SetFactory);
+        if (SuccIt == BlockEntryStates.end() ||
+            NewSuccEntryState != OldSuccEntryState) {
+          BlockEntryStates[Successor] = NewSuccEntryState;
+          Worklist.enqueueBlock(Successor);
+        }
+      }
+    }
+  }
+
+  void dump() const {
+    llvm::dbgs() << "==========================================\n";
+    llvm::dbgs() << "       Expired Loans Results:\n";
+    llvm::dbgs() << "==========================================\n";
+    const CFGBlock &B = Cfg.getExit();
+    getExitState(&B).dump(llvm::dbgs());
+  }
+
+  ExpiredLattice getEntryState(const CFGBlock *B) const {
+    auto It = BlockEntryStates.find(B);
+    if (It != BlockEntryStates.end()) {
+      return It->second;
+    }
+    return ExpiredLattice(SetFactory.getEmptySet());
+  }
+
+  ExpiredLattice getExitState(const CFGBlock *B) const {
+    auto It = BlockExitStates.find(B);
+    if (It != BlockExitStates.end()) {
+      return It->second;
+    }
+    return ExpiredLattice(SetFactory.getEmptySet());
+  }
+};
+
 // ========================================================================= //
 //  TODO: Analysing dataflow results and error reporting.
 // ========================================================================= //
@@ -756,5 +892,9 @@ void runLifetimeSafetyAnalysis(const DeclContext &DC, const 
CFG &Cfg,
   LifetimeDataflow Dataflow(Cfg, FactMgr, AC);
   Dataflow.run();
   DEBUG_WITH_TYPE("LifetimeDataflow", Dataflow.dump());
+
+  ExpiredLoansAnalysis ExpiredAnalysis(Cfg, FactMgr, AC);
+  ExpiredAnalysis.run();
+  DEBUG_WITH_TYPE("ExpiredLoans", ExpiredAnalysis.dump());
 }
 } // namespace clang

>From 47425603375f6fd568da7cec2dbc7073406dbbed Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <u...@google.com>
Date: Mon, 14 Jul 2025 15:50:10 +0000
Subject: [PATCH 2/3] make the dataflow algorithm generic to avoid duplication

---
 clang/lib/Analysis/LifetimeSafety.cpp | 22 +++++++---------------
 1 file changed, 7 insertions(+), 15 deletions(-)

diff --git a/clang/lib/Analysis/LifetimeSafety.cpp 
b/clang/lib/Analysis/LifetimeSafety.cpp
index a6de79cffb371..cdb3e5e47c90f 100644
--- a/clang/lib/Analysis/LifetimeSafety.cpp
+++ b/clang/lib/Analysis/LifetimeSafety.cpp
@@ -737,7 +737,7 @@ class LifetimeDataflow {
 struct ExpiredLattice {
   LoanSet Expired;
 
-  ExpiredLattice() = default;
+  ExpiredLattice() : Expired(nullptr) {};
   explicit ExpiredLattice(LoanSet S) : Expired(S) {}
 
   bool operator==(const ExpiredLattice &Other) const {
@@ -814,10 +814,10 @@ class ExpiredLoansAnalysis {
       : Cfg(C), AC(AC), Xfer(FS, SetFactory) {}
 
   void run() {
-    llvm::TimeTraceScope TimeProfile("Expired Loans Analysis");
+    llvm::TimeTraceScope TimeProfile("ExpiredLoansAnalysis");
     ForwardDataflowWorklist Worklist(Cfg, AC);
     const CFGBlock *Entry = &Cfg.getEntry();
-    BlockEntryStates[Entry] = ExpiredLattice(SetFactory.getEmptySet());
+    BlockEntryStates[Entry] = ExpiredLattice{};
     Worklist.enqueueBlock(Entry);
     while (const CFGBlock *B = Worklist.dequeue()) {
       ExpiredLattice EntryState = getEntryState(B);
@@ -849,24 +849,16 @@ class ExpiredLoansAnalysis {
   }
 
   ExpiredLattice getEntryState(const CFGBlock *B) const {
-    auto It = BlockEntryStates.find(B);
-    if (It != BlockEntryStates.end()) {
-      return It->second;
-    }
-    return ExpiredLattice(SetFactory.getEmptySet());
+    return BlockEntryStates.lookup(B);
   }
 
   ExpiredLattice getExitState(const CFGBlock *B) const {
-    auto It = BlockExitStates.find(B);
-    if (It != BlockExitStates.end()) {
-      return It->second;
-    }
-    return ExpiredLattice(SetFactory.getEmptySet());
+    return BlockExitStates.lookup(B);
   }
 };
 
 // ========================================================================= //
-//  TODO: Analysing dataflow results and error reporting.
+//  TODO: Liveness analysis, analysing dataflow results and error reporting.
 // ========================================================================= //
 } // anonymous namespace
 
@@ -895,6 +887,6 @@ void runLifetimeSafetyAnalysis(const DeclContext &DC, const 
CFG &Cfg,
 
   ExpiredLoansAnalysis ExpiredAnalysis(Cfg, FactMgr, AC);
   ExpiredAnalysis.run();
-  DEBUG_WITH_TYPE("ExpiredLoans", ExpiredAnalysis.dump());
+  DEBUG_WITH_TYPE("LifetimeExpiredLoans", ExpiredAnalysis.dump());
 }
 } // namespace clang

>From e90b6151677644856712faa4c3d0127470932788 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <u...@google.com>
Date: Mon, 14 Jul 2025 16:45:12 +0000
Subject: [PATCH 3/3] format

---
 clang/lib/Analysis/LifetimeSafety.cpp | 445 ++++++++++----------------
 1 file changed, 177 insertions(+), 268 deletions(-)

diff --git a/clang/lib/Analysis/LifetimeSafety.cpp 
b/clang/lib/Analysis/LifetimeSafety.cpp
index cdb3e5e47c90f..6bd8181ef7989 100644
--- a/clang/lib/Analysis/LifetimeSafety.cpp
+++ b/clang/lib/Analysis/LifetimeSafety.cpp
@@ -496,7 +496,108 @@ class FactGenerator : public 
ConstStmtVisitor<FactGenerator> {
 };
 
 // ========================================================================= //
-//                              The Dataflow Lattice
+//                         Generic Dataflow Framework
+// ========================================================================= //
+/// A generic, policy-based driver for forward dataflow analyses. It combines
+/// the dataflow runner and the transferer logic into a single class hierarchy.
+///
+/// The derived class is expected to provide:
+/// - A `Lattice` type.
+/// - `Lattice getInitialState()`
+/// - `Lattice join(Lattice, Lattice)`
+/// - `Lattice transfer(Lattice, const FactType&)` for relevant fact types.
+///
+/// \tparam Derived The CRTP derived class that implements the specific
+/// analysis.
+/// \tparam LatticeType The lattice type used by the analysis.
+template <typename Derived, typename LatticeType> class DataflowAnalysis {
+public:
+  using Lattice = LatticeType;
+
+private:
+  const CFG &Cfg;
+  AnalysisDeclContext &AC;
+
+  llvm::DenseMap<const CFGBlock *, Lattice> BlockEntryStates;
+  llvm::DenseMap<const CFGBlock *, Lattice> BlockExitStates;
+
+protected:
+  FactManager &AllFacts;
+
+  explicit DataflowAnalysis(const CFG &C, AnalysisDeclContext &AC,
+                            FactManager &F)
+      : Cfg(C), AC(AC), AllFacts(F) {}
+
+public:
+  void run() {
+    Derived &d = static_cast<Derived &>(*this);
+    ForwardDataflowWorklist Worklist(Cfg, AC);
+    const CFGBlock *Entry = &Cfg.getEntry();
+    BlockEntryStates[Entry] = d.getInitialState();
+    Worklist.enqueueBlock(Entry);
+
+    while (const CFGBlock *B = Worklist.dequeue()) {
+      Lattice EntryState = getEntryState(B);
+      Lattice ExitState = transferBlock(B, EntryState);
+      BlockExitStates[B] = ExitState;
+
+      for (const CFGBlock *Successor : B->succs()) {
+        auto SuccIt = BlockEntryStates.find(Successor);
+        Lattice OldSuccEntryState = (SuccIt != BlockEntryStates.end())
+                                        ? SuccIt->second
+                                        : d.getInitialState();
+        Lattice NewSuccEntryState = d.join(OldSuccEntryState, ExitState);
+
+        if (SuccIt == BlockEntryStates.end() ||
+            NewSuccEntryState != OldSuccEntryState) {
+          BlockEntryStates[Successor] = NewSuccEntryState;
+          Worklist.enqueueBlock(Successor);
+        }
+      }
+    }
+  }
+
+  Lattice getEntryState(const CFGBlock *B) const {
+    return BlockEntryStates.lookup(B);
+  }
+
+  Lattice getExitState(const CFGBlock *B) const {
+    return BlockExitStates.lookup(B);
+  }
+
+private:
+  Lattice transferBlock(const CFGBlock *Block, Lattice EntryState) {
+    Lattice BlockState = EntryState;
+    for (const Fact *F : AllFacts.getFacts(Block)) {
+      BlockState = transferFact(BlockState, F);
+    }
+    return BlockState;
+  }
+
+  Lattice transferFact(Lattice In, const Fact *F) {
+    Derived *d = static_cast<Derived *>(this);
+    switch (F->getKind()) {
+    case Fact::Kind::Issue:
+      return d->transfer(In, *F->getAs<IssueFact>());
+    case Fact::Kind::Expire:
+      return d->transfer(In, *F->getAs<ExpireFact>());
+    case Fact::Kind::AssignOrigin:
+      return d->transfer(In, *F->getAs<AssignOriginFact>());
+    case Fact::Kind::ReturnOfOrigin:
+      return d->transfer(In, *F->getAs<ReturnOfOriginFact>());
+    }
+    llvm_unreachable("Unknown fact kind");
+  }
+
+public:
+  Lattice transfer(Lattice In, const IssueFact &) { return In; }
+  Lattice transfer(Lattice In, const ExpireFact &) { return In; }
+  Lattice transfer(Lattice In, const AssignOriginFact &) { return In; }
+  Lattice transfer(Lattice In, const ReturnOfOriginFact &) { return In; }
+};
+
+// ========================================================================= //
+//                          Loan Propagation Analysis
 // ========================================================================= //
 
 // Using LLVM's immutable collections is efficient for dataflow analysis
@@ -535,52 +636,6 @@ struct LifetimeLattice {
     return !(*this == Other);
   }
 
-  LoanSet getLoans(OriginID OID, LifetimeFactory &Factory) const {
-    if (auto *Loans = Origins.lookup(OID))
-      return *Loans;
-    return Factory.LoanSetFact.getEmptySet();
-  }
-
-  /// Computes the union of two lattices by performing a key-wise join of
-  /// their OriginLoanMaps.
-  // TODO(opt): This key-wise join is a performance bottleneck. A more
-  // efficient merge could be implemented using a Patricia Trie or HAMT
-  // instead of the current AVL-tree-based ImmutableMap.
-  // TODO(opt): Keep the state small by removing origins which become dead.
-  LifetimeLattice join(const LifetimeLattice &Other,
-                       LifetimeFactory &Factory) const {
-    /// Merge the smaller map into the larger one ensuring we iterate over the
-    /// smaller map.
-    if (Origins.getHeight() < Other.Origins.getHeight())
-      return Other.join(*this, Factory);
-
-    OriginLoanMap JoinedState = Origins;
-    // For each origin in the other map, union its loan set with ours.
-    for (const auto &Entry : Other.Origins) {
-      OriginID OID = Entry.first;
-      LoanSet OtherLoanSet = Entry.second;
-      JoinedState = Factory.OriginMapFact.add(
-          JoinedState, OID,
-          join(getLoans(OID, Factory), OtherLoanSet, Factory));
-    }
-    return LifetimeLattice(JoinedState);
-  }
-
-  LoanSet join(LoanSet a, LoanSet b, LifetimeFactory &Factory) const {
-    /// Merge the smaller set into the larger one ensuring we iterate over the
-    /// smaller set.
-    if (a.getHeight() < b.getHeight())
-      std::swap(a, b);
-    LoanSet Result = a;
-    for (LoanID LID : b) {
-      /// TODO(opt): Profiling shows that this loop is a major performance
-      /// bottleneck. Investigate using a BitVector to represent the set of
-      /// loans for improved join performance.
-      Result = Factory.LoanSetFact.add(Result, LID);
-    }
-    return Result;
-  }
-
   void dump(llvm::raw_ostream &OS) const {
     OS << "LifetimeLattice State:\n";
     if (Origins.isEmpty())
@@ -594,138 +649,66 @@ struct LifetimeLattice {
   }
 };
 
-// ========================================================================= //
-//                              The Transfer Function
-// ========================================================================= //
-class Transferer {
-  FactManager &AllFacts;
+class LoanPropagationAnalysis
+    : public DataflowAnalysis<LoanPropagationAnalysis, LifetimeLattice> {
+
   LifetimeFactory &Factory;
 
 public:
-  explicit Transferer(FactManager &F, LifetimeFactory &Factory)
-      : AllFacts(F), Factory(Factory) {}
-
-  /// Computes the exit state of a block by applying all its facts sequentially
-  /// to a given entry state.
-  /// TODO: We might need to store intermediate states per-fact in the block 
for
-  /// later analysis.
-  LifetimeLattice transferBlock(const CFGBlock *Block,
-                                LifetimeLattice EntryState) {
-    LifetimeLattice BlockState = EntryState;
-    llvm::ArrayRef<const Fact *> Facts = AllFacts.getFacts(Block);
-
-    for (const Fact *F : Facts) {
-      BlockState = transferFact(BlockState, F);
+  LoanPropagationAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager 
&F,
+                          LifetimeFactory &Factory)
+      : DataflowAnalysis(C, AC, F), Factory(Factory) {}
+
+  // Make the base class's transfer overloads visible.
+  using DataflowAnalysis<LoanPropagationAnalysis, Lattice>::transfer;
+
+  Lattice getInitialState() { return Lattice{}; }
+
+  Lattice join(Lattice L1, Lattice L2) {
+    /// Merge the smaller map into the larger one ensuring we iterate over the
+    /// smaller map.
+    if (L1.Origins.getHeight() < L2.Origins.getHeight())
+      std::swap(L1, L2);
+
+    OriginLoanMap JoinedState = L1.Origins;
+    // For each origin in the other map, union its loan set with ours.
+    for (const auto &Entry : L2.Origins) {
+      OriginID OID = Entry.first;
+      LoanSet OtherLoanSet = Entry.second;
+      JoinedState = Factory.OriginMapFact.add(
+          JoinedState, OID, join(getLoans(L1, OID), OtherLoanSet));
     }
-    return BlockState;
+    return Lattice(JoinedState);
   }
 
-private:
-  LifetimeLattice transferFact(LifetimeLattice In, const Fact *F) {
-    switch (F->getKind()) {
-    case Fact::Kind::Issue:
-      return transfer(In, *F->getAs<IssueFact>());
-    case Fact::Kind::AssignOrigin:
-      return transfer(In, *F->getAs<AssignOriginFact>());
-    // Expire and ReturnOfOrigin facts don't modify the Origins and the State.
-    case Fact::Kind::Expire:
-    case Fact::Kind::ReturnOfOrigin:
-      return In;
-    }
-    llvm_unreachable("Unknown fact kind");
+  LoanSet join(LoanSet S1, LoanSet S2) {
+    if (S1.getHeight() < S2.getHeight())
+      std::swap(S1, S2);
+    for (LoanID L : S2)
+      S1 = Factory.LoanSetFact.add(S1, L);
+    return S1;
   }
 
-  /// A new loan is issued to the origin. Old loans are erased.
-  LifetimeLattice transfer(LifetimeLattice In, const IssueFact &F) {
+  // Overloads for specific fact types this transferer cares about.
+  Lattice transfer(Lattice In, const IssueFact &F) {
     OriginID OID = F.getOriginID();
     LoanID LID = F.getLoanID();
-    return LifetimeLattice(
+    return Lattice(
         Factory.OriginMapFact.add(In.Origins, OID, 
Factory.createLoanSet(LID)));
   }
 
-  /// The destination origin's loan set is replaced by the source's.
-  /// This implicitly "resets" the old loans of the destination.
-  LifetimeLattice transfer(LifetimeLattice InState, const AssignOriginFact &F) 
{
+  Lattice transfer(Lattice In, const AssignOriginFact &F) {
     OriginID DestOID = F.getDestOriginID();
     OriginID SrcOID = F.getSrcOriginID();
-    LoanSet SrcLoans = InState.getLoans(SrcOID, Factory);
-    return LifetimeLattice(
-        Factory.OriginMapFact.add(InState.Origins, DestOID, SrcLoans));
+    LoanSet SrcLoans = getLoans(In, SrcOID);
+    return Lattice(Factory.OriginMapFact.add(In.Origins, DestOID, SrcLoans));
   }
-};
-
-// ========================================================================= //
-//                              Dataflow analysis
-// ========================================================================= //
-
-/// Drives the intra-procedural dataflow analysis.
-///
-/// Orchestrates the analysis by iterating over the CFG using a worklist
-/// algorithm. It computes a fixed point by propagating the LifetimeLattice
-/// state through each block until the state no longer changes.
-/// TODO: Maybe use the dataflow framework! The framework might need changes
-/// to support the current comparison done at block-entry.
-class LifetimeDataflow {
-  const CFG &Cfg;
-  AnalysisDeclContext &AC;
-  LifetimeFactory LifetimeFact;
-
-  Transferer Xfer;
-
-  /// Stores the merged analysis state at the entry of each CFG block.
-  llvm::DenseMap<const CFGBlock *, LifetimeLattice> BlockEntryStates;
-  /// Stores the analysis state at the exit of each CFG block, after the
-  /// transfer function has been applied.
-  llvm::DenseMap<const CFGBlock *, LifetimeLattice> BlockExitStates;
-
-public:
-  LifetimeDataflow(const CFG &C, FactManager &FS, AnalysisDeclContext &AC)
-      : Cfg(C), AC(AC), Xfer(FS, LifetimeFact) {}
 
-  void run() {
-    llvm::TimeTraceScope TimeProfile("Lifetime Dataflow");
-    ForwardDataflowWorklist Worklist(Cfg, AC);
-    const CFGBlock *Entry = &Cfg.getEntry();
-    BlockEntryStates[Entry] = LifetimeLattice{};
-    Worklist.enqueueBlock(Entry);
-    while (const CFGBlock *B = Worklist.dequeue()) {
-      LifetimeLattice EntryState = getEntryState(B);
-      LifetimeLattice ExitState = Xfer.transferBlock(B, EntryState);
-      BlockExitStates[B] = ExitState;
-
-      for (const CFGBlock *Successor : B->succs()) {
-        auto SuccIt = BlockEntryStates.find(Successor);
-        LifetimeLattice OldSuccEntryState = (SuccIt != BlockEntryStates.end())
-                                                ? SuccIt->second
-                                                : LifetimeLattice{};
-        LifetimeLattice NewSuccEntryState =
-            OldSuccEntryState.join(ExitState, LifetimeFact);
-        // Enqueue the successor if its entry state has changed.
-        // TODO(opt): Consider changing 'join' to report a change if !=
-        // comparison is found expensive.
-        if (SuccIt == BlockEntryStates.end() ||
-            NewSuccEntryState != OldSuccEntryState) {
-          BlockEntryStates[Successor] = NewSuccEntryState;
-          Worklist.enqueueBlock(Successor);
-        }
-      }
-    }
-  }
-
-  void dump() const {
-    llvm::dbgs() << "==========================================\n";
-    llvm::dbgs() << "       Dataflow results:\n";
-    llvm::dbgs() << "==========================================\n";
-    const CFGBlock &B = Cfg.getExit();
-    getExitState(&B).dump(llvm::dbgs());
-  }
-
-  LifetimeLattice getEntryState(const CFGBlock *B) const {
-    return BlockEntryStates.lookup(B);
-  }
-
-  LifetimeLattice getExitState(const CFGBlock *B) const {
-    return BlockExitStates.lookup(B);
+private:
+  LoanSet getLoans(Lattice L, OriginID OID) {
+    if (auto *Loans = L.Origins.lookup(OID))
+      return *Loans;
+    return Factory.LoanSetFact.getEmptySet();
   }
 };
 
@@ -747,15 +730,6 @@ struct ExpiredLattice {
     return !(*this == Other);
   }
 
-  /// Computes the union of two lattices.
-  ExpiredLattice join(const ExpiredLattice &Other,
-                      LoanSet::Factory &Factory) const {
-    LoanSet JoinedSet = Expired;
-    for (LoanID LID : Other.Expired)
-      JoinedSet = Factory.add(JoinedSet, LID);
-    return ExpiredLattice(JoinedSet);
-  }
-
   void dump(llvm::raw_ostream &OS) const {
     OS << "ExpiredLattice State:\n";
     if (Expired.isEmpty())
@@ -766,100 +740,36 @@ struct ExpiredLattice {
 };
 
 /// Transfer function for the expired loans analysis.
-class ExpiredLoansTransferer {
-  FactManager &AllFacts;
-  LoanSet::Factory &SetFactory;
-
-public:
-  explicit ExpiredLoansTransferer(FactManager &F, LoanSet::Factory &SF)
-      : AllFacts(F), SetFactory(SF) {}
-
-  /// Computes the exit state of a block by applying all its facts sequentially
-  /// to a given entry state.
-  ExpiredLattice transferBlock(const CFGBlock *Block,
-                                ExpiredLattice EntryState) {
-    ExpiredLattice BlockState = EntryState;
-    llvm::ArrayRef<const Fact *> Facts = AllFacts.getFacts(Block);
-
-    for (const Fact *F : Facts) {
-      BlockState = transferFact(BlockState, F);
-    }
-    return BlockState;
-  }
+class ExpiredLoansAnalysis
+    : public DataflowAnalysis<ExpiredLoansAnalysis, ExpiredLattice> {
 
-private:
-  ExpiredLattice transferFact(ExpiredLattice In, const Fact *F) {
-    if (const auto *EF = F->getAs<ExpireFact>())
-      return ExpiredLattice(SetFactory.add(In.Expired, EF->getLoanID()));
-
-    if (const auto *IF = F->getAs<IssueFact>())
-      return ExpiredLattice(SetFactory.remove(In.Expired, IF->getLoanID()));
-
-    return In;
-  }
-};
-
-/// Dataflow analysis driver for tracking expired loans.
-class ExpiredLoansAnalysis {
-  const CFG &Cfg;
-  AnalysisDeclContext &AC;
-  LoanSet::Factory SetFactory;
-  ExpiredLoansTransferer Xfer;
-
-  llvm::DenseMap<const CFGBlock *, ExpiredLattice> BlockEntryStates;
-  llvm::DenseMap<const CFGBlock *, ExpiredLattice> BlockExitStates;
+  LoanSet::Factory &SetFactory;
 
 public:
-  ExpiredLoansAnalysis(const CFG &C, FactManager &FS, AnalysisDeclContext &AC)
-      : Cfg(C), AC(AC), Xfer(FS, SetFactory) {}
+  ExpiredLoansAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F,
+                       LoanSet::Factory &SF)
+      : DataflowAnalysis(C, AC, F), SetFactory(SF) {}
 
-  void run() {
-    llvm::TimeTraceScope TimeProfile("ExpiredLoansAnalysis");
-    ForwardDataflowWorklist Worklist(Cfg, AC);
-    const CFGBlock *Entry = &Cfg.getEntry();
-    BlockEntryStates[Entry] = ExpiredLattice{};
-    Worklist.enqueueBlock(Entry);
-    while (const CFGBlock *B = Worklist.dequeue()) {
-      ExpiredLattice EntryState = getEntryState(B);
-      ExpiredLattice ExitState = Xfer.transferBlock(B, EntryState);
-      BlockExitStates[B] = ExitState;
+  using DataflowAnalysis<ExpiredLoansAnalysis, Lattice>::transfer;
 
-      for (const CFGBlock *Successor : B->succs()) {
-        auto SuccIt = BlockEntryStates.find(Successor);
-        ExpiredLattice OldSuccEntryState = (SuccIt != BlockEntryStates.end())
-                                                ? SuccIt->second
-                                                : ExpiredLattice{};
-        ExpiredLattice NewSuccEntryState =
-            OldSuccEntryState.join(ExitState, SetFactory);
-        if (SuccIt == BlockEntryStates.end() ||
-            NewSuccEntryState != OldSuccEntryState) {
-          BlockEntryStates[Successor] = NewSuccEntryState;
-          Worklist.enqueueBlock(Successor);
-        }
-      }
-    }
-  }
+  Lattice getInitialState() { return Lattice(SetFactory.getEmptySet()); }
 
-  void dump() const {
-    llvm::dbgs() << "==========================================\n";
-    llvm::dbgs() << "       Expired Loans Results:\n";
-    llvm::dbgs() << "==========================================\n";
-    const CFGBlock &B = Cfg.getExit();
-    getExitState(&B).dump(llvm::dbgs());
+  Lattice join(Lattice L1, Lattice L2) const {
+    LoanSet JoinedSet = L1.Expired;
+    for (LoanID LID : L2.Expired)
+      JoinedSet = SetFactory.add(JoinedSet, LID);
+    return Lattice(JoinedSet);
   }
 
-  ExpiredLattice getEntryState(const CFGBlock *B) const {
-    return BlockEntryStates.lookup(B);
+  // Overloads for specific fact types this transferer cares about.
+  Lattice transfer(Lattice In, const ExpireFact &F) {
+    return Lattice(SetFactory.add(In.Expired, F.getLoanID()));
   }
 
-  ExpiredLattice getExitState(const CFGBlock *B) const {
-    return BlockExitStates.lookup(B);
+  Lattice transfer(Lattice In, const IssueFact &F) {
+    return Lattice(SetFactory.remove(In.Expired, F.getLoanID()));
   }
 };
-
-// ========================================================================= //
-//  TODO: Liveness analysis, analysing dataflow results and error reporting.
-// ========================================================================= //
 } // anonymous namespace
 
 void runLifetimeSafetyAnalysis(const DeclContext &DC, const CFG &Cfg,
@@ -872,21 +782,20 @@ void runLifetimeSafetyAnalysis(const DeclContext &DC, 
const CFG &Cfg,
   FactGen.run();
   DEBUG_WITH_TYPE("LifetimeFacts", FactMgr.dump(Cfg, AC));
 
-  /// TODO(opt): Consider optimizing individual blocks before running the
-  /// dataflow analysis.
-  /// 1. Expression Origins: These are assigned once and read at most once,
-  ///    forming simple chains. These chains can be compressed into a single
-  ///    assignment.
-  /// 2. Block-Local Loans: Origins of expressions are never read by other
-  ///    blocks; only Decls are visible.  Therefore, loans in a block that
-  ///    never reach an Origin associated with a Decl can be safely dropped by
-  ///    the analysis.
-  LifetimeDataflow Dataflow(Cfg, FactMgr, AC);
-  Dataflow.run();
-  DEBUG_WITH_TYPE("LifetimeDataflow", Dataflow.dump());
-
-  ExpiredLoansAnalysis ExpiredAnalysis(Cfg, FactMgr, AC);
+  // Run Loan Propagation Analysis
+  LifetimeFactory LifetimeFact;
+  LoanPropagationAnalysis LoanPropagation(Cfg, AC, FactMgr, LifetimeFact);
+  LoanPropagation.run();
+  DEBUG_WITH_TYPE(
+      "LifetimeDataflow",
+      LoanPropagation.getExitState(&Cfg.getExit()).dump(llvm::dbgs()));
+
+  // Run Expired Loans Analysis
+  ExpiredLoansAnalysis ExpiredAnalysis(Cfg, AC, FactMgr,
+                                       LifetimeFact.LoanSetFact);
   ExpiredAnalysis.run();
-  DEBUG_WITH_TYPE("LifetimeExpiredLoans", ExpiredAnalysis.dump());
+  DEBUG_WITH_TYPE(
+      "ExpiredLoans",
+      ExpiredAnalysis.getExitState(&Cfg.getExit()).dump(llvm::dbgs()));
 }
 } // namespace clang

_______________________________________________
llvm-branch-commits mailing list
llvm-branch-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits

Reply via email to