mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
186 lines
7.1 KiB
C++
186 lines
7.1 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 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<SILFunction *> &indexedScopes;
|
|
|
|
IndexLookupFunc(const std::vector<SILFunction *> &indexedScopes)
|
|
: indexedScopes(indexedScopes) {}
|
|
|
|
Optional<SILFunction *> operator()(int idx) const {
|
|
if (auto funcPtr = indexedScopes[idx]) {
|
|
return funcPtr;
|
|
}
|
|
return None;
|
|
}
|
|
};
|
|
using IndexRange = IteratorRange<int *>;
|
|
|
|
public:
|
|
// A range of SILFunction scopes converted from their scope indices and
|
|
// filtered to remove any erased functions.
|
|
using ScopeRange = OptionalTransformRange<IndexRange, IndexLookupFunc, int *>;
|
|
|
|
private:
|
|
SILModule *M;
|
|
|
|
// The analysis data. nullptr if it has never been computed.
|
|
std::unique_ptr<ClosureScopeData> scopeData;
|
|
|
|
public:
|
|
ClosureScopeAnalysis(SILModule *M);
|
|
~ClosureScopeAnalysis();
|
|
|
|
static bool classof(const SILAnalysis *S) {
|
|
return S->getKind() == AnalysisKind::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 notifyAddFunction(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 notifyDeleteFunction(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<SILFunction *, 16> visited;
|
|
|
|
BlotSetVector<SILFunction *> 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(std::function<void(SILFunction *)> visitor);
|
|
};
|
|
|
|
} // end namespace swift
|
|
|
|
#endif
|