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

>From e1327406119cc0887f473ebd93a02d6eb383b234 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <u...@google.com>
Date: Tue, 15 Jul 2025 22:19:48 +0000
Subject: [PATCH] add-liveness-finally

---
 .../clang/Analysis/Analyses/LifetimeSafety.h  |   5 +
 clang/lib/Analysis/LifetimeSafety.cpp         |  83 +++++++++++
 .../unittests/Analysis/LifetimeSafetyTest.cpp | 136 ++++++++++++++++++
 3 files changed, 224 insertions(+)

diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety.h 
b/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
index 1c00558d32f63..1b7cb82fbe891 100644
--- a/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
+++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety.h
@@ -34,6 +34,7 @@ class Fact;
 class FactManager;
 class LoanPropagationAnalysis;
 class ExpiredLoansAnalysis;
+class LiveOriginAnalysis;
 struct LifetimeFactory;
 
 /// A generic, type-safe wrapper for an ID, distinguished by its `Tag` type.
@@ -97,6 +98,9 @@ class LifetimeSafetyAnalysis {
   /// their Path.
   std::vector<LoanID> getLoanIDForVar(const VarDecl *VD) const;
 
+  /// Returns the set of live origins at a specific program point.
+  OriginSet getLiveOriginsAtPoint(ProgramPoint PP) const;
+
   /// Retrieves program points that were specially marked in the source code
   /// for testing.
   ///
@@ -114,6 +118,7 @@ class LifetimeSafetyAnalysis {
   std::unique_ptr<FactManager> FactMgr;
   std::unique_ptr<LoanPropagationAnalysis> LoanPropagation;
   std::unique_ptr<ExpiredLoansAnalysis> ExpiredLoans;
+  std::unique_ptr<LiveOriginAnalysis> LiveOrigins;
 };
 } // namespace internal
 } // namespace clang::lifetimes
diff --git a/clang/lib/Analysis/LifetimeSafety.cpp 
b/clang/lib/Analysis/LifetimeSafety.cpp
index 815a36e13412c..a717681c4ea44 100644
--- a/clang/lib/Analysis/LifetimeSafety.cpp
+++ b/clang/lib/Analysis/LifetimeSafety.cpp
@@ -727,12 +727,14 @@ join(llvm::ImmutableMap<K, V> A, llvm::ImmutableMap<K, V> 
B,
 // ========================================================================= //
 
 using OriginLoanMap = llvm::ImmutableMap<OriginID, LoanSet>;
+using OriginSet = llvm::ImmutableSet<OriginID>;
 
 /// An object to hold the factories for immutable collections, ensuring
 /// that all created states share the same underlying memory management.
 struct LifetimeFactory {
   OriginLoanMap::Factory OriginMapFactory;
   LoanSet::Factory LoanSetFactory;
+  OriginSet::Factory OriginSetFactory;
 
   /// Creates a singleton set containing only the given loan ID.
   LoanSet createLoanSet(LoanID LID) {
@@ -833,6 +835,78 @@ class LoanPropagationAnalysis
   }
 };
 
+// ========================================================================= //
+//                         Live Origins Analysis
+// ========================================================================= //
+
+/// The dataflow lattice for origin liveness analysis.
+/// It tracks the set of origins that are live at a given program point.
+struct LivenessLattice {
+  OriginSet LiveOrigins;
+
+  LivenessLattice() : LiveOrigins(nullptr) {};
+  explicit LivenessLattice(OriginSet S) : LiveOrigins(S) {}
+
+  bool operator==(const LivenessLattice &Other) const {
+    return LiveOrigins == Other.LiveOrigins;
+  }
+  bool operator!=(const LivenessLattice &Other) const {
+    return !(*this == Other);
+  }
+
+  void dump(llvm::raw_ostream &OS) const {
+    OS << "LivenessLattice State:\n";
+    if (LiveOrigins.isEmpty())
+      OS << "  <empty>\n";
+    for (const OriginID &OID : LiveOrigins)
+      OS << "  Origin " << OID << " is live\n";
+  }
+};
+
+/// The analysis that tracks which origins are live. This is a backward
+/// analysis.
+class LiveOriginAnalysis
+    : public DataflowAnalysis<LiveOriginAnalysis, LivenessLattice,
+                              Direction::Backward> {
+
+  OriginSet::Factory &SetFactory;
+
+public:
+  LiveOriginAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F,
+                     OriginSet::Factory &SF)
+      : DataflowAnalysis(C, AC, F), SetFactory(SF) {}
+
+  using DataflowAnalysis<LiveOriginAnalysis, Lattice,
+                         Direction::Backward>::transfer;
+
+  StringRef getAnalysisName() const { return "LiveOrigins"; }
+
+  Lattice getInitialState() { return Lattice(SetFactory.getEmptySet()); }
+
+  /// Merges two lattices by taking the union of the live origin sets.
+  Lattice join(Lattice L1, Lattice L2) const {
+    return Lattice(utils::join(L1.LiveOrigins, L2.LiveOrigins, SetFactory));
+  }
+
+  /// An assignment `p = q` kills the liveness of `p` and generates liveness
+  /// for `q`.
+  Lattice transfer(Lattice In, const AssignOriginFact &F) {
+    OriginSet S = SetFactory.remove(In.LiveOrigins, F.getDestOriginID());
+    S = SetFactory.add(S, F.getSrcOriginID());
+    return Lattice(S);
+  }
+
+  /// Issuing a new loan to an origin kills its liveness.
+  Lattice transfer(Lattice In, const IssueFact &F) {
+    return Lattice(SetFactory.remove(In.LiveOrigins, F.getOriginID()));
+  }
+
+  /// A return statement generates liveness for the returned origin.
+  Lattice transfer(Lattice In, const ReturnOfOriginFact &F) {
+    return Lattice(SetFactory.add(In.LiveOrigins, F.getReturnedOriginID()));
+  }
+};
+
 // ========================================================================= //
 //                         Expired Loans Analysis
 // ========================================================================= //
