Skip to content

Commit e081091

Browse files
authored
Merge pull request #41826 from atrick/fix-recursive-closure
Rewrite ClosureScopeAnalysis for generality.
2 parents 14fb9a0 + ddf0965 commit e081091

File tree

10 files changed

+529
-211
lines changed

10 files changed

+529
-211
lines changed

include/swift/SIL/ApplySite.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,10 @@ class ApplySite {
169169
FOREACH_IMPL_RETURN(getCalleeFunction());
170170
}
171171

172+
bool isCalleeDynamicallyReplaceable() const {
173+
FOREACH_IMPL_RETURN(isCalleeDynamicallyReplaceable());
174+
}
175+
172176
/// Return the referenced function if the callee is a function_ref
173177
/// instruction.
174178
SILFunction *getReferencedFunctionOrNull() const {

include/swift/SIL/SILFunction.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,10 @@ class SILFunction
224224
/// For details see BasicBlockBitfield::bitfieldID;
225225
unsigned currentBitfieldID = 1;
226226

227+
/// Unique identifier for vector indexing and deterministic sorting.
228+
/// May be reused when zombie functions are recovered.
229+
unsigned index;
230+
227231
/// The function's set of semantics attributes.
228232
///
229233
/// TODO: Why is this using a std::string? Why don't we use uniqued
@@ -446,6 +450,8 @@ class SILFunction
446450
return SILFunctionConventions(fnType, getModule());
447451
}
448452

453+
unsigned getIndex() const { return index; }
454+
449455
SILProfiler *getProfiler() const { return Profiler; }
450456

