//===--- 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 trasformation 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 /// lanuguage's practical limitations. /// //===----------------------------------------------------------------------===// #ifndef SWIFT_SILOPTIMIZER_ANALYSIS_CLOSURESCOPE_H #define SWIFT_SILOPTIMIZER_ANALYSIS_CLOSURESCOPE_H #include "swift/Basic/BlotSetVector.h" #include "swift/SIL/SILFunction.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 ClosureScopeData; class ClosureScopeAnalysis : public SILAnalysis { friend class ClosureScopeData; // Get a closure's scope function from its index. This functor is compatible // with OptionalTransformRange. Unfortunately it exposes the internals of the // analysis data. struct IndexLookupFunc { // A reference to all closure parent scopes ordered by their index. const std::vector &indexedScopes; IndexLookupFunc(const std::vector &indexedScopes) : indexedScopes(indexedScopes) {} Optional operator()(int idx) const { if (auto funcPtr = indexedScopes[idx]) { return funcPtr; } return None; } }; using IndexRange = IteratorRange; public: // A range of SILFunction scopes converted from their scope indices and // filtered to remove any erased functions. using ScopeRange = OptionalTransformRange; private: SILModule *M; // The analysis data. nullptr if it has never been computed. std::unique_ptr scopeData; public: ClosureScopeAnalysis(SILModule *M); ~ClosureScopeAnalysis(); static bool classof(const SILAnalysis *S) { return S->getKind() == SILAnalysisKind::ClosureScope; } SILModule *getModule() const { return M; } // Return true if the given function is the parent scope for any closures. bool isClosureScope(SILFunction *scopeFunc); // Return a range of scopes for the given closure. The elements of the // returned range have type `SILFunction *` and are non-null. Returns an // empty range for a SILFunction that is not a closure or is a dead closure. ScopeRange getClosureScopes(SILFunction *closureFunc); /// 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: ClosureScopeData *getOrComputeScopeData(); }; // ClosureScopeAnalysis utility for visiting functions top down in closure scope // order. class TopDownClosureFunctionOrder { ClosureScopeAnalysis *CSA; llvm::SmallSet visited; BlotSetVector closureWorklist; public: TopDownClosureFunctionOrder(ClosureScopeAnalysis *CSA) : CSA(CSA) {} // Visit all functions in a module, visiting each closure scope function // before // the closure function itself. void visitFunctions(llvm::function_ref visitor); }; } // end namespace swift #endif