20#include "llvm/ADT/BitVector.h"
21#include "llvm/ADT/SmallSet.h"
22#include "llvm/ADT/SmallVector.h"
23#include "llvm/Support/TimeProfiler.h"
24#include "llvm/Support/raw_ostream.h"
32 llvm::TimeTraceScope(
"ComputePersistentOrigins");
34 llvm::BitVector PersistentOrigins(NumOrigins);
40 auto CheckOrigin = [&](
OriginID OID) {
41 if (PersistentOrigins.test(OID.Value))
43 auto &FirstSeenBlock = OriginToFirstSeenBlock[OID.Value];
44 if (FirstSeenBlock ==
nullptr)
46 if (FirstSeenBlock != B) {
48 PersistentOrigins.set(OID.Value);
52 switch (F->getKind()) {
58 CheckOrigin(OF->getDestOriginID());
59 CheckOrigin(OF->getSrcOriginID());
65 CheckOrigin(Cur->getOuterOriginID());
83 return PersistentOrigins;
102 : PersistentOrigins(Persistent), BlockLocalOrigins(BlockLocal) {}
106 return PersistentOrigins ==
Other.PersistentOrigins &&
107 BlockLocalOrigins ==
Other.BlockLocalOrigins;
111 void dump(llvm::raw_ostream &OS)
const {
112 OS <<
"LoanPropagationLattice State:\n";
113 OS <<
" Persistent Origins:\n";
114 if (PersistentOrigins.isEmpty())
116 for (
const auto &Entry : PersistentOrigins) {
117 if (Entry.second.isEmpty())
118 OS <<
" Origin " << Entry.first <<
" contains no loans\n";
119 for (
const LoanID &LID : Entry.second)
120 OS <<
" Origin " << Entry.first <<
" contains Loan " << LID <<
"\n";
122 OS <<
" Block-Local Origins:\n";
123 if (BlockLocalOrigins.isEmpty())
125 for (
const auto &Entry : BlockLocalOrigins) {
126 if (Entry.second.isEmpty())
127 OS <<
" Origin " << Entry.first <<
" contains no loans\n";
128 for (
const LoanID &LID : Entry.second)
129 OS <<
" Origin " << Entry.first <<
" contains Loan " << LID <<
"\n";
137 AnalysisImpl(
const CFG &
C, AnalysisDeclContext &AC, FactManager &F,
138 OriginLoanMap::Factory &OriginLoanMapFactory,
139 LoanSet::Factory &LoanSetFactory)
140 : DataflowAnalysis(
C, AC, F), OriginLoanMapFactory(OriginLoanMapFactory),
141 LoanSetFactory(LoanSetFactory),
144 using Base::transfer;
146 StringRef getAnalysisName()
const {
return "LoanPropagation"; }
148 Lattice getInitialState() {
return Lattice{}; }
152 Lattice
join(Lattice A, Lattice B) {
154 A.PersistentOrigins, B.PersistentOrigins, OriginLoanMapFactory,
156 assert((S1 || S2) &&
"unexpectedly merging 2 empty sets");
161 return utils::join(*S1, *S2, LoanSetFactory);
166 return Lattice(JoinedOrigins, OriginLoanMapFactory.getEmptyMap());
170 Lattice
transfer(Lattice In,
const IssueFact &F) {
172 LoanID LID = F.getLoanID();
173 LoanSet NewLoans = LoanSetFactory.add(LoanSetFactory.getEmptySet(), LID);
174 return setLoans(In, OID, NewLoans);
180 Lattice
transfer(Lattice In,
const OriginFlowFact &F) {
181 OriginID DestOID = F.getDestOriginID();
182 OriginID SrcOID = F.getSrcOriginID();
185 F.getKillDest() ? LoanSetFactory.getEmptySet() : getLoans(In, DestOID);
186 LoanSet SrcLoans = getLoans(In, SrcOID);
189 return setLoans(In, DestOID, MergedLoans);
192 Lattice
transfer(Lattice In,
const KillOriginFact &F) {
193 return setLoans(In, F.getKilledOrigin(), LoanSetFactory.getEmptySet());
196 Lattice
transfer(Lattice In,
const ExpireFact &F) {
197 if (
auto OID = F.getOriginID())
198 return setLoans(In, *OID, LoanSetFactory.getEmptySet());
203 return getLoans(getState(P), OID);
206 llvm::SmallVector<OriginID> buildOriginFlowChain(
ProgramPoint StartPoint,
209 const CFG *Cfg)
const {
210 assert(getLoans(StartOID, StartPoint).
contains(TargetLoan) &&
211 "TargetLoan must be present in the StartOID at the StartPoint");
214 const CFGBlock *EndBlock =
nullptr;
215 size_t BlockID = FactMgr.getBlockID(StartPoint);
216 for (
const CFGBlock *
Block : *Cfg)
217 if (
Block->getBlockID() == BlockID) {
225 using SearchState = std::pair<const CFGBlock *, OriginID>;
227 SearchState CurrState;
228 llvm::SmallVector<OriginID> OriginFlowChain;
231 llvm::SmallVector<DFSNode> PendingStates;
232 llvm::SmallSet<SearchState, 16> VistedStates;
233 PendingStates.push_back({{EndBlock, StartOID}, {}});
236 while (!PendingStates.empty()) {
237 DFSNode CurrNode = PendingStates.pop_back_val();
238 auto [CurrBlock, CurrOID] = CurrNode.CurrState;
241 const auto [BuildResult,
Complete] =
242 buildOriginFlowChain(CurrBlock, CurrOID, TargetLoan);
243 if (!BuildResult.empty()) {
244 CurrNode.OriginFlowChain.append(BuildResult);
245 CurrOID = BuildResult.back();
250 return CurrNode.OriginFlowChain;
254 for (
const CFGBlock *PredBlock : CurrBlock->preds()) {
255 SearchState NextState = {PredBlock, CurrOID};
256 if (getLoans(getOutState(PredBlock), CurrOID).
contains(TargetLoan) &&
257 VistedStates.insert(NextState).second)
258 PendingStates.push_back({NextState, CurrNode.OriginFlowChain});
263 "buildOriginFlowChain did not reach IssueFact for TargetLoan");
266 llvm::SmallVector<OriginID> buildOriginFlowChain(
const UseFact *UF,
268 const CFG *Cfg)
const {
269 for (
const OriginList *Cur = UF->getUsedOrigins(); Cur;
270 Cur = Cur->peelOuterOrigin())
271 if (getLoans(Cur->getOuterOriginID(), UF).contains(TargetLoan))
272 return buildOriginFlowChain(UF, Cur->getOuterOriginID(), TargetLoan,
280 bool isPersistent(
OriginID OID)
const {
281 return PersistentOrigins.test(OID.Value);
285 if (isPersistent(OID))
286 return Lattice(OriginLoanMapFactory.add(L.PersistentOrigins, OID, Loans),
287 L.BlockLocalOrigins);
288 return Lattice(L.PersistentOrigins,
289 OriginLoanMapFactory.add(L.BlockLocalOrigins, OID, Loans));
294 isPersistent(OID) ? &L.PersistentOrigins : &L.BlockLocalOrigins;
295 if (
auto *Loans = Map->lookup(OID))
297 return LoanSetFactory.getEmptySet();
308 std::pair<llvm::SmallVector<OriginID>,
bool>
309 buildOriginFlowChain(
const CFGBlock *
Block,
const OriginID StartOID,
310 const LoanID TargetLoan)
const {
312 llvm::SmallVector<OriginID> OriginFlowChain;
314 for (
const Fact *F : llvm::reverse(FactMgr.getFacts(
Block))) {
315 if (
const auto *IF = F->getAs<IssueFact>())
316 if (IF->getLoanID() == TargetLoan && IF->getOriginID() == CurrOID)
317 return {OriginFlowChain,
true};
319 const auto *OFF = F->getAs<OriginFlowFact>();
320 if (!OFF || OFF->getDestOriginID() != CurrOID)
323 const OriginID SrcOriginID = OFF->getSrcOriginID();
324 if (!getLoans(SrcOriginID, OFF).
contains(TargetLoan))
327 OriginFlowChain.push_back(SrcOriginID);
328 CurrOID = SrcOriginID;
331 return {OriginFlowChain,
false};
334 OriginLoanMap::Factory &OriginLoanMapFactory;
335 LoanSet::Factory &LoanSetFactory;
339 llvm::BitVector PersistentOrigins;
344 using AnalysisImpl::AnalysisImpl;
349 OriginLoanMap::Factory &OriginLoanMapFactory,
350 LoanSet::Factory &LoanSetFactory)
351 : PImpl(
std::make_unique<
Impl>(
C, AC, F, OriginLoanMapFactory,
359 return PImpl->getLoans(OID, P);
364 const CFG *Cfg)
const {
365 return PImpl->buildOriginFlowChain(StartPoint, StartOID, TargetLoan, Cfg);
370 return PImpl->buildOriginFlowChain(UF, TargetLoan, Cfg);
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.
static bool contains(const std::set< tok::TokenKind > &Terminators, const Token &Tok)
AnalysisDeclContext contains the context data for the function, method or block under analysis.
Represents a single basic block in a source-level CFG.
Represents a source-level, intra-procedural CFG that represents the control-flow of a Stmt.
A generic, policy-based driver for dataflow analyses.
llvm::ArrayRef< const Fact * > getFacts(const CFGBlock *B) const
OriginManager & getOriginMgr()
An abstract base class for a single, atomic lifetime-relevant event.
@ InvalidateOrigin
An origin is invalidated (e.g. vector resized, delete called).
@ TestPoint
A marker for a specific point in the code, for testing.
@ Expire
A loan expires as its underlying storage is freed (e.g., variable goes out of scope).
@ Issue
A new loan is issued from a borrow expression (e.g., &x).
@ OriginFlow
An origin is propagated from a source to a destination (e.g., p = q).
@ MovedOrigin
An origin that is moved (e.g., passed to an rvalue reference parameter).
@ Use
An origin is used (eg. appears as l-value expression like DeclRefExpr).
@ OriginEscapes
An origin that escapes the function scope (e.g., via return).
@ KillOrigin
All loans of an origin are cleared.
OriginID getOriginID() const
All loans are cleared from an origin (e.g., assigning a callable without tracked origins to std::func...
OriginID getKilledOrigin() const
llvm::SmallVector< OriginID > buildOriginFlowChain(ProgramPoint StartPoint, const OriginID StartOID, const LoanID TargetLoan, const CFG *Cfg) const
Builds the chain of origins through which a loan has propagated.
LoanSet getLoans(OriginID OID, ProgramPoint P) const
~LoanPropagationAnalysis()
LoanPropagationAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F, OriginLoanMap::Factory &OriginLoanMapFactory, LoanSet::Factory &LoanSetFactory)
Represents that an origin escapes the current scope through various means.
OriginID getEscapedOriginID() const
A list of origins representing levels of indirection for pointer-like types.
OriginList * peelOuterOrigin() const
unsigned getNumOrigins() const
const OriginList * getUsedOrigins() const
BlockID
The various types of blocks that can occur within a API notes file.
void transfer(const StmtToEnvMap &StmtToEnv, const Stmt &S, Environment &Env, Environment::ValueModel &Model)
Evaluates S and updates Env accordingly.
@ 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...
llvm::ImmutableSet< T > join(llvm::ImmutableSet< T > A, llvm::ImmutableSet< T > B, typename llvm::ImmutableSet< T >::Factory &F)
Computes the union of two ImmutableSets.
const Fact * ProgramPoint
A ProgramPoint identifies a location in the CFG by pointing to a specific Fact.
llvm::ImmutableSet< LoanID > LoanSet
utils::ID< struct LoanTag > LoanID
utils::ID< struct OriginTag > OriginID
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)
bool operator!=(CanQual< T > x, CanQual< U > y)
@ Other
Other implicit parameter.