Skip to content

[LifetimeSafety] Add per-program-point lattice tracking #149199

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

usx95
Copy link
Contributor

@usx95 usx95 commented Jul 16, 2025

Add per-program-point state tracking to the dataflow analysis framework.

  • Added a ProgramPoint type representing a pair of a CFGBlock and a Fact within that block
  • Added a PerPointStates map to store lattice states at each program point
  • Modified the transferBlock method to store intermediate states after each fact is processed
  • Added a getLoans method to the LoanPropagationAnalysis class that uses program points

This change enables more precise analysis by tracking program state at each individual program point rather than just at block boundaries. This is necessary for answering queries about the state of loans, origins, and other properties at specific points in the program, which is required for error reporting in the lifetime safety analysis.

Copy link
Contributor Author

usx95 commented Jul 16, 2025

@usx95 usx95 changed the title lifetime-lattice-tracking-per-point [LifetimeSafety] Add per-program-point lattice tracking Jul 16, 2025
@usx95 usx95 force-pushed the users/usx95/07-16-lifetime-lattice-tracking-per-point branch from 4f11ea0 to a0b1ef0 Compare July 16, 2025 22:27
@usx95 usx95 marked this pull request as ready for review July 16, 2025 22:35
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:analysis labels Jul 16, 2025
@usx95 usx95 requested review from jvoung, Xazax-hun and ymand July 16, 2025 22:36
@llvmbot
Copy link
Member

llvmbot commented Jul 16, 2025

@llvm/pr-subscribers-clang

Author: Utkarsh Saxena (usx95)

Changes

Add per-program-point state tracking to the dataflow analysis framework.

  • Added a ProgramPoint type representing a pair of a CFGBlock and a Fact within that block
  • Added a PerPointStates map to store lattice states at each program point
  • Modified the transferBlock method to store intermediate states after each fact is processed
  • Added a getLoans method to the LoanPropagationAnalysis class that uses program points

This change enables more precise analysis by tracking program state at each individual program point rather than just at block boundaries. This is necessary for answering queries about the state of loans, origins, and other properties at specific points in the program, which is required for error reporting in the lifetime safety analysis.


Full diff: https://github.com/llvm/llvm-project/pull/149199.diff

1 Files Affected:

  • (modified) clang/lib/Analysis/LifetimeSafety.cpp (+29-7)
diff --git a/clang/lib/Analysis/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety.cpp
index e3a03cf93880e..3bf3b0d344cfa 100644
--- a/clang/lib/Analysis/LifetimeSafety.cpp
+++ b/clang/lib/Analysis/LifetimeSafety.cpp
@@ -502,6 +502,12 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
 
 enum class Direction { Forward, Backward };
 
+/// A program point is a pair of a CFGBlock and a Fact within that block.
+///
+/// This is used to represent the state of the program *after* the Fact is
+/// executed.
+using ProgramPoint = std::pair<const CFGBlock *, const Fact *>;
+
 /// A generic, policy-based driver for dataflow analyses. It combines
 /// the dataflow runner and the transferer logic into a single class hierarchy.
 ///
@@ -530,8 +536,14 @@ class DataflowAnalysis {
   const CFG &Cfg;
   AnalysisDeclContext &AC;
 
+  /// The dataflow state before a basic block is processed.
   llvm::DenseMap<const CFGBlock *, Lattice> InStates;
+  /// The dataflow state after a basic block is processed.
   llvm::DenseMap<const CFGBlock *, Lattice> OutStates;
+  /// The dataflow state at a Program Point.
+  /// In a forward analysis, this is the state after the Fact at that point has
+  /// been applied, while in a backward analysis, it is the state before.
+  llvm::DenseMap<ProgramPoint, Lattice> PerPointStates;
 
   static constexpr bool isForward() { return Dir == Direction::Forward; }
 
@@ -577,6 +589,8 @@ class DataflowAnalysis {
     }
   }
 
