77// ===----------------------------------------------------------------------===//
88#include " clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h"
99#include " Dataflow.h"
10+ #include " clang/Analysis/CFG.h"
11+ #include " llvm/ADT/BitVector.h"
12+ #include " llvm/Support/TimeProfiler.h"
1013#include < memory>
1114
1215namespace clang ::lifetimes::internal {
16+
17+ // Pre-pass to find persistent origins. An origin is persistent if it is
18+ // referenced in more than one basic block.
19+ static llvm::BitVector computePersistentOrigins (FactManager &FactMgr,
20+ const CFG &C) {
21+ llvm::TimeTraceScope (" ComputePersistentOrigins" );
22+ unsigned NumOrigins = FactMgr.getOriginMgr ().getOrigins ().size ();
23+ llvm::BitVector PersistentOrigins (NumOrigins + 1 );
24+
25+ llvm::SmallVector<const CFGBlock *> OriginToFirstSeenBlock (NumOrigins + 1 ,
26+ nullptr );
27+ for (const CFGBlock *B : C) {
28+ for (const Fact *F : FactMgr.getFacts (B)) {
29+ auto CheckOrigin = [&](OriginID OID) {
30+ if (PersistentOrigins.test (OID.Value ))
31+ return ;
32+ auto &FirstSeenBlock = OriginToFirstSeenBlock[OID.Value ];
33+ if (FirstSeenBlock == nullptr )
34+ FirstSeenBlock = B;
35+ if (FirstSeenBlock != B) {
36+ // We saw this origin in more than one block.
37+ PersistentOrigins.set (OID.Value );
38+ }
39+ };
40+
41+ switch (F->getKind ()) {
42+ case Fact::Kind::Issue:
43+ CheckOrigin (F->getAs <IssueFact>()->getOriginID ());
44+ break ;
45+ case Fact::Kind::OriginFlow: {
46+ const auto *OF = F->getAs <OriginFlowFact>();
47+ CheckOrigin (OF->getDestOriginID ());
48+ CheckOrigin (OF->getSrcOriginID ());
49+ break ;
50+ }
51+ case Fact::Kind::ReturnOfOrigin:
52+ CheckOrigin (F->getAs <ReturnOfOriginFact>()->getReturnedOriginID ());
53+ break ;
54+ case Fact::Kind::Use:
55+ CheckOrigin (F->getAs <UseFact>()->getUsedOrigin (FactMgr.getOriginMgr ()));
56+ break ;
57+ case Fact::Kind::Expire:
58+ case Fact::Kind::TestPoint:
59+ break ;
60+ }
61+ }
62+ }
63+ return PersistentOrigins;
64+ }
65+
1366namespace {
67+
1468// / Represents the dataflow lattice for loan propagation.
1569// /
1670// / This lattice tracks which loans each origin may hold at a given program
1771// / point.The lattice has a finite height: An origin's loan set is bounded by
1872// / 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.
2173struct Lattice {
2274 // / 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) {}
75+ // / Origins that appear in multiple blocks. Participates in join operations.
76+ OriginLoanMap PersistentOrigins = OriginLoanMap(nullptr );
77+ // / Origins confined to a single block. Discarded at block boundaries.
78+ OriginLoanMap BlockLocalOrigins = OriginLoanMap(nullptr );
79+
80+ explicit Lattice (const OriginLoanMap &Persistent,
81+ const OriginLoanMap &BlockLocal)
82+ : PersistentOrigins(Persistent), BlockLocalOrigins(BlockLocal) {}
2683 Lattice () = default ;
2784
2885 bool operator ==(const Lattice &Other) const {
29- return Origins == Other.Origins ;
86+ return PersistentOrigins == Other.PersistentOrigins &&
87+ BlockLocalOrigins == Other.BlockLocalOrigins ;
3088 }
3189 bool operator !=(const Lattice &Other) const { return !(*this == Other); }
3290
3391 void dump (llvm::raw_ostream &OS) const {
3492 OS << " LoanPropagationLattice State:\n " ;
35- if (Origins.isEmpty ())
93+ OS << " Persistent Origins:\n " ;
94+ if (PersistentOrigins.isEmpty ())
3695 OS << " <empty>\n " ;
37- for (const auto &Entry : Origins) {
96+ for (const auto &Entry : PersistentOrigins) {
97+ if (Entry.second .isEmpty ())
98+ OS << " Origin " << Entry.first << " contains no loans\n " ;
99+ for (const LoanID &LID : Entry.second )
100+ OS << " Origin " << Entry.first << " contains Loan " << LID << " \n " ;
101+ }
102+ OS << " Block-Local Origins:\n " ;
103+ if (BlockLocalOrigins.isEmpty ())
104+ OS << " <empty>\n " ;
105+ for (const auto &Entry : BlockLocalOrigins) {
38106 if (Entry.second .isEmpty ())
39107 OS << " Origin " << Entry.first << " contains no loans\n " ;
40108 for (const LoanID &LID : Entry.second )
@@ -50,7 +118,8 @@ class AnalysisImpl
50118 OriginLoanMap::Factory &OriginLoanMapFactory,
51119 LoanSet::Factory &LoanSetFactory)
52120 : DataflowAnalysis(C, AC, F), OriginLoanMapFactory(OriginLoanMapFactory),
53- LoanSetFactory (LoanSetFactory) {}
121+ LoanSetFactory (LoanSetFactory),
122+ PersistentOrigins(computePersistentOrigins(F, C)) {}
54123
55124 using Base::transfer;
56125
@@ -59,10 +128,10 @@ class AnalysisImpl
59128 Lattice getInitialState () { return Lattice{}; }
60129
61130 // / 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 .
131+ // / Only persistent origins are joined; block-local origins are discarded .
63132 Lattice join (Lattice A, Lattice B) {
64133 OriginLoanMap JoinedOrigins = utils::join (
65- A.Origins , B.Origins , OriginLoanMapFactory,
134+ A.PersistentOrigins , B.PersistentOrigins , OriginLoanMapFactory,
66135 [&](const LoanSet *S1, const LoanSet *S2) {
67136 assert ((S1 || S2) && " unexpectedly merging 2 empty sets" );
68137 if (!S1)
@@ -74,16 +143,15 @@ class AnalysisImpl
74143 // Asymmetric join is a performance win. For origins present only on one
75144 // branch, the loan set can be carried over as-is.
76145 utils::JoinKind::Asymmetric);
77- return Lattice (JoinedOrigins);
146+ return Lattice (JoinedOrigins, OriginLoanMapFactory. getEmptyMap () );
78147 }
79148
80149 // / A new loan is issued to the origin. Old loans are erased.
81150 Lattice transfer (Lattice In, const IssueFact &F) {
82151 OriginID OID = F.getOriginID ();
83152 LoanID LID = F.getLoanID ();
84- return Lattice (OriginLoanMapFactory.add (
85- In.Origins , OID,
86- LoanSetFactory.add (LoanSetFactory.getEmptySet (), LID)));
153+ LoanSet NewLoans = LoanSetFactory.add (LoanSetFactory.getEmptySet (), LID);
154+ return setLoans (In, OID, NewLoans);
87155 }
88156
89157 // / A flow from source to destination. If `KillDest` is true, this replaces
@@ -98,22 +166,41 @@ class AnalysisImpl
98166 LoanSet SrcLoans = getLoans (In, SrcOID);
99167 LoanSet MergedLoans = utils::join (DestLoans, SrcLoans, LoanSetFactory);
100168
101- return Lattice (OriginLoanMapFactory. add (In. Origins , DestOID, MergedLoans) );
169+ return setLoans (In , DestOID, MergedLoans);
102170 }
103171
104172 LoanSet getLoans (OriginID OID, ProgramPoint P) const {
105173 return getLoans (getState (P), OID);
106174 }
107175
108176private:
177+ // / Returns true if the origin is persistent (referenced in multiple blocks).
178+ bool isPersistent (OriginID OID) const {
179+ return PersistentOrigins.test (OID.Value );
180+ }
181+
182+ Lattice setLoans (Lattice L, OriginID OID, LoanSet Loans) {
183+ if (isPersistent (OID))
184+ return Lattice (OriginLoanMapFactory.add (L.PersistentOrigins , OID, Loans),
185+ L.BlockLocalOrigins );
186+ return Lattice (L.PersistentOrigins ,
187+ OriginLoanMapFactory.add (L.BlockLocalOrigins , OID, Loans));
188+ }
189+
109190 LoanSet getLoans (Lattice L, OriginID OID) const {
110- if (auto *Loans = L.Origins .lookup (OID))
191+ const OriginLoanMap *Map =
192+ isPersistent (OID) ? &L.PersistentOrigins : &L.BlockLocalOrigins ;
193+ if (auto *Loans = Map->lookup (OID))
111194 return *Loans;
112195 return LoanSetFactory.getEmptySet ();
113196 }
114197
115198 OriginLoanMap::Factory &OriginLoanMapFactory;
116199 LoanSet::Factory &LoanSetFactory;
200+ // / Boolean vector indexed by origin ID. If true, the origin appears in
201+ // / multiple basic blocks and must participate in join operations. If false,
202+ // / the origin is block-local and can be discarded at block boundaries.
203+ llvm::BitVector PersistentOrigins;
117204};
118205} // namespace
119206
0 commit comments