Skip to content

Commit b4abe85

Browse files
committed
Give opaque existential containers extra inhabitants.
We can use the extra inhabitants of the type metadata field as extra inhabitants of the entire existential container, allowing `Any?` and similar types to be the same size as non-optional existentials.
1 parent 8da411b commit b4abe85

11 files changed

+169
-84
lines changed

lib/IRGen/GenExistential.cpp

+60-4
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,11 @@ namespace {
109109
Address projectMetadataRef(IRGenFunction &IGF, Address addr) {
110110
return IGF.Builder.CreateStructGEP(addr, 1, getFixedBufferSize(IGF.IGM));
111111
}
112+
113+
/// Give the offset of the metadata field of an existential object.
114+
Size getMetadataRefOffset(IRGenModule &IGM) {
115+
return getFixedBufferSize(IGM);
116+
}
112117

113118
/// Given the address of an existential object, load its metadata
114119
/// object.
@@ -810,12 +815,12 @@ class OpaqueExistentialTypeInfo final :
810815
IndirectTypeInfo<OpaqueExistentialTypeInfo, FixedTypeInfo>>;
811816
friend super;
812817

813-
// FIXME: We could get spare bits out of the metadata and/or witness
814-
// pointers.
815818
OpaqueExistentialTypeInfo(ArrayRef<const ProtocolDecl *> protocols,
816-
llvm::Type *ty, Size size, Alignment align)
819+
llvm::Type *ty, Size size,
820+
SpareBitVector &&spareBits,
821+
Alignment align)
817822
: super(protocols, ty, size,
818-
SpareBitVector::getConstant(size.getValueInBits(), false), align,
823+
std::move(spareBits), align,
819824
IsNotPOD, IsBitwiseTakable, IsFixedSize) {}
820825

821826
public:
@@ -902,6 +907,37 @@ class OpaqueExistentialTypeInfo final :
902907
call->setDoesNotThrow();
903908
return;
904909
}
910+
911+
// Opaque existentials have extra inhabitants and spare bits in their type
912+
// metadata pointer, matching those of a standalone thick metatype (which
913+
// in turn match those of a heap object).
914+
unsigned getFixedExtraInhabitantCount(IRGenModule &IGM) const override {
915+
return getHeapObjectExtraInhabitantCount(IGM);
916+
}
917+
APInt getFixedExtraInhabitantValue(IRGenModule &IGM,
918+
unsigned bits,
919+
unsigned index) const override {
920+
auto offset = getLayout().getMetadataRefOffset(IGM).getValueInBits();
921+
return getHeapObjectFixedExtraInhabitantValue(IGM, bits, index, offset);
922+
}
923+
APInt getFixedExtraInhabitantMask(IRGenModule &IGM) const override {
924+
auto mask = APInt::getAllOnesValue(IGM.getPointerSize().getValueInBits());
925+
mask = mask.zext(getFixedSize().getValueInBits());
926+
mask = mask.shl(getLayout().getMetadataRefOffset(IGM).getValueInBits());
927+
return mask;
928+
}
929+
llvm::Value *getExtraInhabitantIndex(IRGenFunction &IGF,
930+
Address src, SILType T,
931+
bool isOutlined) const override {
932+
auto type = getLayout().projectMetadataRef(IGF, src);
933+
return getHeapObjectExtraInhabitantIndex(IGF, type);
934+
}
935+
void storeExtraInhabitant(IRGenFunction &IGF, llvm::Value *index,
936+
Address dest, SILType T, bool isOutlined)
937+
const override {
938+
auto type = getLayout().projectMetadataRef(IGF, dest);
939+
return storeHeapObjectExtraInhabitant(IGF, index, type);
940+
}
905941
};
906942

907943