+  Lattice getState(ProgramPoint P) const { return PerPointStates.lookup(P); }
+
   Lattice getInState(const CFGBlock *B) const { return InStates.lookup(B); }
 
   Lattice getOutState(const CFGBlock *B) const { return OutStates.lookup(B); }
@@ -590,18 +604,23 @@ class DataflowAnalysis {
     getOutState(&B).dump(llvm::dbgs());
   }
 
+private:
   /// Computes the state at one end of a block by applying all its facts
   /// sequentially to a given state from the other end.
-  /// TODO: We might need to store intermediate states per-fact in the block for
-  /// later analysis.
   Lattice transferBlock(const CFGBlock *Block, Lattice State) {
     auto Facts = AllFacts.getFacts(Block);
-    if constexpr (isForward())
-      for (const Fact *F : Facts)
+    if constexpr (isForward()) {
+      for (const Fact *F : Facts) {
         State = transferFact(State, F);
-    else
-      for (const Fact *F : llvm::reverse(Facts))
+        PerPointStates[{Block, F}] = State;
+      }
+    } else {
+      for (const Fact *F : llvm::reverse(Facts)) {
+        // In backward analysis, capture the state before applying the fact.
+        PerPointStates[{Block, F}] = State;
         State = transferFact(State, F);
+      }
+    }
     return State;
   }
 
@@ -769,6 +788,10 @@ class LoanPropagationAnalysis
         Factory.OriginMapFactory.add(In.Origins, DestOID, SrcLoans));
   }
 
