mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
195 lines
7.6 KiB
C++
195 lines
7.6 KiB
C++
//===--- ClosureScope.h - Determines closure's defining scope ---*- C++ -*-===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
///
|
|
/// Map each non-escaping closure SILFunction to a set of SILFunctions
|
|
/// corresponding to its parent scopes.
|
|
///
|
|
/// This is a lightweight analysis specific to non-escaping closures. Unlike
|
|
/// CallerAnalysis, this does not reflect the call tree. A closure's scope is a
|
|
/// function that directly references the closure. It may directly invoke the
|
|
/// closure (as a caller) or may simply pass it off as an argument.
|
|
///
|
|
/// Like CallerAnalysis, ClosureScope is top-down, but unlike CallerAnalysis, it
|
|
/// does not require complex invalidation and recomputation. The underlying
|
|
/// assumption is that no transformation will add new references to existing
|
|
/// non-escaping closures, with some exceptions like SILCloner.
|
|
///
|
|
/// TODO: When this analysis is used across passes, fix SILCloner to update or
|
|
/// invalidate. In SILVerifier, if this analysis is marked valid, check that no
|
|
/// new unseen closure references have been added.
|
|
///
|
|
/// We do not currently mark SILFunctions as non-escaping. However, only
|
|
/// closures that are not assigned to an lvalue and are never passed as escaping
|
|
/// closures can use the @inout_aliasable convention. For now, we simply limit
|
|
/// this analysis to such closures. This covers the set of closures that
|
|
/// SILOptimizer must view as non-escaping, which must in turn be a subset of
|
|
/// Sema's non-escaping closures.
|
|
///
|
|
/// NOTE: a non-escaping function can be passed as an escaping function via
|
|
/// withoutActuallyEscaping. However, using that API to introduce recursion is
|
|
/// disallowed according to exclusivity semantics. That is, non-escaping
|
|
/// function types cannot be reentrant (SE-0176). In this analysis, we assert
|
|
/// that closure scopes are acyclic. Although the language does not currently
|
|
/// enforce non-reentrant, non-escaping closures, the scope graph cannot be
|
|
/// cyclic because there's no way to name a non-escaping closure. So, in the
|
|
/// long term the acyclic assumption made by this analysis is protected by
|
|
/// non-reentrant semantics, and in the short-term it's safe because of the
|
|
/// language's practical limitations.
|
|
///
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef SWIFT_SILOPTIMIZER_ANALYSIS_CLOSURESCOPE_H
|
|
#define SWIFT_SILOPTIMIZER_ANALYSIS_CLOSURESCOPE_H
|
|
|
|
#include "swift/Basic/Assertions.h"
|
|
#include "swift/Basic/BlotSetVector.h"
|
|
#include "swift/SIL/SILFunction.h"
|
|
#include "swift/SIL/SILModule.h"
|
|
#include "swift/SILOptimizer/Analysis/Analysis.h"
|
|
#include "llvm/ADT/SmallSet.h"
|
|
#include "llvm/ADT/iterator.h"
|
|
|
|
namespace swift {
|
|
|
|
/// Return true if this function is known to be non-escaping.
|
|
///
|
|
/// This must only return true if the closure was also deemed non-escaping in
|
|
/// Sema. Sema will eventually guarantee non-reentrance.
|
|
///
|
|
/// This must always return true for any closure with @inout_aliasable parameter
|
|
/// conventions, because passes like AccessEnforcementSelection assume that
|
|
/// convention only applies to non-escaping closures.
|
|
///
|
|
/// (Hence, SILGen may only use @inout_aliasable for closures that Sema deems
|
|
/// non-escaping.)
|
|
///
|
|
/// This may conservatively return false for any non-escaping closures that
|
|
/// don't happen to use @inout_aliasable.
|
|
inline bool isNonEscapingClosure(CanSILFunctionType funcTy) {
|
|
auto isInoutAliasable = [](SILParameterInfo paramInfo) {
|
|
return paramInfo.getConvention()
|
|
== ParameterConvention::Indirect_InoutAliasable;
|
|
};
|
|
return llvm::any_of(funcTy->getParameters(), isInoutAliasable);
|
|
}
|
|
|
|
class ClosureGraph;
|
|
|
|
class ClosureScopeAnalysis : public SILAnalysis {
|
|
friend class ClosureGraph;
|
|
|
|
SILModule *M;
|
|
|
|
// The analysis data. nullptr if it has never been computed.
|
|
std::unique_ptr<ClosureGraph> scopeGraph;
|
|
|
|
public:
|
|
ClosureScopeAnalysis(SILModule *M);
|
|
~ClosureScopeAnalysis();
|
|
|
|
static bool classof(const SILAnalysis *S) {
|
|
return S->getKind() == SILAnalysisKind::ClosureScope;
|
|
}
|
|
|
|
SILModule *getModule() const { return M; }
|
|
|
|
/// Visit the parent scopes of \p closure if it has any. If \p visitor returns
|
|
/// false, exit early and return false. Otherwise return true.
|
|
bool visitClosureScopes(SILFunction *closure,
|
|
std::function<bool(SILFunction *scopeFunc)> visitor);
|
|
|
|
/// Visit the closures directly referenced by \p scopeFunc.
|
|
bool visitClosures(SILFunction *scopeFunc,
|
|
std::function<bool(SILFunction *closure)> visitor);
|
|
|
|
/// Return true if this function is a reachable closure.
|
|
bool isReachableClosure(SILFunction *function) {
|
|
// This visitor returns false immediately on the first scope.
|
|
return !visitClosureScopes(function, [](SILFunction *) { return false; });
|
|
}
|
|
|
|
/// Invalidate all information in this analysis.
|
|
virtual void invalidate() override;
|
|
|
|
/// Invalidate all of the information for a specific function.
|
|
virtual void invalidate(SILFunction *F, InvalidationKind K) override {
|
|
// No invalidation needed because the analysis does not cache anything
|
|
// per call-site in functions, and we assume that references to closures
|
|
// cannot be added to functions, aside from cloning.
|
|
}
|
|
|
|
/// Notify the analysis about a newly created function.
|
|
virtual void notifyAddedOrModifiedFunction(SILFunction *F) override {
|
|
// Nothing to be done because the analysis does not cache anything
|
|
// per call-site in functions.
|
|
}
|
|
|
|
/// Notify the analysis about a function which will be deleted from the
|
|
/// module.
|
|
virtual void notifyWillDeleteFunction(SILFunction *F) override;
|
|
|
|
/// Notify the analysis about changed witness or vtables.
|
|
virtual void invalidateFunctionTables() override {
|
|
// witness tables have no effect on closure scopes.
|
|
}
|
|
|
|
protected:
|
|
ClosureScopeAnalysis(const ClosureScopeAnalysis &) = delete;
|
|
ClosureScopeAnalysis &operator=(const ClosureScopeAnalysis &) = delete;
|
|
|
|
ClosureGraph *getOrComputeGraph();
|
|
};
|
|
|
|
// ClosureScopeAnalysis utility for visiting functions top-down or bottom-up in
|
|
// closure scope order.
|
|
class ClosureFunctionOrder {
|
|
class ClosureDFS;
|
|
friend class ClosureDFS;
|
|
|
|
ClosureScopeAnalysis *csa;
|
|
|
|
// All functions in this module in top-down order (RPO) following the closure
|
|
// scope graph. Functions that define a closure occur before the closure.
|
|
std::vector<SILFunction *> topDownFunctions;
|
|
|
|
// If the closure scope graph has any cycles, record each function at the
|
|
// head of a cycle. This does not include all the functions in the
|
|
// strongly-connected component. This is extremely rare. It is always a local
|
|
// function that refers to itself either directly or indirectly.
|
|
SmallPtrSet<SILFunction *, 4> closureCycleHeads;
|
|
|
|
public:
|
|
ClosureFunctionOrder(ClosureScopeAnalysis *csa) : csa(csa) {}
|
|
|
|
void compute();
|
|
|
|
ArrayRef<SILFunction *> getTopDownFunctions() const {
|
|
assert(!topDownFunctions.empty()
|
|
|| csa->getModule()->getFunctions().empty());
|
|
return topDownFunctions;
|
|
}
|
|
|
|
bool isHeadOfClosureCycle(SILFunction *function) const {
|
|
return closureCycleHeads.contains(function);
|
|
}
|
|
|
|
SWIFT_ASSERT_ONLY_DECL(void dump());
|
|
|
|
protected:
|
|
ClosureFunctionOrder(const ClosureFunctionOrder &) = delete;
|
|
ClosureFunctionOrder &operator=(const ClosureFunctionOrder &) = delete;
|
|
};
|
|
|
|
} // end namespace swift
|
|
|
|
#endif
|