@@ -1407,7 +1443,27 @@ static const TypeInfo *createExistentialTypeInfo(IRGenModule &IGM, CanType T) {
14071443
OpaqueExistentialLayout opaque(protosWithWitnessTables.size());
14081444
Alignment align = opaque.getAlignment(IGM);
14091445
Size size = opaque.getSize(IGM);
1446+
// There are spare bits in the metadata pointer and witness table pointers
1447+
// consistent with a native object reference.
1448+
SpareBitVector spareBits;
1449+
spareBits.appendClearBits(size.getValueInBits());
1450+
/* TODO: There are spare bits we could theoretically use in the type metadata
1451+
and witness table pointers, but opaque existentials are currently address-
1452+
only, and we can't soundly take advantage of spare bits for in-memory
1453+
representations.
1454+
1455+
auto metadataOffset = opaque.getMetadataRefOffset(IGM);
1456+
spareBits.appendClearBits(metadataOffset.getValueInBits());
1457+
auto typeSpareBits = IGM.getHeapObjectSpareBits();
1458+
spareBits.append(typeSpareBits);
1459+
auto witnessSpareBits =
1460+
IGM.getWitnessTablePtrSpareBits();
1461+
for (unsigned i = 0, e = protosWithWitnessTables.size(); i < e; ++i)
1462+
spareBits.append(witnessSpareBits);
1463+
assert(spareBits.size() == size.getValueInBits());
1464+
*/
14101465
return OpaqueExistentialTypeInfo::create(protosWithWitnessTables, type, size,
1466+
std::move(spareBits),
14111467
align);
14121468
}
14131469

lib/IRGen/GenOpaque.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
using namespace swift;
3939
using namespace irgen;
4040