+  LoanSet getLoans(OriginID OID, ProgramPoint P) {
+    return getLoans(getState(P), OID);
+  }
+
 private:
   LoanSet getLoans(Lattice L, OriginID OID) {
     if (auto *Loans = L.Origins.lookup(OID))
@@ -779,7 +802,6 @@ class LoanPropagationAnalysis
 
 // ========================================================================= //
 //  TODO:
-// - Modifying loan propagation to answer `LoanSet getLoans(Origin O, Point P)`
 // - Modify loan expiry analysis to answer `bool isExpired(Loan L, Point P)`
 // - Modify origin liveness analysis to answer `bool isLive(Origin O, Point P)`
 // - Using the above three to perform the final error reporting.

@llvmbot
Copy link
Member

llvmbot commented Jul 16, 2025

@llvm/pr-subscribers-clang-analysis

Author: Utkarsh Saxena (usx95)

Changes

Add per-program-point state tracking to the dataflow analysis framework.

  • Added a ProgramPoint type representing a pair of a CFGBlock and a Fact within that block
  • Added a PerPointStates map to store lattice states at each program point
  • Modified the transferBlock method to store intermediate states after each fact is processed
  • Added a getLoans method to the LoanPropagationAnalysis class that uses program points

This change enables more precise analysis by tracking program state at each individual program point rather than just at block boundaries. This is necessary for answering queries about the state of loans, origins, and other properties at specific points in the program, which is required for error reporting in the lifetime safety analysis.


Full diff: https://github.com/llvm/llvm-project/pull/149199.diff

1 Files Affected:

  • (modified) clang/lib/Analysis/LifetimeSafety.cpp (+29-7)
diff --git a/clang/lib/Analysis/LifetimeSafety.cpp b/clang/lib/Analysis/LifetimeSafety.cpp
index e3a03cf93880e..3bf3b0d344cfa 100644
--- a/clang/lib/Analysis/LifetimeSafety.cpp
+++ b/clang/lib/Analysis/LifetimeSafety.cpp
@@ -502,6 +502,12 @@ class FactGenerator : public ConstStmtVisitor<FactGenerator> {
 
 enum class Direction { Forward, Backward };
 
+/// A program point is a pair of a CFGBlock and a Fact within that block.
+///
+/// This is used to represent the state of the program *after* the Fact is
+/// executed.
+using ProgramPoint = std::pair<const CFGBlock *, const Fact *>;
+
 /// A generic, policy-based driver for dataflow analyses. It combines
 /// the dataflow runner and the transferer logic into a single class hierarchy.
 ///
@@ -530,8 +536,14 @@ class DataflowAnalysis {
   const CFG &Cfg;
   AnalysisDeclContext &AC;
 
+  /// The dataflow state before a basic block is processed.
   llvm::DenseMap<const CFGBlock *, Lattice> InStates;
+  /// The dataflow state after a basic block is processed.
   llvm::DenseMap<const CFGBlock *, Lattice> OutStates;
+  /// The dataflow state at a Program Point.
+  /// In a forward analysis, this is the state after the Fact at that point has
+  /// been applied, while in a backward analysis, it is the state before.
+  llvm::DenseMap<ProgramPoint, Lattice> PerPointStates;
 
   static constexpr bool isForward() { return Dir == Direction::Forward; }
 
@@ -577,6 +589,8 @@ class DataflowAnalysis {
     }
   }
 
+  Lattice getState(ProgramPoint P) const { return PerPointStates.lookup(P); }
+
   Lattice getInState(const CFGBlock *B) const { return InStates.lookup(B); }
 
   Lattice getOutState(const CFGBlock *B) const { return OutStates.lookup(B); }
@@ -590,18 +604,23 @@ class DataflowAnalysis {
     getOutState(&B).dump(llvm::dbgs());
   }
 
+private:
   /// Computes the state at one end of a block by applying all its facts
   /// sequentially to a given state from the other end.
-  /// TODO: We might need to store intermediate states per-fact in the block for
-  /// later analysis.
   Lattice transferBlock(const CFGBlock *Block, Lattice State) {
     auto Facts = AllFacts.getFacts(Block);
-    if constexpr (isForward())
-      for (const Fact *F : Facts)
+    if constexpr (isForward()) {
+      for (const Fact *F : Facts) {
         State = transferFact(State, F);
-    else
-      for (const Fact *F : llvm::reverse(Facts))
+        PerPointStates[{Block, F}] = State;
+      }
+    } else {
+      for (const Fact *F : llvm::reverse(Facts)) {
+        // In backward analysis, capture the state before applying the fact.
+        PerPointStates[{Block, F}] = State;
         State = transferFact(State, F);
+      }
+    }
     return State;
   }
 
@@ -769,6 +788,10 @@ class LoanPropagationAnalysis
         Factory.OriginMapFactory.add(In.Origins, DestOID, SrcLoans));
   }
 
+  LoanSet getLoans(OriginID OID, ProgramPoint P) {
+    return getLoans(getState(P), OID);
+  }
+
 private:
   LoanSet getLoans(Lattice L, OriginID OID) {
     if (auto *Loans = L.Origins.lookup(OID))
@@ -779,7 +802,6 @@ class LoanPropagationAnalysis
 
 // ========================================================================= //
 //  TODO:
-// - Modifying loan propagation to answer `LoanSet getLoans(Origin O, Point P)`
 // - Modify loan expiry analysis to answer `bool isExpired(Loan L, Point P)`
 // - Modify origin liveness analysis to answer `bool isLive(Origin O, Point P)`
 // - Using the above three to perform the final error reporting.

@usx95 usx95 force-pushed the users/usx95/07-16-lifetime-lattice-tracking-per-point branch from a0b1ef0 to 3e67c98 Compare July 17, 2025 09:48
/// The dataflow state at a Program Point.
/// In a forward analysis, this is the state after the Fact at that point has
/// been applied, while in a backward analysis, it is the state before.
llvm::DenseMap<ProgramPoint, Lattice> PerPointStates;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think all of the analyses need PerPointStates? E.g., what about the ExpiredLoansAnalysis? I wonder if we should have a boolean policy here.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah never mind. Now I remember you want to keep these states instead of replaying the analysis for the blocks.

Copy link
Collaborator

@Xazax-hun Xazax-hun left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LG, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:analysis clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants