77// ===----------------------------------------------------------------------===//
88#include " clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h"
99#include " Dataflow.h"
10+ #include " llvm/ADT/BitVector.h"
11+ #include " llvm/Support/TimeProfiler.h"
1012#include < memory>
1113
1214namespace clang ::lifetimes::internal {
15+
16+ // Pre-pass to find persistent origins. An origin is persistent if it is
17+ // referenced in more than one basic block.
18+ static llvm::BitVector computePersistentOrigins (FactManager &FactMgr,
19+ const CFG &C) {
20+ llvm::TimeTraceScope (" ComputePersistentOrigins" );
21+ llvm::BitVector PersistentOrigins (FactMgr.getOriginMgr ().getOrigins ().size () +
22+ 1 );
23+
24+ llvm::DenseMap<OriginID, const CFGBlock *> OriginToBlock;
25+ for (const CFGBlock *B : C) {
26+ for (const Fact *F : FactMgr.getFacts (B)) {
27+ auto CheckOrigin = [&](OriginID OID) {
28+ if (PersistentOrigins.test (OID.Value ))
29+ return ;
30+ auto It = OriginToBlock.find (OID);
31+ if (It == OriginToBlock.end ())
32+ OriginToBlock[OID] = B;
33+ else if (It->second != B) {
34+ // We saw this origin in more than one block.
35+ PersistentOrigins.set (OID.Value );
36+ }
37+ };
38+
39+ switch (F->getKind ()) {
40+ case Fact::Kind::Issue:
41+ CheckOrigin (F->getAs <IssueFact>()->getOriginID ());
42+ break ;
43+ case Fact::Kind::OriginFlow: {
44+ const auto *OF = F->getAs <OriginFlowFact>();
45+ CheckOrigin (OF->getDestOriginID ());
46+ CheckOrigin (OF->getSrcOriginID ());
47+ break ;
48+ }
49+ case Fact::Kind::ReturnOfOrigin:
50+ CheckOrigin (F->getAs <ReturnOfOriginFact>()->getReturnedOriginID ());
51+ break ;
52+ case Fact::Kind::Use:
53+ CheckOrigin (F->getAs <UseFact>()->getUsedOrigin (FactMgr.getOriginMgr ()));
54+ break ;
55+ case Fact::Kind::Expire:
56+ case Fact::Kind::TestPoint:
57+ break ;
58+ }
59+ }
60+ }
61+ return PersistentOrigins;
62+ }
63+
1364namespace {
65+
1466// / Represents the dataflow lattice for loan propagation.
1567// /
1668// / This lattice tracks which loans each origin may hold at a given program
1769// / point.The lattice has a finite height: An origin's loan set is bounded by
1870// / the total number of loans in the function.
19- // / TODO(opt): To reduce the lattice size, propagate origins of declarations,
20- // / not expressions, because expressions are not visible across blocks.
2171struct Lattice {
2272 // / The map from an origin to the set of loans it contains.
23- OriginLoanMap Origins = OriginLoanMap(nullptr );
24-
25- explicit Lattice (const OriginLoanMap &S) : Origins(S) {}
73+ // / Origins that appear in multiple blocks. Participates in join operations.
74+ OriginLoanMap PersistentOrigins = OriginLoanMap(nullptr );
75+ // / Origins confined to a single block. Discarded at block boundaries.
76+ OriginLoanMap BlockLocalOrigins = OriginLoanMap(nullptr );
77+
78+ explicit Lattice (const OriginLoanMap &Persistent,
79+ const OriginLoanMap &BlockLocal)
80+ : PersistentOrigins(Persistent), BlockLocalOrigins(BlockLocal) {}
2681 Lattice () = default ;
2782
2883 bool operator ==(const Lattice &Other) const {
29- return Origins == Other.Origins ;
84+ return PersistentOrigins == Other.PersistentOrigins &&
85+ BlockLocalOrigins == Other.BlockLocalOrigins ;
3086 }
3187 bool operator !=(const Lattice &Other) const { return !(*this == Other); }
3288
3389 void dump (llvm::raw_ostream &OS) const {
3490 OS << " LoanPropagationLattice State:\n " ;
35- if (Origins.isEmpty ())
91+ OS << " Persistent Origins:\n " ;
92+ if (PersistentOrigins.isEmpty ())
93+ OS << " <empty>\n " ;
94+ for (const auto &Entry : PersistentOrigins) {
95+ if (Entry.second .isEmpty ())
96+ OS << " Origin " << Entry.first << " contains no loans\n " ;
97+ for (const LoanID &LID : Entry.second )
98+ OS << " Origin " << Entry.first << " contains Loan " << LID << " \n " ;
99+ }
100+ OS << " Block-Local Origins:\n " ;
101+ if (BlockLocalOrigins.isEmpty ())
36102 OS << " <empty>\n " ;
37- for (const auto &Entry : Origins ) {
103+ for (const auto &Entry : BlockLocalOrigins ) {
38104 if (Entry.second .isEmpty ())
39105 OS << " Origin " << Entry.first << " contains no loans\n " ;
40106 for (const LoanID &LID : Entry.second )
@@ -50,7 +116,8 @@ class AnalysisImpl
50116 OriginLoanMap::Factory &OriginLoanMapFactory,
51117 LoanSet::Factory &LoanSetFactory)
52118 : DataflowAnalysis(C, AC, F), OriginLoanMapFactory(OriginLoanMapFactory),
53- LoanSetFactory (LoanSetFactory) {}
119+ LoanSetFactory (LoanSetFactory),
120+ PersistentOrigins(computePersistentOrigins(F, C)) {}
54121
55122 using Base::transfer;
56123
@@ -59,10 +126,10 @@ class AnalysisImpl
59126 Lattice getInitialState () { return Lattice{}; }
60127
61128 // / Merges two lattices by taking the union of loans for each origin.
62- // TODO(opt): Keep the state small by removing origins which become dead .
129+ // / Only persistent origins are joined; block-local origins are discarded .
63130 Lattice join (Lattice A, Lattice B) {
64131 OriginLoanMap JoinedOrigins = utils::join (
65- A.Origins , B.Origins , OriginLoanMapFactory,
132+ A.PersistentOrigins , B.PersistentOrigins , OriginLoanMapFactory,
66133 [&](const LoanSet *S1, const LoanSet *S2) {
67134 assert ((S1 || S2) && " unexpectedly merging 2 empty sets" );
68135 if (!S1)
@@ -74,16 +141,15 @@ class AnalysisImpl
74141 // Asymmetric join is a performance win. For origins present only on one
75142 // branch, the loan set can be carried over as-is.
76143 utils::JoinKind::Asymmetric);
77- return Lattice (JoinedOrigins);
144+ return Lattice (JoinedOrigins, OriginLoanMapFactory. getEmptyMap () );
78145 }
79146
80147 // / A new loan is issued to the origin. Old loans are erased.
81148 Lattice transfer (Lattice In, const IssueFact &F) {
82149 OriginID OID = F.getOriginID ();
83150 LoanID LID = F.getLoanID ();
84- return Lattice (OriginLoanMapFactory.add (
85- In.Origins , OID,
86- LoanSetFactory.add (LoanSetFactory.getEmptySet (), LID)));
151+ LoanSet NewLoans = LoanSetFactory.add (LoanSetFactory.getEmptySet (), LID);
152+ return setLoans (In, OID, NewLoans);
87153 }
88154
89155 // / A flow from source to destination. If `KillDest` is true, this replaces
@@ -98,22 +164,42 @@ class AnalysisImpl
98164 LoanSet SrcLoans = getLoans (In, SrcOID);
99165 LoanSet MergedLoans = utils::join (DestLoans, SrcLoans, LoanSetFactory);
100166
101- return Lattice (OriginLoanMapFactory. add (In. Origins , DestOID, MergedLoans) );
167+ return setLoans (In , DestOID, MergedLoans);
102168 }
103169
104170 LoanSet getLoans (OriginID OID, ProgramPoint P) const {
105171 return getLoans (getState (P), OID);
106172 }
107173
108174private:
175+ // / Returns true if the origin is persistent (referenced in multiple blocks).
176+ bool isPersistent (OriginID OID) const {
177+ return PersistentOrigins.test (OID.Value );
178+ }
179+
180+ Lattice setLoans (Lattice L, OriginID OID, LoanSet Loans) {
181+ if (isPersistent (OID)) {
182+ return Lattice (OriginLoanMapFactory.add (L.PersistentOrigins , OID, Loans),
183+ L.BlockLocalOrigins );
184+ }
185+ return Lattice (L.PersistentOrigins ,
186+ OriginLoanMapFactory.add (L.BlockLocalOrigins , OID, Loans));
187+ }
188+
109189 LoanSet getLoans (Lattice L, OriginID OID) const {
110- if (auto *Loans = L.Origins .lookup (OID))
190+ const OriginLoanMap *Map =
191+ isPersistent (OID) ? &L.PersistentOrigins : &L.BlockLocalOrigins ;
192+ if (auto *Loans = Map->lookup (OID))
111193 return *Loans;
112194 return LoanSetFactory.getEmptySet ();
113195 }
114196
115197 OriginLoanMap::Factory &OriginLoanMapFactory;
116198 LoanSet::Factory &LoanSetFactory;
199+ // / Origins that appear in multiple basic blocks and must participate in join
200+ // / operations. Origins not in this set are block-local and can be discarded
201+ // / at block boundaries.
202+ llvm::BitVector PersistentOrigins;
117203};
118204} // namespace
119205
0 commit comments