Skip to content

Commit 210ef1f

Browse files
committed
[IRGen] Emit witness tables for packs.
This mostly mirrors what's already implemented for metadata pack emission.
1 parent b6972bd commit 210ef1f

File tree

5 files changed

+293
-1
lines changed

5 files changed

+293
-1
lines changed

lib/IRGen/GenPack.cpp

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@
1515
//===----------------------------------------------------------------------===//
1616

1717
#include "GenPack.h"
18+
#include "GenProto.h"
1819
#include "swift/AST/Decl.h"
1920
#include "swift/AST/GenericEnvironment.h"
2021
#include "swift/AST/IRGenOptions.h"
22+
#include "swift/AST/PackConformance.h"
2123
#include "swift/AST/Types.h"
2224
#include "swift/SIL/SILModule.h"
2325
#include "swift/SIL/SILType.h"
@@ -364,6 +366,230 @@ irgen::emitTypeMetadataPackRef(IRGenFunction &IGF, CanPackType packType,
364366
return response;
365367
}
366368

369+
static Address emitFixedSizeWitnessTablePack(IRGenFunction &IGF,
370+
CanPackType packType,
371+
PackConformance *packConformance) {
372+
assert(!packType->containsPackExpansionType());
373+
374+
unsigned elementCount = packType->getNumElements();
375+
auto allocType =
376+
llvm::ArrayType::get(IGF.IGM.WitnessTablePtrTy, elementCount);
377+
378+
auto pack = IGF.createAlloca(allocType, IGF.IGM.getPointerAlignment());
379+
IGF.Builder.CreateLifetimeStart(pack,
380+
IGF.IGM.getPointerSize() * elementCount);
381+
382+
for (unsigned i : indices(packType->getElementTypes())) {
383+
Address slot =
384+
IGF.Builder.CreateStructGEP(pack, i, IGF.IGM.getPointerSize());
385+
386+
auto conformance = packConformance->getPatternConformances()[i];
387+
auto *wtable =
388+
emitWitnessTableRef(IGF, packType.getElementType(i),
389+
/*srcMetadataCache=*/nullptr, conformance);
390+
391+
IGF.Builder.CreateStore(wtable, slot);
392+
}
393+
394+
return pack;
395+
}
396+
397+
static llvm::Value *emitPackExpansionElementWitnessTable(
398+
IRGenFunction &IGF, CanPackExpansionType expansionTy,
399+
ProtocolConformanceRef conformance, llvm::Value *index) {
400+
auto patternTy = expansionTy.getPatternType();
401+
402+
// Find all the pack archetypes appearing in the pattern type.
403+
SmallVector<Type, 2> patternPacks;
404+
patternTy->getTypeParameterPacks(patternPacks);
405+
406+
// Get the outer generic signature and environment.
407+
auto *genericEnv = cast<PackArchetypeType>(expansionTy.getCountType())
408+
->getGenericEnvironment();
409+
auto subMap = genericEnv->getForwardingSubstitutionMap();
410+
411+
auto genericSig = genericEnv->getGenericSignature().getCanonicalSignature();
412+
413+
// Create an opened element signature and environment.
414+
auto elementSig = IGF.IGM.Context.getOpenedElementSignature(
415+
genericSig, expansionTy.getCountType());
416+
auto *elementEnv = GenericEnvironment::forOpenedElement(
417+
elementSig, UUID::fromTime(), expansionTy.getCountType(), subMap);
418+
419+
// Open each pack archetype.
420+
for (auto patternPackType : patternPacks) {
421+
// Get the witness table for the pack archetype.
422+
auto patternPackArchetype =
423+
cast<PackArchetypeType>(patternPackType->getCanonicalType());
424+
for (auto *proto : patternPackArchetype->getConformsTo()) {
425+
auto conf = ProtocolConformanceRef(proto);
426+
auto patternPack = emitWitnessTableRef(
427+
IGF, patternPackArchetype, /*srcMetadataCache=*/nullptr, conf);
428+
429+
patternPack = IGF.Builder.CreatePointerCast(patternPack,
430+
IGF.IGM.WitnessTablePtrPtrTy);
431+
432+
Address patternPackAddress(patternPack, IGF.IGM.WitnessTablePtrTy,
433+
IGF.IGM.getPointerAlignment());
434+
435+
// Load the witness table pack element from the current source index.
436+
Address fromPtr(
437+
IGF.Builder.CreateInBoundsGEP(patternPackAddress.getElementType(),
438+
patternPackAddress.getAddress(), index),
439+
patternPackAddress.getElementType(),
440+
patternPackAddress.getAlignment());
441+
auto *wtable = IGF.Builder.CreateLoad(fromPtr);
442+
443+
// Bind the witness table pack element to the element archetype.
444+
auto elementArchetype = elementEnv->mapPackTypeIntoElementContext(
445+
patternPackArchetype->getInterfaceType());
446+
447+
IGF.setScopedLocalTypeData(
448+
CanType(elementArchetype),
449+
LocalTypeDataKind::forProtocolWitnessTable(conf), wtable);
450+
}
451+
}
452+
453+
// Replace pack archetypes with element archetypes in the pattern type.
454+
auto instantiatedPatternTy =
455+
elementEnv
456+
->mapPackTypeIntoElementContext(patternTy->mapTypeOutOfContext())
457+
->getCanonicalType();
458+
459+
// FIXME: Handle witness table packs for associatedtype's conformances.
460+
461+
// Emit the element witness table.
462+
auto *wtable = emitWitnessTableRef(IGF, instantiatedPatternTy,
463+
/*srcMetadataCache=*/nullptr, conformance);
464+
return wtable;
465+
}
466+
467+
static void emitExpansionWitnessTablePack(IRGenFunction &IGF, Address pack,
468+
CanPackExpansionType expansionTy,
469+
ProtocolConformanceRef conformance,
470+
llvm::Value *dynamicIndex,
471+
llvm::Value *dynamicLength) {
472+
auto *prev = IGF.Builder.GetInsertBlock();
473+
auto *check = IGF.createBasicBlock("pack-expansion-check");
474+
auto *loop = IGF.createBasicBlock("pack-expansion-loop");
475+
auto *rest = IGF.createBasicBlock("pack-expansion-rest");
476+
477+
IGF.Builder.CreateBr(check);
478+
IGF.Builder.emitBlock(check);
479+
480+
// An index into the source witness table pack.
481+
auto *phi = IGF.Builder.CreatePHI(IGF.IGM.SizeTy, 2);
482+
phi->addIncoming(llvm::ConstantInt::get(IGF.IGM.SizeTy, 0), prev);
483+
484+
// If we reach the end, jump to the continuation block.
485+
auto *cond = IGF.Builder.CreateICmpULT(phi, dynamicLength);
486+
IGF.Builder.CreateCondBr(cond, loop, rest);
487+
488+
IGF.Builder.emitBlock(loop);
489+
490+
auto *element =
491+
emitPackExpansionElementWitnessTable(IGF, expansionTy, conformance, phi);
492+
493+
// Store the element witness table into to the current destination index.
494+
auto *eltIndex = IGF.Builder.CreateAdd(dynamicIndex, phi);
495+
Address eltPtr(IGF.Builder.CreateInBoundsGEP(pack.getElementType(),
496+
pack.getAddress(), eltIndex),
497+
pack.getElementType(), pack.getAlignment());
498+
499+
IGF.Builder.CreateStore(element, eltPtr);
500+
501+
// Increment our counter.
502+
auto *next =
503+
IGF.Builder.CreateAdd(phi, llvm::ConstantInt::get(IGF.IGM.SizeTy, 1));
504+
505+
phi->addIncoming(next, loop);
506+
507+
// Repeat the loop.
508+
IGF.Builder.CreateBr(check);
509+
510+
// Fall through.
511+
IGF.Builder.emitBlock(rest);
512+
}
513+
514+
StackAddress irgen::emitWitnessTablePack(IRGenFunction &IGF,
515+
CanPackType packType,
516+
PackConformance *packConformance) {
517+
auto *shape = IGF.emitPackShapeExpression(packType);
518+
519+
if (auto *constantInt = dyn_cast<llvm::ConstantInt>(shape)) {
520+
assert(packType->getNumElements() == constantInt->getValue());
521+
return StackAddress(
522+
emitFixedSizeWitnessTablePack(IGF, packType, packConformance));
523+
}
524+
525+
assert(packType->containsPackExpansionType());
526+
auto pack = IGF.emitDynamicAlloca(IGF.IGM.WitnessTablePtrTy, shape,
527+
IGF.IGM.getPointerAlignment(),
528+
/*allowTaskAlloc=*/true);
529+
530+
auto index = 0;
531+
auto visitFn = [&](CanType eltTy, unsigned staticIndex,
532+
llvm::Value *dynamicIndex, llvm::Value *dynamicLength) {
533+
if (staticIndex != 0 || dynamicIndex == nullptr) {
534+
auto *constant = llvm::ConstantInt::get(IGF.IGM.SizeTy, staticIndex);
535+
accumulateSum(IGF, dynamicIndex, constant);
536+
}
537+
538+
auto conformance = packConformance->getPatternConformances()[index];
539+
if (auto expansionTy = dyn_cast<PackExpansionType>(eltTy)) {
540+
emitExpansionWitnessTablePack(IGF, pack.getAddress(), expansionTy,
541+
conformance, dynamicIndex, dynamicLength);
542+
} else {
543+
Address eltPtr(
544+
IGF.Builder.CreateInBoundsGEP(pack.getAddress().getElementType(),
545+
pack.getAddressPointer(), dynamicIndex),
546+
pack.getAddress().getElementType(), pack.getAlignment());
547+
548+
auto *wtable = emitWitnessTableRef(
549+
IGF, eltTy, /*srcMetadataCache=*/nullptr, conformance);
550+
IGF.Builder.CreateStore(wtable, eltPtr);
551+
}
552+
++index;
553+
};
554+
555+
visitPackExplosion(IGF, packType, visitFn);
556+
557+
return pack;
558+
}
559+
560+
void irgen::cleanupWitnessTablePack(IRGenFunction &IGF, StackAddress pack,
561+
Optional<unsigned> elementCount) {
562+
if (pack.getExtraInfo()) {
563+
IGF.emitDeallocateDynamicAlloca(pack);
564+
} else {
565+
IGF.Builder.CreateLifetimeEnd(pack.getAddress(),
566+
IGF.IGM.getPointerSize() * (*elementCount));
567+
}
568+
}
569+
570+
llvm::Value *irgen::emitWitnessTablePackRef(IRGenFunction &IGF,
571+
CanPackType packType,
572+
PackConformance *conformance) {
573+
assert(Lowering::TypeConverter::protocolRequiresWitnessTable(
574+
conformance->getProtocol()) &&
575+
"looking up witness table for protocol that doesn't have one");
576+
577+
auto localDataKind =
578+
LocalTypeDataKind::forProtocolWitnessTablePack(conformance);
579+
580+
auto wtable = IGF.tryGetLocalTypeData(packType, localDataKind);
581+
if (wtable)
582+
return wtable;
583+
584+
auto pack = emitWitnessTablePack(IGF, packType, conformance);
585+
586+
auto *result = pack.getAddress().getAddress();
587+
588+
IGF.setScopedLocalTypeData(packType, localDataKind, result);
589+
590+
return result;
591+
}
592+
367593
llvm::Value *
368594
irgen::emitTypeMetadataPackElementRef(IRGenFunction &IGF, CanPackType packType,
369595
llvm::Value *index,

lib/IRGen/GenPack.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,15 @@ void cleanupTypeMetadataPack(IRGenFunction &IGF,
5959
StackAddress pack,
6060
Optional<unsigned> elementCount);
6161

62+
StackAddress emitWitnessTablePack(IRGenFunction &IGF, CanPackType packType,
63+
PackConformance *conformance);
64+
65+
llvm::Value *emitWitnessTablePackRef(IRGenFunction &IGF, CanPackType packType,
66+
PackConformance *conformance);
67+
68+
void cleanupWitnessTablePack(IRGenFunction &IGF, StackAddress pack,
69+
Optional<unsigned> elementCount);
70+
6271
/// Emit the dynamic index of a particular structural component
6372
/// of the given pack type. If the component is a pack expansion, this
6473
/// is the index of the first element of the pack (or where it would be
@@ -70,4 +79,4 @@ llvm::Value *emitIndexOfStructuralPackComponent(IRGenFunction &IGF,
7079
} // end namespace irgen
7180
} // end namespace swift
7281

