Skip to content

Commit 533f42d

Browse files
committed
Closures and local functions only capture generic parameters if necessary
The CaptureInfo computed by Sema now records if the body of the function uses any generic parameters from the outer context. SIL type lowering only adds a generic signature if this is the case, instead of unconditionally. This might yield a marginal performance improvement in some cases, but more interestingly will allow @convention(c) conversions from generic context. Swift SVN r32161
1 parent 2afeb10 commit 533f42d

15 files changed

+275
-97
lines changed

include/swift/AST/AnyFunctionRef.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,12 @@ class AnyFunctionRef {
6666
return AFD->getBodyParamPatterns();
6767
return TheFunction.get<AbstractClosureExpr *>()->getParamPatterns();
6868
}
69+
70+
bool hasType() const {
71+
if (auto *AFD = TheFunction.dyn_cast<AbstractFunctionDecl *>())
72+
return AFD->hasType();
73+
return !TheFunction.get<AbstractClosureExpr *>()->getType().isNull();
74+
}
6975

7076
Type getType() const {
7177
if (auto *AFD = TheFunction.dyn_cast<AbstractFunctionDecl *>())

include/swift/AST/CaptureInfo.h

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,18 +68,24 @@ class CapturedValue {
6868

6969
/// \brief Stores information about captured variables.
7070
class CaptureInfo {
71-
llvm::PointerIntPair<const CapturedValue *, 1, bool> CapturesAndComputed;
72-
size_t Count = 0;
71+
const CapturedValue *Captures;
72+
unsigned Count = 0;
73+
bool GenericParamCaptures : 1;
74+
bool Computed : 1;
7375

7476
public:
75-
bool hasBeenComputed() { return CapturesAndComputed.getInt(); }
77+
CaptureInfo()
78+
: Captures(nullptr), Count(0), GenericParamCaptures(0), Computed(0) { }
79+
80+
bool hasBeenComputed() { return Computed; }
7681
bool empty() { return Count == 0; }
7782

7883
ArrayRef<CapturedValue> getCaptures() const {
79-
return llvm::makeArrayRef(CapturesAndComputed.getPointer(), Count);
84+
return llvm::makeArrayRef(Captures, Count);
8085
}
8186
void setCaptures(ArrayRef<CapturedValue> C) {
82-
CapturesAndComputed.setPointerAndInt(C.data(), true);
87+
Captures = C.data();
88+
Computed = true;
8389
Count = C.size();
8490
}
8591

@@ -92,6 +98,15 @@ class CaptureInfo {
9298
/// \returns true if getLocalCaptures() will return a non-empty list.
9399
bool hasLocalCaptures() const;
94100

101+
/// \returns true if the function captures any generic type parameters.
102+
bool hasGenericParamCaptures() {
103+
return GenericParamCaptures;
104+
}
105+
106+
void setGenericParamCaptures(bool genericParamCaptures) {
107+
GenericParamCaptures = genericParamCaptures;
108+
}
109+
95110
void dump() const;
96111
void print(raw_ostream &OS) const;
97112
};

include/swift/SIL/TypeLowering.h

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#define SIL_TypeLowering_h
1515

1616
#include "swift/AST/ArchetypeBuilder.h"
17+
#include "swift/AST/CaptureInfo.h"
1718
#include "swift/ABI/MetadataValues.h"
1819
#include "swift/SIL/AbstractionPattern.h"
1920
#include "swift/SIL/SILLocation.h"
@@ -389,6 +390,12 @@ struct SILConstantInfo {
389390
return SILType::getPrimitiveObjectType(SILFnType);
390391
}
391392

393+
ArrayRef<Substitution> getForwardingSubstitutions(ASTContext &C) {
394+
if (!ContextGenericParams)
395+
return { };
396+
return ContextGenericParams->getForwardingSubstitutions(C);
397+
}
398+
392399
friend bool operator==(SILConstantInfo lhs, SILConstantInfo rhs) {
393400
return lhs.FormalType == rhs.FormalType &&
394401
lhs.FormalInterfaceType == rhs.FormalInterfaceType &&
@@ -521,7 +528,7 @@ class TypeConverter {
521528

522529
llvm::DenseMap<OverrideKey, SILConstantInfo> ConstantOverrideTypes;
523530

524-
llvm::DenseMap<AnyFunctionRef, std::vector<CapturedValue>> LoweredCaptures;
531+
llvm::DenseMap<AnyFunctionRef, CaptureInfo> LoweredCaptures;
525532

526533
/// The set of recursive types we've already diagnosed.
527534
llvm::DenseSet<NominalTypeDecl *> RecursiveNominalTypes;
@@ -781,8 +788,13 @@ class TypeConverter {
781788
/// Retrieve the set of archetypes open in the given context.
782789
GenericParamList *getEffectiveGenericParamsForContext(DeclContext *dc);
783790

784-
/// Retrieve the set of generic parameters for the given context.
785-
CanGenericSignature getEffectiveGenericSignatureForContext(DeclContext *dc);
791+
/// Retrieve the set of archetypes closed over by the given function.
792+
GenericParamList *getEffectiveGenericParams(AnyFunctionRef fn,
793+
CaptureInfo captureInfo);
794+
795+
/// Retrieve the set of generic parameters closed over by the given function.
796+
CanGenericSignature getEffectiveGenericSignature(AnyFunctionRef fn,
797+
CaptureInfo captureInfo);
786798

787799
/// Push a generic function context. See GenericContextScope for an RAII
788800
/// interface to this function.
@@ -827,10 +839,10 @@ class TypeConverter {
827839
static SILLinkage getLinkageForProtocolConformance(
828840
const NormalProtocolConformance *C,
829841
ForDefinition_t definition);
830-
842+
831843
/// Get the capture list from a closure, with transitive function captures
832844
/// flattened.
833-
ArrayRef<CapturedValue> getLoweredLocalCaptures(AnyFunctionRef fn);
845+
CaptureInfo getLoweredLocalCaptures(AnyFunctionRef fn);
834846

835847
enum class ABIDifference : uint8_t {
836848
// No ABI differences, function can be trivially bitcast to result type.

lib/SIL/SILFunctionType.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#define DEBUG_TYPE "libsil"
2020
#include "swift/SIL/SILType.h"
2121
#include "swift/SIL/SILModule.h"
22+
#include "swift/AST/AnyFunctionRef.h"
2223
#include "swift/AST/Decl.h"
2324
#include "swift/AST/DiagnosticsSIL.h"
2425
#include "swift/AST/ForeignErrorConvention.h"

lib/SIL/TypeLowering.cpp

Lines changed: 74 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1855,8 +1855,34 @@ TypeConverter::getEffectiveGenericParamsForContext(DeclContext *dc) {
18551855
return dc->getGenericParamsOfContext();
18561856
}
18571857

1858+
GenericParamList *
1859+
TypeConverter::getEffectiveGenericParams(AnyFunctionRef fn,
1860+
CaptureInfo captureInfo) {
1861+
auto dc = fn.getAsDeclContext()->getParent();
1862+
1863+
if (dc->isLocalContext() &&
1864+
!captureInfo.hasGenericParamCaptures()) {
1865+
return nullptr;
1866+
}
1867+
1868+
return getEffectiveGenericParamsForContext(dc);
1869+
}
1870+
18581871
CanGenericSignature
1859-
TypeConverter::getEffectiveGenericSignatureForContext(DeclContext *dc) {
1872+
TypeConverter::getEffectiveGenericSignature(AnyFunctionRef fn,
1873+
CaptureInfo captureInfo) {
1874+
auto dc = fn.getAsDeclContext();
1875+
1876+
if (auto sig = dc->getGenericSignatureOfContext())
1877+
return sig->getCanonicalSignature();
1878+
1879+
dc = dc->getParent();
1880+
1881+
if (dc->isLocalContext() &&
1882+
!captureInfo.hasGenericParamCaptures()) {
1883+
return nullptr;
1884+
}
1885+
18601886
// Find the innermost context that has a generic parameter list.
18611887
// FIXME: This is wrong for generic local functions in generic contexts.
18621888
// Their GenericParamList is not semantically a child of the context
@@ -1876,11 +1902,16 @@ TypeConverter::getEffectiveGenericSignatureForContext(DeclContext *dc) {
18761902
CanAnyFunctionType
18771903
TypeConverter::getFunctionTypeWithCaptures(CanAnyFunctionType funcType,
18781904
AnyFunctionRef theClosure) {
1879-
auto parentContext = theClosure.getAsDeclContext()->getParent();
1880-
// Capture generic parameters from the enclosing context.
1881-
GenericParamList *genericParams
1882-
= getEffectiveGenericParamsForContext(parentContext);
1883-
1905+
// Get transitive closure of value captured by this function, and any
1906+
// captured functions.
1907+
auto captureInfo = getLoweredLocalCaptures(theClosure);
1908+
1909+
// Capture generic parameters from the enclosing context if necessary.
1910+
GenericParamList *genericParams =
1911+
getEffectiveGenericParams(theClosure, captureInfo);
1912+
1913+
// If we don't have any local captures (including function captures),
1914+
// there's no context to apply.
18841915
if (!theClosure.getCaptureInfo().hasLocalCaptures()) {
18851916
if (!genericParams)
18861917
return adjustFunctionType(funcType,
@@ -1900,12 +1931,7 @@ TypeConverter::getFunctionTypeWithCaptures(CanAnyFunctionType funcType,
19001931

19011932
SmallVector<TupleTypeElt, 8> inputFields;
19021933

1903-
// Note that the list of lowered local captures may be empty even if
1904-
// CaptureInfo::hasLocalCaptures() returned true. This is the case if e.g.
1905-
// a local function calls another local function which has no captures itself.
1906-
ArrayRef<CapturedValue> captures = getLoweredLocalCaptures(theClosure);
1907-
1908-
for (auto capture : captures) {
1934+
for (auto capture : captureInfo.getCaptures()) {
19091935
auto VD = capture.getDecl();
19101936
// A capture of a 'var' or 'inout' variable is done with the underlying
19111937
// object type.
@@ -1955,12 +1981,16 @@ TypeConverter::getFunctionTypeWithCaptures(CanAnyFunctionType funcType,
19551981
CanAnyFunctionType
19561982
TypeConverter::getFunctionInterfaceTypeWithCaptures(CanAnyFunctionType funcType,
19571983
AnyFunctionRef theClosure) {
1958-
auto context = theClosure.getAsDeclContext();
1984+
// Get transitive closure of value captured by this function, and any
1985+
// captured functions.
1986+
auto captureInfo = getLoweredLocalCaptures(theClosure);
19591987

1960-
// Capture generic parameters from the enclosing context.
1961-
CanGenericSignature genericSig
1962-
= getEffectiveGenericSignatureForContext(context);
1988+
// Capture generic parameters from the enclosing context if necessary.
1989+
CanGenericSignature genericSig = getEffectiveGenericSignature(theClosure,
1990+
captureInfo);
19631991

1992+
// If we don't have any local captures (including function captures),
1993+
// there's no context to apply.
19641994
if (!theClosure.getCaptureInfo().hasLocalCaptures()) {
19651995
if (!genericSig)
19661996
return adjustFunctionType(funcType,
@@ -1978,12 +2008,7 @@ TypeConverter::getFunctionInterfaceTypeWithCaptures(CanAnyFunctionType funcType,
19782008

19792009
SmallVector<TupleTypeElt, 8> inputFields;
19802010

1981-
// Note that the list of lowered local captures may be empty even if
1982-
// CaptureInfo::hasLocalCaptures() returned true. This is the case if e.g.
1983-
// a local function calls another local function which has no captures itself.
1984-
ArrayRef<CapturedValue> captures = getLoweredLocalCaptures(theClosure);
1985-
1986-
for (auto capture : captures) {
2011+
for (auto capture : captureInfo.getCaptures()) {
19872012
// A capture of a 'var' or 'inout' variable is done with the underlying
19882013
// object type.
19892014
auto vd = capture.getDecl();
@@ -2019,7 +2044,9 @@ TypeConverter::getFunctionInterfaceTypeWithCaptures(CanAnyFunctionType funcType,
20192044
TupleType::get(inputFields, Context)->getCanonicalType();
20202045

20212046
// Map context archetypes out of the captures.
2022-
capturedInputs = getInterfaceTypeOutOfContext(capturedInputs, context);
2047+
capturedInputs =
2048+
getInterfaceTypeOutOfContext(capturedInputs,
2049+
theClosure.getAsDeclContext());
20232050

20242051
auto extInfo = AnyFunctionType::ExtInfo(FunctionType::Representation::Thin,
20252052
/*noreturn*/ false,
@@ -2117,22 +2144,22 @@ TypeConverter::getConstantContextGenericParams(SILDeclRef c) {
21172144
ValueDecl *vd = c.loc.dyn_cast<ValueDecl *>();
21182145

21192146
/// Get the function generic params, including outer params.
2120-
auto getLocalFuncParams = [&](FuncDecl *func) -> GenericParamList * {
2121-
// FIXME: For local generic functions we need to chain the local generic
2122-
// context to the outer context.
2123-
if (auto GP = func->getGenericParamsOfContext())
2124-
return GP;
2125-
return getEffectiveGenericParamsForContext(func->getParent());
2126-
};
2127-
21282147
switch (c.kind) {
21292148
case SILDeclRef::Kind::Func: {
21302149
if (auto *ACE = c.getAbstractClosureExpr()) {
2150+
auto captureInfo = getLoweredLocalCaptures(ACE);
2151+
21312152
// Closures are currently never natively generic.
2132-
return {getEffectiveGenericParamsForContext(ACE->getParent()), nullptr};
2153+
return {getEffectiveGenericParams(ACE, captureInfo), nullptr};
21332154
}
21342155
FuncDecl *func = cast<FuncDecl>(vd);
2135-
return {getLocalFuncParams(func), func->getGenericParams()};
2156+
// FIXME: For local generic functions we need to chain the local generic
2157+
// context to the outer context.
2158+
if (auto GP = func->getGenericParamsOfContext())
2159+
return {GP, func->getGenericParams()};
2160+
auto captureInfo = getLoweredLocalCaptures(func);
2161+
return {getEffectiveGenericParams(func, captureInfo),
2162+
func->getGenericParams()};
21362163
}
21372164
case SILDeclRef::Kind::EnumElement: {
21382165
auto eltDecl = cast<EnumElementDecl>(vd);
@@ -2328,11 +2355,15 @@ getAnyFunctionRefFromCapture(CapturedValue capture) {
23282355
return None;
23292356
}
23302357

2331-
ArrayRef<CapturedValue>
2358+
CaptureInfo
23322359
TypeConverter::getLoweredLocalCaptures(AnyFunctionRef fn) {
23332360
// First, bail out if there are no local captures at all.
2334-
if (!fn.getCaptureInfo().hasLocalCaptures())
2335-
return {};
2361+
if (!fn.getCaptureInfo().hasLocalCaptures()) {
2362+
CaptureInfo info;
2363+
info.setGenericParamCaptures(
2364+
fn.getCaptureInfo().hasGenericParamCaptures());
2365+
return info;
2366+
};
23362367

23372368
// See if we've cached the lowered capture list for this function.
23382369
auto found = LoweredCaptures.find(fn);
@@ -2342,12 +2373,16 @@ TypeConverter::getLoweredLocalCaptures(AnyFunctionRef fn) {
23422373
// Recursively collect transitive captures from captured local functions.
23432374
llvm::DenseSet<AnyFunctionRef> visitedFunctions;
23442375
llvm::SetVector<CapturedValue> captures;
2376+
bool capturesGenericParams = false;
23452377

23462378
std::function<void (AnyFunctionRef)> collectFunctionCaptures
23472379
= [&](AnyFunctionRef curFn) {
23482380
if (!visitedFunctions.insert(curFn).second)
23492381
return;
23502382

2383+
if (curFn.getCaptureInfo().hasGenericParamCaptures())
2384+
capturesGenericParams = true;
2385+
23512386
SmallVector<CapturedValue, 4> localCaptures;
23522387
curFn.getCaptureInfo().getLocalCaptures(localCaptures);
23532388
for (auto capture : localCaptures) {
@@ -2401,11 +2436,11 @@ TypeConverter::getLoweredLocalCaptures(AnyFunctionRef fn) {
24012436
collectFunctionCaptures(fn);
24022437

24032438
// Cache the uniqued set of transitive captures.
2404-
auto inserted = LoweredCaptures.insert({fn, {}});
2439+
auto inserted = LoweredCaptures.insert({fn, CaptureInfo()});
24052440
assert(inserted.second && "already in map?!");
24062441
auto &cachedCaptures = inserted.first->second;
2407-
std::copy(captures.begin(), captures.end(),
2408-
std::back_inserter(cachedCaptures));
2442+
cachedCaptures.setGenericParamCaptures(capturesGenericParams);
2443+
cachedCaptures.setCaptures(Context.AllocateCopy(captures));
24092444

24102445
return cachedCaptures;
24112446
}

lib/SILGen/SILGenApply.cpp

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ emitCapturesAsArgumentSource(SILGenFunction &gen,
7575
// The capture array should match the explosion schema of the closure's
7676
// first formal argument type.
7777
auto info = gen.SGM.Types.getConstantInfo(SILDeclRef::forAnyFunctionRef(fn));
78-
auto subs = gen.getForwardingSubstitutions();
78+
auto subs = info.getForwardingSubstitutions(gen.getASTContext());
7979
auto origFormalTy = info.FormalInterfaceType;
8080
CanFunctionType formalTy;
8181
if (!subs.empty()) {
@@ -1195,11 +1195,16 @@ class SILGenApply : public Lowering::ExprVisitor<SILGenApply> {
11951195
substFnType = captures.second;
11961196
setSelfParam(std::move(captures.first), captures.second.getInput());
11971197
}
1198-
1199-
// Forward local substitutions to a local function.
1200-
if (afd->getParent()->isLocalContext()) {
1201-
subs = SGF.getForwardingSubstitutions();
1202-
}
1198+
1199+
// FIXME: We should be checking hasLocalCaptures() on the lowered
1200+
// captures in the constant info too, to generate more efficient
1201+
// code for mutually recursive local functions which otherwise
1202+
// capture no state.
1203+
auto constantInfo = SGF.getConstantInfo(constant);
1204+
1205+
// Forward local substitutions to a non-generic local function.
1206+
if (afd->getParent()->isLocalContext() && !afd->getGenericParams())
1207+
subs = constantInfo.getForwardingSubstitutions(SGF.getASTContext());
12031208
}
12041209

12051210
if (e->getDeclRef().isSpecialized()) {
@@ -1232,8 +1237,14 @@ class SILGenApply : public Lowering::ExprVisitor<SILGenApply> {
12321237
substFnType = captures.second;
12331238
setSelfParam(std::move(captures.first), captures.second.getInput());
12341239
}
1235-
subs = SGF.getForwardingSubstitutions();
12361240

1241+
// FIXME: We should be checking hasLocalCaptures() on the lowered
1242+
// captures in the constant info above, to generate more efficient
1243+
// code for mutually recursive local functions which otherwise
1244+
// capture no state.
1245+
auto constantInfo = SGF.getConstantInfo(constant);
1246+
subs = constantInfo.getForwardingSubstitutions(SGF.getASTContext());
1247+
12371248
setCallee(Callee::forDirect(SGF, constant, substFnType, e));
12381249
// If there are substitutions, add them, always at depth 0.
12391250
if (!subs.empty())

lib/SILGen/SILGenExpr.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1941,9 +1941,7 @@ RValue RValueEmitter::visitAbstractClosureExpr(AbstractClosureExpr *e,
19411941

19421942
// Generate the closure value (if any) for the closure expr's function
19431943
// reference.
1944-
return RValue(SGF, e,
1945-
SGF.emitClosureValue(e, SILDeclRef(e),
1946-
SGF.getForwardingSubstitutions(), e));
1944+
return RValue(SGF, e, SGF.emitClosureValue(e, SILDeclRef(e), e));
19471945
}
19481946

19491947
RValue RValueEmitter::

0 commit comments

Comments
 (0)