Skip to content

Commit e18f21c

Browse files
committed
persistent-origin-optimisation
1 parent c46bfed commit e18f21c

File tree

2 files changed

+93
-15
lines changed

2 files changed

+93
-15
lines changed

clang/lib/Analysis/LifetimeSafety/LoanPropagation.cpp

Lines changed: 93 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,60 @@
77
//===----------------------------------------------------------------------===//
88
#include "clang/Analysis/Analyses/LifetimeSafety/LoanPropagation.h"
99
#include "Dataflow.h"
10+
#include "llvm/Support/TimeProfiler.h"
1011
#include <memory>
1112

1213
namespace clang::lifetimes::internal {
14+
15+
// Pre-pass to find persistent origins. An origin is persistent if it is
16+
// referenced in more than one basic block.
17+
static llvm::DenseSet<OriginID> computePersistentOrigins(FactManager &FactMgr,
18+
const CFG &C) {
19+
llvm::TimeTraceScope("ComputePersistentOrigins");
20+
llvm::DenseSet<OriginID> PersistentOrigins;
21+
22+
llvm::DenseMap<OriginID, const CFGBlock *> OriginToBlock;
23+
for (const CFGBlock *B : C) {
24+
for (const Fact *F : FactMgr.getFacts(B)) {
25+
auto CheckOrigin = [&](OriginID OID) {
26+
if (PersistentOrigins.count(OID))
27+
return;
28+
auto It = OriginToBlock.find(OID);
29+
if (It == OriginToBlock.end()) {
30+
OriginToBlock[OID] = B;
31+
} else if (It->second != B) {
32+
// We saw this origin in more than one block.
33+
PersistentOrigins.insert(OID);
34+
}
35+
};
36+
37+
switch (F->getKind()) {
38+
case Fact::Kind::Issue:
39+
CheckOrigin(F->getAs<IssueFact>()->getOriginID());
40+
break;
41+
case Fact::Kind::OriginFlow: {
42+
const auto *OF = F->getAs<OriginFlowFact>();
43+
CheckOrigin(OF->getDestOriginID());
44+
CheckOrigin(OF->getSrcOriginID());
45+
break;
46+
}
47+
case Fact::Kind::ReturnOfOrigin:
48+
CheckOrigin(F->getAs<ReturnOfOriginFact>()->getReturnedOriginID());
49+
break;
50+
case Fact::Kind::Use:
51+
CheckOrigin(F->getAs<UseFact>()->getUsedOrigin(FactMgr.getOriginMgr()));
52+
break;
53+
case Fact::Kind::Expire:
54+
case Fact::Kind::TestPoint:
55+
break;
56+
}
57+
}
58+
}
59+
return PersistentOrigins;
60+
}
61+
1362
namespace {
63+
1464
/// Represents the dataflow lattice for loan propagation.
1565
///
1666
/// This lattice tracks which loans each origin may hold at a given program
@@ -20,21 +70,35 @@ namespace {
2070
/// 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);
73+
OriginLoanMap PersistentOrigins = OriginLoanMap(nullptr);
74+
OriginLoanMap BlockLocalOrigins = OriginLoanMap(nullptr);
2475

25-
explicit Lattice(const OriginLoanMap &S) : Origins(S) {}
76+
explicit Lattice(const OriginLoanMap &Persistent,
77+
const OriginLoanMap &BlockLocal)
78+
: PersistentOrigins(Persistent), BlockLocalOrigins(BlockLocal) {}
2679
Lattice() = default;
2780

2881
bool operator==(const Lattice &Other) const {
29-
return Origins == Other.Origins;
82+
return PersistentOrigins == Other.PersistentOrigins &&
83+
BlockLocalOrigins == Other.BlockLocalOrigins;
3084
}
3185
bool operator!=(const Lattice &Other) const { return !(*this == Other); }
3286

3387
void dump(llvm::raw_ostream &OS) const {
3488
OS << "LoanPropagationLattice State:\n";
35-
if (Origins.isEmpty())
89+
OS << " Persistent Origins:\n";
90+
if (PersistentOrigins.isEmpty())
3691
OS << " <empty>\n";
37-
for (const auto &Entry : Origins) {
92+
for (const auto &Entry : PersistentOrigins) {
93+
if (Entry.second.isEmpty())
94+
OS << " Origin " << Entry.first << " contains no loans\n";
95+
for (const LoanID &LID : Entry.second)
96+
OS << " Origin " << Entry.first << " contains Loan " << LID << "\n";
97+
}
98+
OS << " Block-Local Origins:\n";
99+
if (BlockLocalOrigins.isEmpty())
100+
OS << " <empty>\n";
101+
for (const auto &Entry : BlockLocalOrigins) {
38102
if (Entry.second.isEmpty())
39103
OS << " Origin " << Entry.first << " contains no loans\n";
40104
for (const LoanID &LID : Entry.second)
@@ -50,7 +114,8 @@ class AnalysisImpl
50114
OriginLoanMap::Factory &OriginLoanMapFactory,
51115
LoanSet::Factory &LoanSetFactory)
52116
: DataflowAnalysis(C, AC, F), OriginLoanMapFactory(OriginLoanMapFactory),
53-
LoanSetFactory(LoanSetFactory) {}
117+
LoanSetFactory(LoanSetFactory),
118+
PersistentOrigins(computePersistentOrigins(F, C)) {}
54119

55120
using Base::transfer;
56121

@@ -59,10 +124,9 @@ class AnalysisImpl
59124
Lattice getInitialState() { return Lattice{}; }
60125

61126
/// 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.
63127
Lattice join(Lattice A, Lattice B) {
64128
OriginLoanMap JoinedOrigins = utils::join(
65-
A.Origins, B.Origins, OriginLoanMapFactory,
129+
A.PersistentOrigins, B.PersistentOrigins, OriginLoanMapFactory,
66130
[&](const LoanSet *S1, const LoanSet *S2) {
67131
assert((S1 || S2) && "unexpectedly merging 2 empty sets");
68132
if (!S1)
@@ -74,16 +138,15 @@ class AnalysisImpl
74138
// Asymmetric join is a performance win. For origins present only on one
75139
// branch, the loan set can be carried over as-is.
76140
utils::JoinKind::Asymmetric);
77-
return Lattice(JoinedOrigins);
141+
return Lattice(JoinedOrigins, OriginLoanMapFactory.getEmptyMap());
78142
}
79143

80144
/// A new loan is issued to the origin. Old loans are erased.
81145
Lattice transfer(Lattice In, const IssueFact &F) {
82146
OriginID OID = F.getOriginID();
83147
LoanID LID = F.getLoanID();
84-
return Lattice(OriginLoanMapFactory.add(
85-
In.Origins, OID,
86-
LoanSetFactory.add(LoanSetFactory.getEmptySet(), LID)));
148+
LoanSet NewLoans = LoanSetFactory.add(LoanSetFactory.getEmptySet(), LID);
149+
return setLoans(In, OID, NewLoans);
87150
}
88151

89152
/// A flow from source to destination. If `KillDest` is true, this replaces
@@ -98,22 +161,38 @@ class AnalysisImpl
98161
LoanSet SrcLoans = getLoans(In, SrcOID);
99162
LoanSet MergedLoans = utils::join(DestLoans, SrcLoans, LoanSetFactory);
100163

101-
return Lattice(OriginLoanMapFactory.add(In.Origins, DestOID, MergedLoans));
164+
return setLoans(In, DestOID, MergedLoans);
102165
}
103166

104167
LoanSet getLoans(OriginID OID, ProgramPoint P) const {
105168
return getLoans(getState(P), OID);
106169
}
107170

108171
private:
172+
bool isPersistent(OriginID OID) const {
173+
return PersistentOrigins.contains(OID);
174+
}
175+
176+
Lattice setLoans(Lattice L, OriginID OID, LoanSet Loans) {
177+
if (isPersistent(OID)) {
178+
return Lattice(OriginLoanMapFactory.add(L.PersistentOrigins, OID, Loans),
179+
L.BlockLocalOrigins);
180+
}
181+
return Lattice(L.PersistentOrigins,
182+
OriginLoanMapFactory.add(L.BlockLocalOrigins, OID, Loans));
183+
}
184+
109185
LoanSet getLoans(Lattice L, OriginID OID) const {
110-
if (auto *Loans = L.Origins.lookup(OID))
186+
const OriginLoanMap *Map =
187+
isPersistent(OID) ? &L.PersistentOrigins : &L.BlockLocalOrigins;
188+
if (auto *Loans = Map->lookup(OID))
111189
return *Loans;
112190
return LoanSetFactory.getEmptySet();
113191
}
114192

115193
OriginLoanMap::Factory &OriginLoanMapFactory;
116194
LoanSet::Factory &LoanSetFactory;
195+
llvm::DenseSet<OriginID> PersistentOrigins;
117196
};
118197
} // namespace
119198

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)