Skip to content

Commit 8e7fde9

Browse files
committed
persistent-origin-optimisation
1 parent c46bfed commit 8e7fde9

File tree

2 files changed

+103
-18
lines changed

2 files changed

+103
-18
lines changed

clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp

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

1214
namespace 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+
1364
namespace {
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.
2171
struct 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

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

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)