[llvm-branch-commits] [clang] [LifetimeSafety] Add expired loans analysis (PR #148222)
Utkarsh Saxena via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Mon Jul 14 09:45:50 PDT 2025
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 <usx at 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 <usx at 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 <usx at 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
More information about the llvm-branch-commits
mailing list