451457
SILFunction *getDynamicallyReplacedFunction() const {

include/swift/SIL/SILModule.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,10 @@ class SILModule {
345345
/// The options passed into this SILModule.
346346
const SILOptions &Options;
347347

348+
/// The number of functions created in this module, which will be the index of
349+
/// the next function.
350+
unsigned nextFunctionIndex = 0;
351+
348352
/// Set if the SILModule was serialized already. It is used
349353
/// to ensure that the module is serialized only once.
350354
bool serialized;
@@ -449,6 +453,12 @@ class SILModule {
449453
/// Called after an instruction is moved from one function to another.
450454
void notifyMovedInstruction(SILInstruction *inst, SILFunction *fromFunction);
451455

456+
unsigned getNewFunctionIndex() { return nextFunctionIndex++; }
457+
458+
// This may be larger that the number of live functions in the 'functions'
459+
// linked list because it includes the indices of zombie functions.
460+
unsigned getNumFunctionIndices() const { return nextFunctionIndex; }
461+
452462
/// Set a serialization action.
453463
void setSerializeSILAction(ActionCallback SerializeSILAction);
454464
ActionCallback getSerializeSILAction() const;

include/swift/SILOptimizer/Analysis/ClosureScope.h

Lines changed: 54 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252

5353
#include "swift/Basic/BlotSetVector.h"
5454
#include "swift/SIL/SILFunction.h"
55+
#include "swift/SIL/SILModule.h"
5556
#include "swift/SILOptimizer/Analysis/Analysis.h"
5657
#include "llvm/ADT/SmallSet.h"
5758
#include "llvm/ADT/iterator.h"
@@ -80,40 +81,15 @@ inline bool isNonEscapingClosure(CanSILFunctionType funcTy) {
8081
return llvm::any_of(funcTy->getParameters(), isInoutAliasable);
8182
}
8283

83-
class ClosureScopeData;
84+
class ClosureGraph;
8485

8586
class ClosureScopeAnalysis : public SILAnalysis {
86-
friend class ClosureScopeData;
87-
88-
// Get a closure's scope function from its index. This functor is compatible
89-
// with OptionalTransformRange. Unfortunately it exposes the internals of the
90-
// analysis data.
91-
struct IndexLookupFunc {
92-
// A reference to all closure parent scopes ordered by their index.
93-
const std::vector<SILFunction *> &indexedScopes;
94-
95-
IndexLookupFunc(const std::vector<SILFunction *> &indexedScopes)
96-
: indexedScopes(indexedScopes) {}
97-
98-
Optional<SILFunction *> operator()(int idx) const {
99-
if (auto funcPtr = indexedScopes[idx]) {
100-
return funcPtr;
101-
}
102-
return None;
103-
}
104-
};
105-
using IndexRange = iterator_range<int *>;
106-
107-
public:
108-
// A range of SILFunction scopes converted from their scope indices and
109-
// filtered to remove any erased functions.
110-
using ScopeRange = OptionalTransformRange<IndexRange, IndexLookupFunc, int *>;
87+
friend class ClosureGraph;
11188

112-
private:
11389
SILModule *M;
11490

11591
// The analysis data. nullptr if it has never been computed.
116-
std::unique_ptr<ClosureScopeData> scopeData;
92+
std::unique_ptr<ClosureGraph> scopeGraph;
11793

11894
public:
11995
ClosureScopeAnalysis(SILModule *M);
@@ -125,13 +101,20 @@ class ClosureScopeAnalysis : public SILAnalysis {
125101

126102
SILModule *getModule() const { return M; }
127103

128-
// Return true if the given function is the parent scope for any closures.
129-
bool isClosureScope(SILFunction *scopeFunc);
104+
/// Visit the parent scopes of \p closure if it has any. If \p visitor returns
105+
/// false, exit early and return false. Otherwise return true.
106+
bool visitClosureScopes(SILFunction *closure,
107+
std::function<bool(SILFunction *scopeFunc)> visitor);
130108

131-
// Return a range of scopes for the given closure. The elements of the
132-
// returned range have type `SILFunction *` and are non-null. Returns an
133-
// empty range for a SILFunction that is not a closure or is a dead closure.
134-
ScopeRange getClosureScopes(SILFunction *closureFunc);
109+
/// Visit the closures directly referenced by \p scopeFunc.
110+
bool visitClosures(SILFunction *scopeFunc,
111+
std::function<bool(SILFunction *closure)> visitor);
112+
113+
/// Return true if this function is a reachable closure.
114+
bool isReachableClosure(SILFunction *function) {
115+
// This visitor returns false immediately on the first scope.
116+
return !visitClosureScopes(function, [](SILFunction *) { return false; });
117+
}
135118

136119
/// Invalidate all information in this analysis.
137120
virtual void invalidate() override;
@@ -159,25 +142,50 @@ class ClosureScopeAnalysis : public SILAnalysis {
159142
}
160143

161144
protected:
162-
ClosureScopeData *getOrComputeScopeData();
145+
ClosureScopeAnalysis(const ClosureScopeAnalysis &) = delete;
146+
ClosureScopeAnalysis &operator=(const ClosureScopeAnalysis &) = delete;
147+
148+
ClosureGraph *getOrComputeGraph();
163149
};
164150

165-
// ClosureScopeAnalysis utility for visiting functions top down in closure scope
166-
// order.
167-
class TopDownClosureFunctionOrder {
168-
ClosureScopeAnalysis *CSA;
151+
// ClosureScopeAnalysis utility for visiting functions top-down or bottom-up in
152+
// closure scope order.
153+
class ClosureFunctionOrder {
154+
class ClosureDFS;
155+
friend class ClosureDFS;
156+
157+
ClosureScopeAnalysis *csa;
169158

170-
llvm::SmallSet<SILFunction *, 16> visited;
159+
// All functions in this module in top-down order (RPO) following the closure
160+
// scope graph. Functions that define a closure occur before the closure.
161+
std::vector<SILFunction *> topDownFunctions;
171162

172-
BlotSetVector<SILFunction *> closureWorklist;
163+
// If the closure scope graph has any cycles, record each function at the
164+
// head of a cycle. This does not include all the functions in the
165+
// strongly-connected component. This is extremely rare. It is always a local
166+
// function that refers to itself either directly or indirectly.
167+
SmallPtrSet<SILFunction *, 4> closureCycleHeads;
173168

174169
public:
175-
TopDownClosureFunctionOrder(ClosureScopeAnalysis *CSA) : CSA(CSA) {}
170+
ClosureFunctionOrder(ClosureScopeAnalysis *csa) : csa(csa) {}
176171

177-
// Visit all functions in a module, visiting each closure scope function
178-
// before
179-
// the closure function itself.
180-
void visitFunctions(llvm::function_ref<void(SILFunction *)> visitor);
172+
void compute();
173+
174+
ArrayRef<SILFunction *> getTopDownFunctions() const {
175+
assert(!topDownFunctions.empty()
176+
|| llvm::empty(csa->getModule()->getFunctions()));
177+
return topDownFunctions;
178+
}
179+
180+
bool isHeadOfClosureCycle(SILFunction *function) const {
181+
return closureCycleHeads.contains(function);
182+
}
183+
184+
SWIFT_ASSERT_ONLY_DECL(void dump());
185+
186+
protected:
187+
ClosureFunctionOrder(const ClosureFunctionOrder &) = delete;
188+
ClosureFunctionOrder &operator=(const ClosureFunctionOrder &) = delete;
181189
};
182190

183191
} // end namespace swift

lib/SIL/IR/SILFunction.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,8 +150,9 @@ SILFunction::SILFunction(SILModule &Module, SILLinkage Linkage, StringRef Name,
150150
IsDynamicallyReplaceable_t isDynamic,
151151
IsExactSelfClass_t isExactSelfClass,
152152
IsDistributed_t isDistributed)
153-
: SwiftObjectHeader(functionMetatype),
154-
Module(Module), Availability(AvailabilityContext::alwaysAvailable()) {
153+
: SwiftObjectHeader(functionMetatype), Module(Module),
154+
index(Module.getNewFunctionIndex()),
155+
Availability(AvailabilityContext::alwaysAvailable()) {
155156
init(Linkage, Name, LoweredType, genericEnv, Loc, isBareSILFunction, isTrans,
156157
isSerialized, entryCount, isThunk, classSubclassScope, inlineStrategy,
157158
E, DebugScope, isDynamic, isExactSelfClass, isDistributed);

lib/SILOptimizer/Analysis/AccessSummaryAnalysis.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,9 @@ static bool hasExpectedUsesOfNoEscapePartialApply(Operand *partialApplyUse) {
162162
if (user->getModule().getASTContext().hadError())
163163
return true;
164164

165+
if (isIncidentalUse(user))
166+
return true;
167+
165168
// It is fine to call the partial apply
166169
switch (user->getKind()) {
167170
case SILInstructionKind::ApplyInst:
@@ -206,10 +209,6 @@ static bool hasExpectedUsesOfNoEscapePartialApply(Operand *partialApplyUse) {
206209
return llvm::all_of(cast<CopyValueInst>(user)->getUses(),
207210
hasExpectedUsesOfNoEscapePartialApply);
208211

209-
// End borrow is always ok.
210-
case SILInstructionKind::EndBorrowInst:
211-
return true;
212-
213212
case SILInstructionKind::IsEscapingClosureInst:
214213
case SILInstructionKind::StoreInst:
215214
case SILInstructionKind::DestroyValueInst:

0 commit comments

Comments
 (0)