@@ -937,6 +1011,10 @@ void LifetimeSafetyAnalysis::run() {
   ExpiredLoans =
       std::make_unique<ExpiredLoansAnalysis>(Cfg, AC, *FactMgr, *Factory);
   ExpiredLoans->run();
+
+  LiveOrigins = std::make_unique<LiveOriginAnalysis>(Cfg, AC, *FactMgr,
+                                                     
Factory->OriginSetFactory);
+  LiveOrigins->run();
 }
 
 LoanSet LifetimeSafetyAnalysis::getLoansAtPoint(OriginID OID,
@@ -969,6 +1047,11 @@ LifetimeSafetyAnalysis::getLoanIDForVar(const VarDecl 
*VD) const {
   return Result;
 }
 
+OriginSet LifetimeSafetyAnalysis::getLiveOriginsAtPoint(ProgramPoint PP) const 
{
+  assert(LiveOrigins && "LiveOriginAnalysis has not been run.");
+  return LiveOrigins->getState(PP).LiveOrigins;
+}
+
 llvm::StringMap<ProgramPoint> LifetimeSafetyAnalysis::getTestPoints() const {
   assert(FactMgr && "FactManager not initialized");
   llvm::StringMap<ProgramPoint> AnnotationToPointMap;
diff --git a/clang/unittests/Analysis/LifetimeSafetyTest.cpp 
b/clang/unittests/Analysis/LifetimeSafetyTest.cpp
index a48fcfd9865a8..7f2cf0eec654f 100644
--- a/clang/unittests/Analysis/LifetimeSafetyTest.cpp
+++ b/clang/unittests/Analysis/LifetimeSafetyTest.cpp
@@ -125,6 +125,13 @@ class LifetimeTestHelper {
     return Analysis.getExpiredLoansAtPoint(PP);
   }
 
+  std::optional<OriginSet> getLiveOriginsAtPoint(llvm::StringRef Annotation) {
+    ProgramPoint PP = Runner.getProgramPoint(Annotation);
+    if (!PP)
+      return std::nullopt;
+    return Analysis.getLiveOriginsAtPoint(PP);
+  }
+
 private:
   template <typename DeclT> DeclT *findDecl(llvm::StringRef Name) {
     auto &Ctx = Runner.getASTContext();
@@ -162,6 +169,15 @@ class OriginInfo {
   LifetimeTestHelper &Helper;
 };
 
+// A helper class to represent a set of origins, identified by variable names.
+class OriginsInfo {
+public:
+  OriginsInfo(const std::vector<std::string> &Vars, LifetimeTestHelper &H)
+      : OriginVars(Vars), Helper(H) {}
+  std::vector<std::string> OriginVars;
+  LifetimeTestHelper &Helper;
+};
+
 /// Matcher to verify the set of loans held by an origin at a specific
 /// program point.
 ///
@@ -232,6 +248,34 @@ MATCHER_P(AreExpiredAt, Annotation, "") {
                             ActualExpiredLoans, result_listener);
 }
 
+/// Matcher to verify the complete set of live origins at a program point.
+MATCHER_P(AreLiveAt, Annotation, "") {
+  const OriginsInfo &Info = arg;
+  auto &Helper = Info.Helper;
+
+  auto ActualLiveSetOpt = Helper.getLiveOriginsAtPoint(Annotation);
+  if (!ActualLiveSetOpt) {
+    *result_listener << "could not get a valid live origin set at point '"
+                     << Annotation << "'";
+    return false;
+  }
+  std::vector<OriginID> ActualLiveOrigins(ActualLiveSetOpt->begin(),
+                                          ActualLiveSetOpt->end());
+
+  std::vector<OriginID> ExpectedLiveOrigins;
+  for (const auto &VarName : Info.OriginVars) {
+    auto OriginIDOpt = Helper.getOriginForDecl(VarName);
+    if (!OriginIDOpt) {
+      *result_listener << "could not find an origin for variable '" << VarName
+                       << "'";
+      return false;
+    }
+    ExpectedLiveOrigins.push_back(*OriginIDOpt);
+  }
+  return ExplainMatchResult(UnorderedElementsAreArray(ExpectedLiveOrigins),
+                            ActualLiveOrigins, result_listener);
+}
+
 // Base test fixture to manage the runner and helper.
 class LifetimeAnalysisTest : public ::testing::Test {
 protected:
@@ -244,6 +288,13 @@ class LifetimeAnalysisTest : public ::testing::Test {
     return OriginInfo(OriginVar, *Helper);
   }
 
+  /// Factory function that hides the std::vector creation.
+  OriginsInfo Origins(std::initializer_list<std::string> OriginVars) {
+    return OriginsInfo({OriginVars}, *Helper);
+  }
+
+  OriginsInfo NoOrigins() { return Origins({}); }
+
   /// Factory function that hides the std::vector creation.
   LoanSetInfo LoansTo(std::initializer_list<std::string> LoanVars) {
     return LoanSetInfo({LoanVars}, *Helper);
@@ -706,5 +757,90 @@ TEST_F(LifetimeAnalysisTest, 
ReassignedPointerThenOriginalExpires) {
   EXPECT_THAT(LoansTo({"s1", "s2"}), AreExpiredAt("p_after_s1_expires"));
 }
 
+TEST_F(LifetimeAnalysisTest, LivenessDeadPointer) {
+  SetupTest(R"(
+    void target() {
+      POINT(p2);
+      MyObj s;
+      MyObj* p = &s;
+      POINT(p1);
+    }
+  )");
+  EXPECT_THAT(NoOrigins(), AreLiveAt("p1"));
+  EXPECT_THAT(NoOrigins(), AreLiveAt("p2"));
+}
+
+TEST_F(LifetimeAnalysisTest, LivenessSimpleReturn) {
+  SetupTest(R"(
+    MyObj* target() {
+      MyObj s;
+      MyObj* p = &s;
+      POINT(p1);
+      return p;
+    }
+  )");
+  EXPECT_THAT(Origins({"p"}), AreLiveAt("p1"));
+}
+
+TEST_F(LifetimeAnalysisTest, LivenessKilledByReassignment) {
+  SetupTest(R"(
+    MyObj* target() {
+      MyObj s1, s2;
+      MyObj* p = &s1;
+      POINT(p1);
+      p = &s2;
+      POINT(p2);
+      return p;
+    }
+  )");
+  EXPECT_THAT(Origins({"p"}), AreLiveAt("p2"));
+  EXPECT_THAT(NoOrigins(), AreLiveAt("p1"));
+}
+
+TEST_F(LifetimeAnalysisTest, LivenessAcrossBranches) {
+  SetupTest(R"(
+    MyObj* target(bool c) {
+      MyObj x, y;
+      MyObj* p = nullptr;
+      POINT(p1);
+      if (c) {
+        p = &x;
+        POINT(p2);
+      } else {
+        p = &y;
+        POINT(p3);
+      }
+      return p;
+    }
+  )");
+  EXPECT_THAT(Origins({"p"}), AreLiveAt("p2"));
+  EXPECT_THAT(Origins({"p"}), AreLiveAt("p3"));
+  // Before the `if`, the value of `p` (`nullptr`) is always overwritten before
+  EXPECT_THAT(NoOrigins(), AreLiveAt("p1"));
+}
+
+TEST_F(LifetimeAnalysisTest, LivenessInLoop) {
+  SetupTest(R"(
+    MyObj* target(bool c) {
+      MyObj s1, s2;
+      MyObj* p = &s1;
+      MyObj* q = &s2;
+      POINT(p1);
+      while(c) {
+        POINT(p2);
+        p = q;
+        POINT(p3);
+      }
+      POINT(p4);
+      return p;
+    }
+  )");
+
+  EXPECT_THAT(Origins({"p"}), AreLiveAt("p4"));
+  EXPECT_THAT(Origins({"p", "q"}), AreLiveAt("p3"));
+  EXPECT_THAT(Origins({"q"}), AreLiveAt("p2"));
+  EXPECT_THAT(Origins({"p", "q"}), AreLiveAt("p1"));
+}
+
 } // anonymous namespace
 } // namespace clang::lifetimes::internal

_______________________________________________
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