clang 23.0.0git
LoanPropagation.cpp
Go to the documentation of this file.
1//===- LoanPropagation.cpp - Loan Propagation Analysis ---------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8#include <cassert>
9#include <memory>
10
11#include "Dataflow.h"
18#include "clang/Analysis/CFG.h"
19#include "clang/Basic/LLVM.h"
20#include "llvm/ADT/BitVector.h"
21#include "llvm/ADT/SmallVector.h"
22#include "llvm/Support/TimeProfiler.h"
23#include "llvm/Support/raw_ostream.h"
24
26
27// Prepass to find persistent origins. An origin is persistent if it is
28// referenced in more than one basic block.
29static llvm::BitVector computePersistentOrigins(const FactManager &FactMgr,
30 const CFG &C) {
31 llvm::TimeTraceScope("ComputePersistentOrigins");
32 unsigned NumOrigins = FactMgr.getOriginMgr().getNumOrigins();
33 llvm::BitVector PersistentOrigins(NumOrigins);
34
35 llvm::SmallVector<const CFGBlock *> OriginToFirstSeenBlock(NumOrigins,
36 nullptr);
37 for (const CFGBlock *B : C) {
38 for (const Fact *F : FactMgr.getFacts(B)) {
39 auto CheckOrigin = [&](OriginID OID) {
40 if (PersistentOrigins.test(OID.Value))
41 return;
42 auto &FirstSeenBlock = OriginToFirstSeenBlock[OID.Value];
43 if (FirstSeenBlock == nullptr)
44 FirstSeenBlock = B;
45 if (FirstSeenBlock != B) {
46 // We saw this origin in more than one block.
47 PersistentOrigins.set(OID.Value);
48 }
49 };
50
51 switch (F->getKind()) {
53 CheckOrigin(F->getAs<IssueFact>()->getOriginID());
54 break;
56 const auto *OF = F->getAs<OriginFlowFact>();
57 CheckOrigin(OF->getDestOriginID());
58 CheckOrigin(OF->getSrcOriginID());
59 break;
60 }
61 case Fact::Kind::Use:
62 for (const OriginList *Cur = F->getAs<UseFact>()->getUsedOrigins(); Cur;
63 Cur = Cur->peelOuterOrigin())
64 CheckOrigin(Cur->getOuterOriginID());
65 break;
67 CheckOrigin(F->getAs<KillOriginFact>()->getKilledOrigin());
68 break;
74 break;
75 }
76 }
77 }
78 return PersistentOrigins;
79}
80
81namespace {
82
83/// Represents the dataflow lattice for loan propagation.
84///
85/// This lattice tracks which loans each origin may hold at a given program
86/// point.The lattice has a finite height: An origin's loan set is bounded by
87/// the total number of loans in the function.
88struct Lattice {
89 /// The map from an origin to the set of loans it contains.
90 /// Origins that appear in multiple blocks. Participates in join operations.
91 OriginLoanMap PersistentOrigins = OriginLoanMap(nullptr);
92 /// Origins confined to a single block. Discarded at block boundaries.
93 OriginLoanMap BlockLocalOrigins = OriginLoanMap(nullptr);
94
95 explicit Lattice(const OriginLoanMap &Persistent,
96 const OriginLoanMap &BlockLocal)
97 : PersistentOrigins(Persistent), BlockLocalOrigins(BlockLocal) {}
98 Lattice() = default;
99
100 bool operator==(const Lattice &Other) const {
101 return PersistentOrigins == Other.PersistentOrigins &&
102 BlockLocalOrigins == Other.BlockLocalOrigins;
103 }
104 bool operator!=(const Lattice &Other) const { return !(*this == Other); }
105
106 void dump(llvm::raw_ostream &OS) const {
107 OS << "LoanPropagationLattice State:\n";
108 OS << " Persistent Origins:\n";
109 if (PersistentOrigins.isEmpty())
110 OS << " <empty>\n";
111 for (const auto &Entry : PersistentOrigins) {
112 if (Entry.second.isEmpty())
113 OS << " Origin " << Entry.first << " contains no loans\n";
114 for (const LoanID &LID : Entry.second)
115 OS << " Origin " << Entry.first << " contains Loan " << LID << "\n";
116 }
117 OS << " Block-Local Origins:\n";
118 if (BlockLocalOrigins.isEmpty())
119 OS << " <empty>\n";
120 for (const auto &Entry : BlockLocalOrigins) {
121 if (Entry.second.isEmpty())
122 OS << " Origin " << Entry.first << " contains no loans\n";
123 for (const LoanID &LID : Entry.second)
124 OS << " Origin " << Entry.first << " contains Loan " << LID << "\n";
125 }
126 }
127};
128
129class AnalysisImpl
130 : public DataflowAnalysis<AnalysisImpl, Lattice, Direction::Forward> {
131public:
132 AnalysisImpl(const CFG &C, AnalysisDeclContext &AC, FactManager &F,
133 OriginLoanMap::Factory &OriginLoanMapFactory,
134 LoanSet::Factory &LoanSetFactory)
135 : DataflowAnalysis(C, AC, F), OriginLoanMapFactory(OriginLoanMapFactory),
136 LoanSetFactory(LoanSetFactory),
137 PersistentOrigins(computePersistentOrigins(F, C)) {}
138
139 using Base::transfer;
140
141 StringRef getAnalysisName() const { return "LoanPropagation"; }
142
143 Lattice getInitialState() { return Lattice{}; }
144
145 /// Merges two lattices by taking the union of loans for each origin.
146 /// Only persistent origins are joined; block-local origins are discarded.
147 Lattice join(Lattice A, Lattice B) {
148 OriginLoanMap JoinedOrigins = utils::join(
149 A.PersistentOrigins, B.PersistentOrigins, OriginLoanMapFactory,
150 [&](const LoanSet *S1, const LoanSet *S2) {
151 assert((S1 || S2) && "unexpectedly merging 2 empty sets");
152 if (!S1)
153 return *S2;
154 if (!S2)
155 return *S1;
156 return utils::join(*S1, *S2, LoanSetFactory);
157 },
158 // Asymmetric join is a performance win. For origins present only on one
159 // branch, the loan set can be carried over as-is.
161 return Lattice(JoinedOrigins, OriginLoanMapFactory.getEmptyMap());
162 }
163
164 /// A new loan is issued to the origin. Old loans are erased.
165 Lattice transfer(Lattice In, const IssueFact &F) {
166 OriginID OID = F.getOriginID();
167 LoanID LID = F.getLoanID();
168 LoanSet NewLoans = LoanSetFactory.add(LoanSetFactory.getEmptySet(), LID);
169 return setLoans(In, OID, NewLoans);
170 }
171
172 /// A flow from source to destination. If `KillDest` is true, this replaces
173 /// the destination's loans with the source's. Otherwise, the source's loans
174 /// are merged into the destination's.
175 Lattice transfer(Lattice In, const OriginFlowFact &F) {
176 OriginID DestOID = F.getDestOriginID();
177 OriginID SrcOID = F.getSrcOriginID();
178
179 LoanSet DestLoans =
180 F.getKillDest() ? LoanSetFactory.getEmptySet() : getLoans(In, DestOID);
181 LoanSet SrcLoans = getLoans(In, SrcOID);
182 LoanSet MergedLoans = utils::join(DestLoans, SrcLoans, LoanSetFactory);
183
184 return setLoans(In, DestOID, MergedLoans);
185 }
186
187 Lattice transfer(Lattice In, const KillOriginFact &F) {
188 return setLoans(In, F.getKilledOrigin(), LoanSetFactory.getEmptySet());
189 }
190
191 Lattice transfer(Lattice In, const ExpireFact &F) {
192 if (auto OID = F.getOriginID())
193 return setLoans(In, *OID, LoanSetFactory.getEmptySet());
194 return In;
195 }
196
197 LoanSet getLoans(OriginID OID, ProgramPoint P) const {
198 return getLoans(getState(P), OID);
199 }
200
201private:
202 /// Returns true if the origin is persistent (referenced in multiple blocks).
203 bool isPersistent(OriginID OID) const {
204 return PersistentOrigins.test(OID.Value);
205 }
206
207 Lattice setLoans(Lattice L, OriginID OID, LoanSet Loans) {
208 if (isPersistent(OID))
209 return Lattice(OriginLoanMapFactory.add(L.PersistentOrigins, OID, Loans),
210 L.BlockLocalOrigins);
211 return Lattice(L.PersistentOrigins,
212 OriginLoanMapFactory.add(L.BlockLocalOrigins, OID, Loans));
213 }
214
215 LoanSet getLoans(Lattice L, OriginID OID) const {
216 const OriginLoanMap *Map =
217 isPersistent(OID) ? &L.PersistentOrigins : &L.BlockLocalOrigins;
218 if (auto *Loans = Map->lookup(OID))
219 return *Loans;
220 return LoanSetFactory.getEmptySet();
221 }
222
223 OriginLoanMap::Factory &OriginLoanMapFactory;
224 LoanSet::Factory &LoanSetFactory;
225 /// Boolean vector indexed by origin ID. If true, the origin appears in
226 /// multiple basic blocks and must participate in join operations. If false,
227 /// the origin is block-local and can be discarded at block boundaries.
228 llvm::BitVector PersistentOrigins;
229};
230} // namespace
231
232class LoanPropagationAnalysis::Impl final : public AnalysisImpl {
233 using AnalysisImpl::AnalysisImpl;
234};
235
237 const CFG &C, AnalysisDeclContext &AC, FactManager &F,
238 OriginLoanMap::Factory &OriginLoanMapFactory,
239 LoanSet::Factory &LoanSetFactory)
240 : PImpl(std::make_unique<Impl>(C, AC, F, OriginLoanMapFactory,
241 LoanSetFactory)) {
242 PImpl->run();
243}
244
246
248 return PImpl->getLoans(OID, P);
249}
250} // namespace clang::lifetimes::internal
This file defines AnalysisDeclContext, a class that manages the analysis context data for context sen...
static void dump(llvm::raw_ostream &OS, StringRef FunctionName, ArrayRef< CounterExpression > Expressions, ArrayRef< CounterMappingRegion > Regions)
Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified.
AnalysisDeclContext contains the context data for the function, method or block under analysis.
Represents a single basic block in a source-level CFG.
Definition CFG.h:632
Represents a source-level, intra-procedural CFG that represents the control-flow of a Stmt.
Definition CFG.h:1250
A generic, policy-based driver for dataflow analyses.
Definition Dataflow.h:57
llvm::ArrayRef< const Fact * > getFacts(const CFGBlock *B) const
Definition Facts.h:345
An abstract base class for a single, atomic lifetime-relevant event.
Definition Facts.h:34
@ InvalidateOrigin
An origin is invalidated (e.g. vector resized).
Definition Facts.h:57
@ TestPoint
A marker for a specific point in the code, for testing.
Definition Facts.h:53
@ Expire
A loan expires as its underlying storage is freed (e.g., variable goes out of scope).
Definition Facts.h:42
@ Issue
A new loan is issued from a borrow expression (e.g., &x).
Definition Facts.h:39
@ OriginFlow
An origin is propagated from a source to a destination (e.g., p = q).
Definition Facts.h:47
@ MovedOrigin
An origin that is moved (e.g., passed to an rvalue reference parameter).
Definition Facts.h:51
@ Use
An origin is used (eg. appears as l-value expression like DeclRefExpr).
Definition Facts.h:49
@ OriginEscapes
An origin that escapes the function scope (e.g., via return).
Definition Facts.h:55
@ KillOrigin
All loans of an origin are cleared.
Definition Facts.h:59
All loans are cleared from an origin (e.g., assigning a callable without tracked origins to std::func...
Definition Facts.h:323
LoanSet getLoans(OriginID OID, ProgramPoint P) const
LoanPropagationAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F, OriginLoanMap::Factory &OriginLoanMapFactory, LoanSet::Factory &LoanSetFactory)
A list of origins representing levels of indirection for pointer-like types.
Definition Origins.h:95
OriginList * peelOuterOrigin() const
Definition Origins.h:99
const OriginList * getUsedOrigins() const
Definition Facts.h:250
void transfer(const StmtToEnvMap &StmtToEnv, const Stmt &S, Environment &Env, Environment::ValueModel &Model)
Evaluates S and updates Env accordingly.
Definition Transfer.cpp:986
@ OS
Indicates that the tracking object is a descendant of a referenced-counted OSObject,...
@ Asymmetric
An asymmetric join preserves keys unique to the first map as-is, while applying the JoinValues operat...
Definition Utils.h:62
static llvm::ImmutableSet< T > join(llvm::ImmutableSet< T > A, llvm::ImmutableSet< T > B, typename llvm::ImmutableSet< T >::Factory &F)
Computes the union of two ImmutableSets.
Definition Utils.h:39
const Fact * ProgramPoint
A ProgramPoint identifies a location in the CFG by pointing to a specific Fact.
Definition Facts.h:91
llvm::ImmutableSet< LoanID > LoanSet
utils::ID< struct LoanTag > LoanID
Definition Loans.h:25
utils::ID< struct OriginTag > OriginID
Definition Origins.h:28
llvm::ImmutableMap< OriginID, LoanSet > OriginLoanMap
static llvm::BitVector computePersistentOrigins(const FactManager &FactMgr, const CFG &C)
bool operator==(const CallGraphNode::CallRecord &LHS, const CallGraphNode::CallRecord &RHS)
Definition CallGraph.h:206
bool operator!=(CanQual< T > x, CanQual< U > y)
@ Other
Other implicit parameter.
Definition Decl.h:1761