Skip to content

Commit 2d2b024

Browse files
committed
IRGen: Heap-allocate metadata and witness table packs captured by escaping closures
1 parent d891cf0 commit 2d2b024

File tree

7 files changed

+146
-40
lines changed

7 files changed

+146
-40
lines changed

include/swift/Runtime/RuntimeFunctions.def

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1008,6 +1008,28 @@ FUNCTION(CompareProtocolConformanceDescriptors,
10081008
ATTRS(NoUnwind, ReadNone, WillReturn),
10091009
EFFECT(NoEffect)) // ?
10101010

1011+
// SWIFT_RUNTIME_EXPORT SWIFT_CC(swift)
1012+
// const Metadata * const *
1013+
// swift_allocateMetadataPack(const Metadata * const *ptr, size_t count);
1014+
FUNCTION(AllocateMetadataPack,
1015+
swift_allocateMetadataPack, SwiftCC,
1016+
AlwaysAvailable, // FIXME
1017+
RETURNS(TypeMetadataPtrPtrTy),
1018+
ARGS(TypeMetadataPtrPtrTy, SizeTy),
1019+
ATTRS(NoUnwind, WillReturn),
1020+
EFFECT(MetaData)) // ?
1021+
1022+
// SWIFT_RUNTIME_EXPORT SWIFT_CC(swift)
1023+
// const WitnessTable * const *
1024+
// swift_allocateWitnessTablePack(const WitnessTable * const *ptr, size_t count);
1025+
FUNCTION(AllocateWitnessTablePack,
1026+
swift_allocateWitnessTablePack, SwiftCC,
1027+
AlwaysAvailable, // FIXME
1028+
RETURNS(WitnessTablePtrPtrTy),
1029+
ARGS(WitnessTablePtrPtrTy, SizeTy),
1030+
ATTRS(NoUnwind, WillReturn),
1031+
EFFECT(MetaData)) // ?
1032+
10111033
// Metadata *swift_getMetatypeMetadata(Metadata *instanceTy);
10121034
FUNCTION(GetMetatypeMetadata, swift_getMetatypeMetadata, C_CC, AlwaysAvailable,
10131035
RETURNS(TypeMetadataPtrTy),

lib/IRGen/GenFunc.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1949,7 +1949,8 @@ Optional<StackAddress> irgen::emitFunctionPartialApplication(
19491949

19501950
// Reserve space for polymorphic bindings.
19511951
auto bindings = NecessaryBindings::forPartialApplyForwarder(
1952-
IGF.IGM, origType, subs, considerParameterSources);
1952+
IGF.IGM, origType, subs, outType->isNoEscape(),
1953+
considerParameterSources);
19531954

19541955
if (!bindings.empty()) {
19551956
hasSingleSwiftRefcountedContext = No;

lib/IRGen/GenProto.cpp

Lines changed: 54 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3279,7 +3279,8 @@ void NecessaryBindings::restore(IRGenFunction &IGF, Address buffer,
32793279

32803280
void NecessaryBindings::save(IRGenFunction &IGF, Address buffer) const {
32813281
emitInitOfGenericRequirementsBuffer(IGF, getRequirements(), buffer,
3282-
MetadataState::Complete, SubMap);
3282+
MetadataState::Complete, SubMap,
3283+
/*onHeapPacks=*/!NoEscape);
32833284
}
32843285

32853286
llvm::Value *irgen::emitWitnessTableRef(IRGenFunction &IGF,
@@ -3485,22 +3486,22 @@ void EmitPolymorphicArguments::emit(SubstitutionMap subs,
34853486

34863487
NecessaryBindings
34873488
NecessaryBindings::forPartialApplyForwarder(IRGenModule &IGM,
3488-
CanSILFunctionType origType,
3489-
SubstitutionMap subs,
3490-
bool considerParameterSources) {
3491-
return computeBindings(IGM, origType, subs,
3492-
considerParameterSources);
3489+
CanSILFunctionType origType,
3490+
SubstitutionMap subs,
3491+
bool noEscape,
3492+
bool considerParameterSources) {
3493+
NecessaryBindings bindings(subs, noEscape);
3494+
bindings.computeBindings(IGM, origType, considerParameterSources);
3495+
return bindings;
34933496
}
34943497

3495-
NecessaryBindings NecessaryBindings::computeBindings(
3496-
IRGenModule &IGM, CanSILFunctionType origType, SubstitutionMap subs,
3498+
void NecessaryBindings::computeBindings(
3499+
IRGenModule &IGM, CanSILFunctionType origType,
34973500
bool considerParameterSources) {
34983501

3499-
NecessaryBindings bindings(subs);
3500-
35013502
// Bail out early if we don't have polymorphic parameters.
35023503
if (!hasPolymorphicParameters(origType))
3503-
return bindings;
3504+
return;
35043505

35053506
// Figure out what we're actually required to pass:
35063507
PolymorphicConvention convention(IGM, origType, considerParameterSources);
@@ -3513,14 +3514,14 @@ NecessaryBindings NecessaryBindings::computeBindings(
35133514
continue;
35143515

35153516
case MetadataSource::Kind::GenericLValueMetadata:
3516-
bindings.addRequirement(GenericRequirement::forMetadata(
3517+
addRequirement(GenericRequirement::forMetadata(
35173518
getOrigSelfType(IGM, origType)));
35183519
continue;
35193520

35203521
case MetadataSource::Kind::SelfMetadata:
35213522
// Async functions pass the SelfMetadata and SelfWitnessTable parameters
35223523
// along explicitly.
3523-
bindings.addRequirement(GenericRequirement::forMetadata(
3524+
addRequirement(GenericRequirement::forMetadata(
35243525
getOrigSelfType(IGM, origType)));
35253526
continue;
35263527

@@ -3538,10 +3539,8 @@ NecessaryBindings NecessaryBindings::computeBindings(
35383539
// - unfulfilled requirements
35393540
convention.enumerateUnfulfilledRequirements(
35403541
[&](GenericRequirement requirement) {
3541-
bindings.addRequirement(requirement);
3542+
addRequirement(requirement);
35423543
});
3543-
3544-
return bindings;
35453544
}
35463545

35473546
/// The information we need to record in generic type metadata
@@ -3592,7 +3591,8 @@ void irgen::emitInitOfGenericRequirementsBuffer(IRGenFunction &IGF,
35923591
ArrayRef<GenericRequirement> requirements,
35933592
Address buffer,
35943593
MetadataState metadataState,
3595-
SubstitutionMap subs) {
3594+
SubstitutionMap subs,
3595+
bool onHeapPacks) {
35963596
if (requirements.empty()) return;
35973597

35983598
// Cast the buffer to %type**.
@@ -3607,7 +3607,7 @@ void irgen::emitInitOfGenericRequirementsBuffer(IRGenFunction &IGF,
36073607
}
36083608

36093609
llvm::Value *value = emitGenericRequirementFromSubstitutions(
3610-
IGF, requirements[index], metadataState, subs);
3610+
IGF, requirements[index], metadataState, subs, onHeapPacks);
36113611
slot = IGF.Builder.CreateElementBitCast(slot,
36123612
requirements[index].getType(IGF.IGM));
36133613
IGF.Builder.CreateStore(value, slot);
@@ -3618,7 +3618,8 @@ llvm::Value *
36183618
irgen::emitGenericRequirementFromSubstitutions(IRGenFunction &IGF,
36193619
GenericRequirement requirement,
36203620
MetadataState metadataState,
3621-
SubstitutionMap subs) {
3621+
SubstitutionMap subs,
3622+
bool onHeapPacks) {
36223623
CanType depTy = requirement.getTypeParameter();
36233624
CanType argType = depTy.subst(subs)->getCanonicalType();
36243625

@@ -3627,16 +3628,39 @@ irgen::emitGenericRequirementFromSubstitutions(IRGenFunction &IGF,
36273628
return IGF.emitPackShapeExpression(argType);
36283629

36293630
case GenericRequirement::Kind::Metadata:
3630-
case GenericRequirement::Kind::MetadataPack:
36313631
return IGF.emitTypeMetadataRef(argType, metadataState).getMetadata();
36323632

3633-
case GenericRequirement::Kind::WitnessTable:
3633+
case GenericRequirement::Kind::MetadataPack: {
3634+
auto metadata = IGF.emitTypeMetadataRef(argType, metadataState).getMetadata();
3635+
metadata = IGF.Builder.CreateBitCast(metadata, IGF.IGM.TypeMetadataPtrPtrTy);
3636+
3637+
// FIXME: We should track if this pack is already known to be on the heap
3638+
if (onHeapPacks) {
3639+
auto shape = IGF.emitPackShapeExpression(argType);
3640+
metadata = IGF.Builder.CreateCall(IGF.IGM.getAllocateMetadataPackFunctionPointer(),
3641+
{metadata, shape});
3642+
}
3643+
3644+
return metadata;
3645+
}
3646+
3647+
case GenericRequirement::Kind::WitnessTable: {
3648+
auto conformance = subs.lookupConformance(depTy, requirement.getProtocol());
3649+
return emitWitnessTableRef(IGF, argType, conformance);
3650+
}
3651+
36343652
case GenericRequirement::Kind::WitnessTablePack: {
3635-
auto proto = requirement.getProtocol();
3636-
auto conformance = subs.lookupConformance(depTy, proto);
3637-
assert(conformance.getRequirement() == proto);
3638-
llvm::Value *metadata = nullptr;
3639-
auto wtable = emitWitnessTableRef(IGF, argType, &metadata, conformance);
3653+
auto conformance = subs.lookupConformance(depTy, requirement.getProtocol());
3654+
auto wtable = emitWitnessTableRef(IGF, argType, conformance);
3655+
wtable = IGF.Builder.CreateBitCast(wtable, IGF.IGM.WitnessTablePtrPtrTy);
3656+
3657+
// FIXME: We should track if this pack is already known to be on the heap
3658+
if (onHeapPacks) {
3659+
auto shape = IGF.emitPackShapeExpression(argType);
3660+
wtable = IGF.Builder.CreateCall(IGF.IGM.getAllocateWitnessTablePackFunctionPointer(),
3661+
{wtable, shape});
3662+
}
3663+
36403664
return wtable;
36413665
}
36423666
}
@@ -3651,10 +3675,10 @@ void GenericTypeRequirements::bindFromBuffer(IRGenFunction &IGF,
36513675
}
36523676

36533677
void irgen::bindFromGenericRequirementsBuffer(IRGenFunction &IGF,
3654-
ArrayRef<GenericRequirement> requirements,
3655-
Address buffer,
3656-
MetadataState metadataState,
3657-
SubstitutionMap subs) {
3678+
ArrayRef<GenericRequirement> requirements,
3679+
Address buffer,
3680+
MetadataState metadataState,
3681+
SubstitutionMap subs) {
36583682
if (requirements.empty()) return;
36593683

36603684
// Cast the buffer to %type**.

lib/IRGen/GenericRequirement.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,15 @@ llvm::Value *
5656
emitGenericRequirementFromSubstitutions(IRGenFunction &IGF,
5757
GenericRequirement requirement,
5858
MetadataState metadataState,
59-
SubstitutionMap subs);
59+
SubstitutionMap subs,
60+
bool onHeapPacks=false);
6061

6162
void emitInitOfGenericRequirementsBuffer(IRGenFunction &IGF,
6263
ArrayRef<GenericRequirement> reqts,
6364
Address buffer,
6465
MetadataState metadataState,
65-
SubstitutionMap subs);
66+
SubstitutionMap subs,
67+
bool onHeapPacks=false);
6668

6769
/// Given a required value, map the requirement into the given
6870
/// context and bind the value.

lib/IRGen/NecessaryBindings.h

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,12 @@ namespace swift {
4444
class NecessaryBindings {
4545
llvm::SetVector<GenericRequirement> RequirementsSet;
4646
SubstitutionMap SubMap;
47+
bool NoEscape;
4748

4849
public:
4950
NecessaryBindings() {}
50-
NecessaryBindings(SubstitutionMap subs) : SubMap(subs) {}
51+
NecessaryBindings(SubstitutionMap subs, bool noEscape)
52+
: SubMap(subs), NoEscape(noEscape) {}
5153

5254
SubstitutionMap getSubstitutionMap() const {
5355
return SubMap;
@@ -58,7 +60,8 @@ class NecessaryBindings {
5860
static NecessaryBindings forPartialApplyForwarder(IRGenModule &IGM,
5961
CanSILFunctionType origType,
6062
SubstitutionMap subs,
61-
bool considerParameterSources = true);
63+
bool noEscape,
64+
bool considerParameterSources);
6265

6366
void addRequirement(GenericRequirement requirement) {
6467
auto type = requirement.getTypeParameter().subst(SubMap);
@@ -93,10 +96,9 @@ class NecessaryBindings {
9396
}
9497

9598
private:
96-
static NecessaryBindings computeBindings(IRGenModule &IGM,
97-
CanSILFunctionType origType,
98-
SubstitutionMap subs,
99-
bool considerParameterSources);
99+
void computeBindings(IRGenModule &IGM,
100+
CanSILFunctionType origType,
101+
bool considerParameterSources);
100102
};
101103

102104
} // end namespace irgen

test/IRGen/variadic_generic_captures.swift

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
// Because of -enable-experimental-feature VariadicGenerics
44
// REQUIRES: asserts
55

6+
public func takesNoEscape(_: () -> ()) {}
7+
68
public func has_metadata_pack<each T>(t: repeat each T) -> () -> () {
79
return { _ = (repeat each T).self }
810
}
@@ -17,8 +19,9 @@ public func has_metadata_pack<each T>(t: repeat each T) -> () -> () {
1719
// CHECK: store [[INT]] %1, [[INT]]* [[SHAPE_PTR]]
1820

1921
// CHECK: [[T_ADDR:%.*]] = getelementptr inbounds %swift.type*, %swift.type** [[GENERIC_ARGS]], i32 1
22+
// CHECK: [[T_HEAP:%.*]] = call swiftcc %swift.type** @swift_allocateMetadataPack(%swift.type** %T, [[INT]] %1)
2023
// CHECK: [[T_ADDR2:%.*]] = bitcast %swift.type** [[T_ADDR]] to %swift.type***
21-
// CHECK: store %swift.type** %T, %swift.type*** [[T_ADDR2]]
24+
// CHECK: store %swift.type** [[T_HEAP]], %swift.type*** [[T_ADDR2]]
2225

2326
// CHECK: [[CONTEXT1:%.*]] = insertvalue {{.*}} @"$s25variadic_generic_captures17has_metadata_pack1tyycxxQp_tRvzlFyycfU_TA"
2427
// CHECK: ret { i8*, %swift.refcounted* } [[CONTEXT1]]
@@ -38,6 +41,42 @@ public func has_metadata_pack<each T>(t: repeat each T) -> () -> () {
3841
// CHECK: tail call swiftcc void @"$s25variadic_generic_captures17has_metadata_pack1tyycxxQp_tRvzlFyycfU_"([[INT]] [[SHAPE]], %swift.type** [[T]])
3942
// CHECK: ret void
4043

44+
public func has_metadata_pack_noescape<each T>(t: repeat each T) {
45+
takesNoEscape { _ = (repeat each T).self }
46+
}
47+
48+
// CHECK-LABEL: define{{( protected)?}}{{( dllexport)?}} swiftcc void @"$s25variadic_generic_captures26has_metadata_pack_noescape1tyxxQp_tRvzlF"(%swift.opaque** noalias nocapture %0, i64 %1, %swift.type** %T) #0 {
49+
// CHECK: [[CONTEXT0:%.*]] = alloca i8, [[INT]]
50+
// CHECK: [[CONTEXT1:%.*]] = bitcast i8* [[CONTEXT0]] to %swift.opaque*
51+
// CHECK: [[CONTEXT:%.*]] = bitcast %swift.opaque* [[CONTEXT1]] to <{{.*}}>*
52+
53+
// CHECK: [[GENERIC_ARGS_ADDR:%.*]] = getelementptr inbounds {{.*}}* [[CONTEXT]], i32 0, i32 {{(1|2)}}
54+
// CHECK: [[GENERIC_ARGS:%.*]] = bitcast {{.*}} [[GENERIC_ARGS_ADDR]] to %swift.type**
55+
// CHECK: [[SHAPE_PTR:%.*]] = bitcast %swift.type** [[GENERIC_ARGS]] to [[INT]]*
56+
// CHECK: store [[INT]] %1, [[INT]]* [[SHAPE_PTR]]
57+
58+
// CHECK: [[T_ADDR:%.*]] = getelementptr inbounds %swift.type*, %swift.type** [[GENERIC_ARGS]], i32 1
59+
// CHECK: [[T_ADDR2:%.*]] = bitcast %swift.type** [[T_ADDR]] to %swift.type***
60+
// CHECK: store %swift.type** %T, %swift.type*** [[T_ADDR2]]
61+
62+
// CHECK: call swiftcc void @"$s25variadic_generic_captures13takesNoEscapeyyyyXEF"(
63+
// CHECK: ret void
64+
65+
// CHECK-LABEL: define internal swiftcc void @"$s25variadic_generic_captures26has_metadata_pack_noescape1tyxxQp_tRvzlFyyXEfU_TA"(%swift.refcounted* swiftself %0)
66+
// CHECK: [[CONTEXT:%.*]] = bitcast %swift.refcounted* %0 to {{.*}}*
67+
// CHECK: [[GENERIC_ARGS_ADDR:%.*]] = getelementptr inbounds {{.*}}* [[CONTEXT]], i32 0, i32 {{(1|2)}}
68+
// CHECK: [[GENERIC_ARGS:%.*]] = bitcast {{.*}} to %swift.type**
69+
70+
// CHECK: [[SHAPE_PTR:%.*]] = bitcast %swift.type** [[GENERIC_ARGS]] to [[INT]]*
71+
// CHECK: [[SHAPE:%.*]] = load [[INT]], [[INT]]* [[SHAPE_PTR]]
72+
73+
// CHECK: [[T_ADDR:%.*]] = getelementptr inbounds %swift.type*, %swift.type** [[GENERIC_ARGS]], i32 1
74+
// CHECK: [[T_PTR:%.*]] = bitcast %swift.type** [[T_ADDR]] to %swift.type***
75+
// CHECK: [[T:%.*]] = load %swift.type**, %swift.type*** [[T_PTR]]
76+
77+
// CHECK: tail call swiftcc void @"$s25variadic_generic_captures26has_metadata_pack_noescape1tyxxQp_tRvzlFyyXEfU_"([[INT]] [[SHAPE]], %swift.type** [[T]])
78+
// CHECK: ret void
79+
4180
public func has_witness_table_pack<each T: Sequence>(t: repeat each T) -> () -> () {
4281
return { _ = (repeat (each T).Element).self }
4382
}

test/Interpreter/variadic_generic_captures.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,20 @@ types.test("WitnessTable2") {
4343
expectEqual((Int, String, Bool).self, hasWitnessTablePack2([[1]], [["hi"]], [[false]])())
4444
}
4545

46+
// Test lifetimes of captured packs
47+
func lifetimeTest1() -> () -> Any.Type {
48+
return hasMetadataPack("hello", Set<Int>())
49+
}
50+
51+
func lifetimeTest2() -> () -> Any.Type {
52+
return hasMetadataPack(3, 1.0)
53+
}
54+
55+
types.test("Lifetime") {
56+
let fn1 = lifetimeTest1()
57+
let fn2 = lifetimeTest2()
58+
expectEqual((String, Set<Int>).self, fn1())
59+
expectEqual((Int, Double).self, fn2())
60+
}
61+
4662
runAllTests()

0 commit comments

Comments
 (0)