Skip to content

Commit d0df749

Browse files
committed
Optimize loan propagation by separating persistent and block-local origins llvm#165789
1 parent d62a5af commit d0df749

File tree

2 files changed

+104
-18
lines changed

2 files changed

+104
-18
lines changed

clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp

Lines changed: 104 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,34 +7,102 @@
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

1215
namespace 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+
1366
namespace {
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.
2173
struct 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

108176
private:
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

clang/unittests/Analysis/LifetimeSafetyTest.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,6 @@ TEST_F(LifetimeAnalysisTest, PointersInACycle) {
543543
EXPECT_THAT(Origin("p1"), HasLoansTo({"v1", "v2", "v3"}, "after_loop"));
544544
EXPECT_THAT(Origin("p2"), HasLoansTo({"v1", "v2", "v3"}, "after_loop"));
545545
EXPECT_THAT(Origin("p3"), HasLoansTo({"v1", "v2", "v3"}, "after_loop"));
546-
EXPECT_THAT(Origin("temp"), HasLoansTo({"v1", "v2", "v3"}, "after_loop"));
547546
}
548547

549548
TEST_F(LifetimeAnalysisTest, PointersAndExpirationInACycle) {

0 commit comments

Comments
 (0)