73-
#endif
82+
#endif

lib/IRGen/GenProto.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
#include "GenHeap.h"
6969
#include "GenMeta.h"
7070
#include "GenOpaque.h"
71+
#include "GenPack.h"
7172
#include "GenPointerAuth.h"
7273
#include "GenPoly.h"
7374
#include "GenType.h"
@@ -3250,6 +3251,9 @@ llvm::Value *irgen::emitWitnessTableRef(IRGenFunction &IGF,
32503251
// conformance info for them. However, that conformance info might be
32513252
// more concrete than we're expecting.
32523253
// TODO: make a best effort to devirtualize, maybe?
3254+
} else if (conformance.isPack()) {
3255+
auto pack = cast<PackType>(srcType);
3256+
return emitWitnessTablePackRef(IGF, pack, conformance.getPack());
32533257
} else {
32543258
concreteConformance = conformance.getConcrete();
32553259
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// RUN: %target-swift-frontend -parse-sil -emit-ir -primary-file %s -enable-experimental-feature VariadicGenerics | %FileCheck %s
2+
3+
// Because of -enable-experimental-feature VariadicGenerics
4+
// REQUIRES: asserts
5+
6+
import Builtin
7+
import Swift
8+
9+
protocol P {}
10+
struct S : P {}
11+
sil_witness_table S : P module main {}
12+
struct S_2 : P {}
13+
sil_witness_table S_2 : P module main {}
14+
struct S_3 : P {}
15+
sil_witness_table S_3 : P module main {}
16+
17+
// CHECK-LABEL: define {{.*}}void @c()
18+
// CHECK: entry:
19+
// CHECK: [[METADATA_PACK:%[^,]+]] = alloca [1 x %swift.type*]
20+
// CHECK: [[WTABLE_PACK:%[^,]+]] = alloca [1 x i8**]
21+
// CHECK: [[METADATA_ELEMENT_0:%[^,]+]] = getelementptr inbounds [1 x %swift.type*], [1 x %swift.type*]* [[METADATA_PACK]]
22+
// CHECK: store %swift.type* bitcast {{.*}}$s26variadic_generic_functions3S_2VMf{{.*}}, %swift.type** [[METADATA_ELEMENT_0]]
23+
// CHECK: [[WTABLE_ELEMENT_0:%[^,]+]] = getelementptr inbounds [1 x i8**], [1 x i8**]* [[WTABLE_PACK]]
24+
// CHECK: store i8** getelementptr inbounds {{.*}}$s26variadic_generic_functions3S_2VAA1PAAWP{{.*}}, i8*** [[WTABLE_ELEMENT_0]]
25+
// CHECK: [[METADATA_PACK_PTR:%[^,]+]] = bitcast [1 x %swift.type*]* [[METADATA_PACK]] to %swift.type**
26+
// CHECK: [[WTABLE_PACK_ADDR:%[^,]+]] = bitcast [1 x i8**]* [[WTABLE_PACK]] to i8***
27+
// CHECK: call swiftcc void @g(i64 1, %swift.type** [[METADATA_PACK_PTR]], i8*** [[WTABLE_PACK_ADDR]])
28+
sil @c : $() -> () {
29+
%g = function_ref @g : $@convention(thin) <T... : P> () -> ()
30+
apply %g<Pack{S_2}>() : $@convention(thin) <T... : P> () -> ()
31+
%ret = tuple ()
32+
return %ret : $()
33+
}
34+
35+
// CHECK: define {{.*}}void @f(i64 %0, %swift.type** %T, i8*** %T.P)
36+
sil @f : $<T... : P> () -> () {
37+
%ret = tuple ()
38+
return %ret : $()
39+
}
40+
41+
// CHECK: define {{.*}}void @g(i64 %0, %swift.type** %T, i8*** %T.P)
42+
sil @g : $<T... : P> () -> () {
43+
%f = function_ref @f : $@convention(thin) <T... : P> () -> ()
44+
apply %f<Pack{repeat each T, S, repeat each T, repeat each T}>() : $@convention(thin) <T... : P> () -> ()
45+
%ret = tuple ()
46+
return %ret : $()
47+
}

test/IRGen/variadic_generic_functions.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,9 @@ func f2<T..., U...>(t: repeat each T, u: repeat each U) {}
1313

1414
// CHECK-LABEL: define hidden swiftcc void @"$s26variadic_generic_functions2f31t1uyxxQp_q_xQptq_Rhzr0_lF"(%swift.opaque** noalias nocapture %0, %swift.opaque** noalias nocapture %1, i64 %2, %swift.type** %T, %swift.type** %U)
1515
func f3<T..., U...>(t: repeat each T, u: repeat each U) where (repeat (each T, each U)): Any {}
16+
17+
protocol P {}
18+
19+
// CHECK-LABEL: define {{.*}}void @f4(%swift.opaque** noalias nocapture %0, i64 %1, %swift.type** %T, i8*** %T.P)
20+
@_silgen_name("f4")
21+
func f4<T... : P>(t: repeat each T) {}

0 commit comments

Comments
 (0)