Skip to content

Commit fbd21e1

Browse files
authored
Merge pull request #19296 from jckarter/opaque-existential-extra-inhabitants
Give opaque existential containers extra inhabitants.
2 parents 7c186dd + b4abe85 commit fbd21e1

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)