clang 24.0.0git
LiveOrigins.cpp
Go to the documentation of this file.
1//===- LiveOrigins.cpp - Live Origins 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
10#include "Dataflow.h"
12#include "llvm/Support/ErrorHandling.h"
13
15namespace {
16
17/// The dataflow lattice for origin liveness analysis.
18/// It tracks which origins are live, why they're live (which UseFact),
19/// and the confidence level of that liveness.
20struct Lattice {
21 /// Origins referenced from more than one block. Participates in joins.
22 LivenessMap Persistent;
23 /// Origins confined to a single block. Discarded at block boundaries.
24 LivenessMap BlockLocal;
25
26 Lattice() : Persistent(nullptr), BlockLocal(nullptr) {};
27
28 Lattice(LivenessMap Persistent, LivenessMap BlockLocal)
29 : Persistent(Persistent), BlockLocal(BlockLocal) {}
30
31 bool operator==(const Lattice &Other) const {
32 return Persistent == Other.Persistent && BlockLocal == Other.BlockLocal;
33 }
34
35 bool operator!=(const Lattice &Other) const { return !(*this == Other); }
36
37 void dump(llvm::raw_ostream &OS, const OriginManager &OM) const {
38 if (Persistent.isEmpty() && BlockLocal.isEmpty())
39 OS << " <empty>\n";
40 for (const LivenessMap &Live : {Persistent, BlockLocal})
41 for (const auto &Entry : Live) {
42 OriginID OID = Entry.first;
43 const LivenessInfo &Info = Entry.second;
44 OS << " ";
45 OM.dump(OID, OS);
46 OS << " is ";
47 switch (Info.Kind) {
49 OS << "definitely";
50 break;
52 OS << "maybe";
53 break;
55 llvm_unreachable("liveness kind of live origins should not be dead.");
56 }
57 OS << " live at this point\n";
58 }
59 }
60};
61
62static SourceLocation GetFactLoc(CausingFactType F) {
63 if (const auto *UF = F.dyn_cast<const UseFact *>())
64 return UF->getUseExpr()->getExprLoc();
65 if (const auto *OEF = F.dyn_cast<const OriginEscapesFact *>()) {
66 if (auto *ReturnEsc = dyn_cast<ReturnEscapeFact>(OEF))
67 return ReturnEsc->getReturnExpr()->getExprLoc();
68 if (auto *FieldEsc = dyn_cast<FieldEscapeFact>(OEF))
69 return FieldEsc->getFieldDecl()->getLocation();
70 if (auto *GlobalEsc = dyn_cast<GlobalEscapeFact>(OEF))
71 return GlobalEsc->getGlobal()->getLocation();
72 }
73 llvm_unreachable("unhandled causing fact in PointerUnion");
74}
75
76/// The analysis that tracks which origins are live, with granular information
77/// about the causing use fact and confidence level. This is a backward
78/// analysis.
79class AnalysisImpl
80 : public DataflowAnalysis<AnalysisImpl, Lattice, Direction::Backward> {
81
82public:
83 AnalysisImpl(const CFG &C, AnalysisDeclContext &AC, FactManager &F,
84 LivenessMap::Factory &SF)
85 : DataflowAnalysis(C, AC, F), FactMgr(F), Factory(SF),
86 PersistentOrigins(F.getPersistentOrigins()) {}
87 using DataflowAnalysis<AnalysisImpl, Lattice, Direction::Backward>::transfer;
88
89 StringRef getAnalysisName() const { return "LiveOrigins"; }
90
91 Lattice getInitialState() {
92 return Lattice(Factory.getEmptyMap(), Factory.getEmptyMap());
93 }
94
95 /// An origin referenced from a single block need not be live outside it.
96 ///
97 /// Loans only ever enter an origin through an `IssueFact` or an
98 /// `OriginFlowFact` naming it as the destination, and both would make the
99 /// origin persistent if they lived in another block. So a block-local origin
100 /// holds no loans anywhere outside its own block, and every consumer of
101 /// liveness intersects it with the origin's loans. Dropping these here keeps
102 /// them out of the joins and out of the block-entry state comparison.
103 Lattice transferAtBlockExit(Lattice L) {
104 return Lattice(L.Persistent, Factory.getEmptyMap());
105 }
106
107 /// Merges two lattices by combining liveness information.
108 /// When the same origin has different confidence levels, we take the lower
109 /// one.
110 Lattice join(Lattice L1, Lattice L2) const {
111 assert(L1.BlockLocal.isEmpty() && L2.BlockLocal.isEmpty() &&
112 "block-local origins must not reach a block boundary");
113 // Take the earliest Fact to make the join hermetic and commutative.
114 auto CombineCausingFact = [](CausingFactType A,
116 if (!A)
117 return B;
118 if (!B)
119 return A;
120 return GetFactLoc(A) < GetFactLoc(B) ? A : B;
121 };
122 auto CombineLivenessKind = [](LivenessKind K1,
124 assert(K1 != LivenessKind::Dead && "LivenessKind should not be dead.");
125 assert(K2 != LivenessKind::Dead && "LivenessKind should not be dead.");
126 // Only return "Must" if both paths are "Must", otherwise Maybe.
127 if (K1 == LivenessKind::Must && K2 == LivenessKind::Must)
128 return LivenessKind::Must;
129 return LivenessKind::Maybe;
130 };
131 auto CombineLivenessInfo = [&](const LivenessInfo *L1,
132 const LivenessInfo *L2) -> LivenessInfo {
133 assert((L1 || L2) && "unexpectedly merging 2 empty sets");
134 if (!L1)
135 return LivenessInfo(L2->CausingFact, LivenessKind::Maybe);
136 if (!L2)
137 return LivenessInfo(L1->CausingFact, LivenessKind::Maybe);
138 return LivenessInfo(CombineCausingFact(L1->CausingFact, L2->CausingFact),
139 CombineLivenessKind(L1->Kind, L2->Kind));
140 };
141 // A symmetric join is required here. If an origin is live on one branch but
142 // not the other, its confidence must be demoted to `Maybe`.
143 LivenessMap Joined =
144 utils::join(L1.Persistent, L2.Persistent, Factory, CombineLivenessInfo,
146 return Lattice(Joined, Factory.getEmptyMap());
147 }
148
149 /// A read operation makes the origin live with definite confidence, as it
150 /// dominates this program point. A write operation kills the liveness of
151 /// the origin since it overwrites the value.
152 Lattice transfer(Lattice In, const UseFact &UF) {
153 Lattice Out = In;
154 for (const OriginList *Cur = UF.getUsedOrigins(); Cur;
155 Cur = Cur->peelOuterOrigin()) {
156 OriginID OID = Cur->getOuterOriginID();
157 // Write kills liveness.
158 if (UF.isWritten())
159 Out = removeLive(Out, OID);
160 else
161 // Read makes origin live with definite confidence (dominates this
162 // point).
163 Out = addLive(Out, OID, LivenessInfo(&UF, LivenessKind::Must));
164 }
165 return Out;
166 }
167
168 /// An escaping origin (e.g., via return) makes the origin live with definite
169 /// confidence, as it dominates this program point.
170 Lattice transfer(Lattice In, const OriginEscapesFact &OEF) {
171 return addLive(In, OEF.getEscapedOriginID(),
172 LivenessInfo(&OEF, LivenessKind::Must));
173 }
174
175 /// Issuing a new loan to an origin kills its liveness.
176 Lattice transfer(Lattice In, const IssueFact &IF) {
177 return removeLive(In, IF.getOriginID());
178 }
179
180 /// An OriginFlow kills the liveness of the destination origin if `KillDest`
181 /// is true. Otherwise, it propagates liveness from destination to source.
182 Lattice transfer(Lattice In, const OriginFlowFact &OF) {
183 Lattice Out = In;
184 OriginID Dest = OF.getDestOriginID();
185 // If the destination of the flow is live, the source of the flow must also
186 // be marked live before this point as its value will flow into the
187 // destination.
188 if (const LivenessInfo *DestInfo = lookupLive(In, Dest))
189 Out = addLive(Out, OF.getSrcOriginID(), *DestInfo);
190 if (OF.getKillDest())
191 Out = removeLive(Out, Dest);
192 return Out;
193 }
194
195 Lattice transfer(Lattice In, const KillOriginFact &F) {
196 return removeLive(In, F.getKilledOrigin());
197 }
198
199 Lattice transfer(Lattice In, const ExpireFact &F) {
200 if (auto OID = F.getOriginID())
201 return removeLive(In, *OID);
202 return In;
203 }
204
205 LiveOriginSet getLiveOriginsAt(ProgramPoint P) const {
206 Lattice L = getState(P);
207 return LiveOriginSet{L.Persistent, L.BlockLocal};
208 }
209
210 // Dump liveness values on all test points in the program.
211 void dump(llvm::raw_ostream &OS,
212 const llvm::StringMap<ProgramPoint> &TestPoints) const {
213 llvm::dbgs() << "==========================================\n";
214 llvm::dbgs() << getAnalysisName() << " results:\n";
215 llvm::dbgs() << "==========================================\n";
216 for (const auto &Entry : TestPoints) {
217 OS << "TestPoint: " << Entry.getKey() << "\n";
218 getState(Entry.getValue()).dump(OS, FactMgr.getOriginMgr());
219 }
220 }
221
222private:
223 /// Routes an origin to the half of the lattice it belongs to.
224 bool isPersistent(OriginID OID) const {
225 return PersistentOrigins.test(OID.Value);
226 }
227
228 Lattice addLive(Lattice L, OriginID OID, LivenessInfo Info) {
229 if (isPersistent(OID))
230 return Lattice(Factory.add(L.Persistent, OID, Info), L.BlockLocal);
231 return Lattice(L.Persistent, Factory.add(L.BlockLocal, OID, Info));
232 }
233
234 Lattice removeLive(Lattice L, OriginID OID) {
235 if (isPersistent(OID))
236 return Lattice(Factory.remove(L.Persistent, OID), L.BlockLocal);
237 return Lattice(L.Persistent, Factory.remove(L.BlockLocal, OID));
238 }
239
240 const LivenessInfo *lookupLive(const Lattice &L, OriginID OID) const {
241 return isPersistent(OID) ? L.Persistent.lookup(OID)
242 : L.BlockLocal.lookup(OID);
243 }
244
245 FactManager &FactMgr;
246 LivenessMap::Factory &Factory;
247 /// Origins referenced from more than one basic block; see
248 /// `FactManager::getPersistentOrigins`.
249 const llvm::BitVector &PersistentOrigins;
250};
251} // namespace
252
253// PImpl wrapper implementation
254class LiveOriginsAnalysis::Impl : public AnalysisImpl {
255 using AnalysisImpl::AnalysisImpl;
256};
257
259 FactManager &F,
260 LivenessMap::Factory &SF)
261 : PImpl(std::make_unique<Impl>(C, AC, F, SF)) {
262 PImpl->run();
263}
264
266
268 return PImpl->getLiveOriginsAt(P);
269}
270
272 llvm::raw_ostream &OS,
273 const llvm::StringMap<ProgramPoint> &TestPoints) const {
274 PImpl->dump(OS, TestPoints);
275}
276} // namespace clang::lifetimes::internal
static void dump(llvm::raw_ostream &OS, StringRef FunctionName, ArrayRef< CounterExpression > Expressions, ArrayRef< CounterMappingRegion > Regions)
AnalysisDeclContext contains the context data for the function, method or block under analysis.
Represents a source-level, intra-procedural CFG that represents the control-flow of a Stmt.
Definition CFG.h:1271
A generic, policy-based driver for dataflow analyses.
Definition Dataflow.h:60
LiveOriginSet getLiveOriginsAt(ProgramPoint P) const
Returns the set of origins that are live at a specific program point, along with the the details of t...
LiveOriginsAnalysis(const CFG &C, AnalysisDeclContext &AC, FactManager &F, LivenessMap::Factory &SF)
void dump(llvm::raw_ostream &OS, const llvm::StringMap< ProgramPoint > &TestPoints) const
Represents that an origin escapes the current scope through various means.
Definition Facts.h:168
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,...
@ Symmetric
A symmetric join applies the JoinValues operation to keys unique to either map, ensuring that values ...
Definition Utils.h:63
SetTy< T > join(SetTy< T > A, SetTy< T > B, typename SetTy< T >::Factory &F)
Computes the union of two ImmutableSets.
Definition Utils.h:49
const Fact * ProgramPoint
A ProgramPoint identifies a location in the CFG by pointing to a specific Fact.
Definition Facts.h:98
utils::ID< struct OriginTag > OriginID
Definition Origins.h:28
::llvm::PointerUnion< const UseFact *, const OriginEscapesFact * > CausingFactType
Definition LiveOrigins.h:34
utils::MapTy< OriginID, LivenessInfo > LivenessMap
Definition LiveOrigins.h:76
bool operator==(const CallGraphNode::CallRecord &LHS, const CallGraphNode::CallRecord &RHS)
Definition CallGraph.h:218
nullptr
This class represents a compute construct, representing a 'Kind' of ‘parallel’, 'serial',...
bool operator!=(CanQual< T > x, CanQual< U > y)
@ Other
Other implicit parameter.
Definition Decl.h:1774
The origins that are live at a program point.
Definition LiveOrigins.h:86