Skip to content

Commit 822ef7f

Browse files
jckarterDavide Italiano
authored andcommitted
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 09857c3 commit 822ef7f

File tree

11 files changed

+169
-84
lines changed

11 files changed

+169
-84
lines changed

lib/IRGen/GenExistential.cpp

Lines changed: 60 additions & 4 deletions
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.
@@ -811,12 +816,12 @@ class OpaqueExistentialTypeInfo final :
811816
IndirectTypeInfo<OpaqueExistentialTypeInfo, FixedTypeInfo>>;
812817
friend super;
813818

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

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

908944

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

lib/IRGen/GenOpaque.cpp

Lines changed: 1 addition & 1 deletion
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

Lines changed: 3 additions & 1 deletion
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

Lines changed: 24 additions & 2 deletions
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

Lines changed: 11 additions & 5 deletions
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

Lines changed: 2 additions & 3 deletions
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

Lines changed: 2 additions & 2 deletions
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)