41-
/// A fixed-size buffer is always 16 bytes and pointer-aligned.
41+
/// A fixed-size buffer is always three pointers in size and pointer-aligned.
4242
/// If we align them more, we'll need to introduce padding to
4343
/// make protocol types work.
4444
Size irgen::getFixedBufferSize(IRGenModule &IGM) {

stdlib/public/Reflection/TypeLowering.cpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,10 @@ unsigned RecordTypeInfoBuilder::addField(unsigned fieldSize,
436436
switch (Kind) {
437437
// The extra inhabitants of a struct are the same as the extra
438438
// inhabitants of the field that has the most.
439+
// Opaque existentials pick up the extra inhabitants of their type metadata
440+
// field.
439441
case RecordKind::Struct:
442+
case RecordKind::OpaqueExistential:
440443
NumExtraInhabitants = std::max(NumExtraInhabitants, numExtraInhabitants);
441444
break;
442445

@@ -450,7 +453,6 @@ unsigned RecordTypeInfoBuilder::addField(unsigned fieldSize,
450453
case RecordKind::Invalid:
451454
case RecordKind::MultiPayloadEnum:
452455
case RecordKind::NoPayloadEnum:
453-
case RecordKind::OpaqueExistential:
454456
case RecordKind::SinglePayloadEnum:
455457
case RecordKind::ThickFunction:
456458
case RecordKind::Tuple:

stdlib/public/runtime/ExistentialMetadataImpl.h

+24-2
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,18 @@ struct LLVM_LIBRARY_VISIBILITY OpaqueExistentialBox
328328
static constexpr size_t stride = sizeof(Container);
329329
static constexpr size_t isPOD = false;
330330
static constexpr bool isBitwiseTakable = true;
331-
static constexpr unsigned numExtraInhabitants = 0;
331+
static constexpr unsigned numExtraInhabitants =
332+
swift_getHeapObjectExtraInhabitantCount();
333+
334+
static void storeExtraInhabitant(Container *dest, int index) {
335+
swift_storeHeapObjectExtraInhabitant((HeapObject**)&dest->Header.Type,
336+
index);
337+
}
338+
339+
static int getExtraInhabitantIndex(const Container *src) {
340+
return swift_getHeapObjectExtraInhabitantIndex(
341+
(HeapObject* const *)&src->Header.Type);
342+
}
332343
};
333344

334345
/// A non-fixed box implementation class for an opaque existential
@@ -371,7 +382,18 @@ struct LLVM_LIBRARY_VISIBILITY NonFixedOpaqueExistentialBox
371382
};
372383

373384
using type = Container;
374-
static constexpr unsigned numExtraInhabitants = 0;
385+
static constexpr unsigned numExtraInhabitants =
386+
swift_getHeapObjectExtraInhabitantCount();
387+
388+
static void storeExtraInhabitant(Container *dest, int index) {
389+
swift_storeHeapObjectExtraInhabitant((HeapObject**)&dest->Header.Type,
390+
index);
391+
}
392+
393+
static int getExtraInhabitantIndex(const Container *src) {
394+
return swift_getHeapObjectExtraInhabitantIndex(
395+
(HeapObject* const *)&src->Header.Type);
396+
}
375397
};
376398

377399
/// A common base class for fixed and non-fixed class-existential box

stdlib/public/runtime/Metadata.cpp

+11-5
Original file line numberDiff line numberDiff line change
@@ -2741,7 +2741,7 @@ class ExistentialCacheEntry {
27412741

27422742
class OpaqueExistentialValueWitnessTableCacheEntry {
27432743
public:
2744-
ValueWitnessTable Data;
2744+
ExtraInhabitantsValueWitnessTable Data;
27452745

27462746
OpaqueExistentialValueWitnessTableCacheEntry(unsigned numTables);
27472747

@@ -2798,9 +2798,11 @@ class ClassExistentialValueWitnessTableCacheEntry {
27982798
/// The uniquing structure for existential type metadata.
27992799
static SimpleGlobalCache<ExistentialCacheEntry> ExistentialTypes;
28002800

2801-
static const ValueWitnessTable OpaqueExistentialValueWitnesses_0 =
2801+
static const ExtraInhabitantsValueWitnessTable
2802+
OpaqueExistentialValueWitnesses_0 =
28022803
ValueWitnessTableForBox<OpaqueExistentialBox<0>>::table;
2803-
static const ValueWitnessTable OpaqueExistentialValueWitnesses_1 =
2804+
static const ExtraInhabitantsValueWitnessTable
2805+
OpaqueExistentialValueWitnesses_1 =
28042806
ValueWitnessTableForBox<OpaqueExistentialBox<1>>::table;
28052807

28062808
/// The standard metadata for Any.
@@ -2855,7 +2857,6 @@ OpaqueExistentialValueWitnessTableCacheEntry::
28552857
OpaqueExistentialValueWitnessTableCacheEntry(unsigned numWitnessTables) {
28562858
using Box = NonFixedOpaqueExistentialBox;
28572859
using Witnesses = NonFixedValueWitnesses<Box, /*known allocated*/ true>;
2858-
static_assert(!Witnesses::hasExtraInhabitants, "no extra inhabitants");
28592860

28602861
#define WANT_ONLY_REQUIRED_VALUE_WITNESSES
28612862
#define VALUE_WITNESS(LOWER_ID, UPPER_ID) \
@@ -2869,8 +2870,13 @@ OpaqueExistentialValueWitnessTableCacheEntry(unsigned numWitnessTables) {
28692870
.withPOD(false)
28702871
.withBitwiseTakable(true)
28712872
.withInlineStorage(false)
2872-
.withExtraInhabitants(false);
2873+
.withExtraInhabitants(true);
28732874
Data.stride = Box::Container::getStride(numWitnessTables);
2875+
2876+
// Extra inhabitant behavior does not change with the number of witnesses.
2877+
Data.extraInhabitantFlags = Witnesses::extraInhabitantFlags;
2878+
Data.getExtraInhabitantIndex = Witnesses::getExtraInhabitantIndex;
2879+
Data.storeExtraInhabitant = Witnesses::storeExtraInhabitant;
28742880

28752881
assert(getNumWitnessTables() == numWitnessTables);
28762882
}

stdlib/public/runtime/MetadataImpl.h

+2-3
Original file line numberDiff line numberDiff line change
@@ -918,13 +918,12 @@ struct NonFixedValueWitnesses :
918918

919919
static void storeExtraInhabitant(OpaqueValue *dest, int index,
920920
const Metadata *self) {
921-
Box::storeExtraInhabitant((typename Box::type*) dest, index, self);
921+
Box::storeExtraInhabitant((typename Box::type*) dest, index);
922922
}
923923

924924
static int getExtraInhabitantIndex(const OpaqueValue *src,
925925
const Metadata *self) {
926-
return Box::getExtraInhabitantIndex((typename Box::type const *) src,
927-
self);
926+
return Box::getExtraInhabitantIndex((typename Box::type const *) src);
928927
}
929928
};
930929

test/IRGen/existentials.sil

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import Swift
88
protocol P {}
99

1010
// NonBitwiseTakableBit = 0x00100000. This struct is bitwise takable because
11-
// 0x30007 = 196615.
12-
// CHECK: @"$S12existentials14BitwiseTakableVWV" = internal constant [11 x i8*] {{.*}} i8* inttoptr (i64 196615 to i8*)
11+
// 0x70007 = 458759
12+
// CHECK: @"$S12existentials14BitwiseTakableVWV" = internal constant [14 x i8*] {{.*}} i8* inttoptr (i64 458759 to i8*)
1313
struct BitwiseTakable {
1414
var p: P
1515
}

0 commit comments

Comments
 (0)