Skip to content

Commit 95bc009

Browse files
committed
Reflection: Multi-payload enum layout
The approach here is to split this into two cases: - If all case payloads have a fixed size, spare bits may be potentially used to differentiate between cases, and the remote reflection library does not have enough information to compute the layout itself. However, the total size must be fixed, so IRGen just emits a builtin type descriptor (which I need to rename to 'fixed type descriptor' since these are also used for imported value types, and now, certain enums). - If at least one case has a size that depends on a generic parameter or is a resilient type, IRGen does not know the size, but this means fancy tricks with spare bits cannot be used either. The remote reflection library uses the same approach as the runtime, basically taking the maximum of the payload size and alignment, and adding a tag byte. As with single-payload enums, we produce a new kind of RecordTypeInfo, this time with a field for every enum case. All cases start at offset zero (but of course this might change, if for example we put the enum tag before the address point). Also, just as with single-payload enums, there is no remote 'project case index' operation on ReflectionContext yet. So the the main benefit from this change is that we don't entirely give up when doing layout of class instances containing enums; however, tools still cannot look inside the enum values themselves, except in the simplest cases involving optionals. Notably, the remote reflection library finally understands all of the standard library's collection types -- Array, Character, Dictionary, Set, and String.
1 parent b9eb24e commit 95bc009

35 files changed

+626
-536
lines changed

include/swift/Reflection/Records.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,14 @@ enum class FieldDescriptorKind : uint16_t {
111111
Class,
112112
Enum,
113113

114+
// Fixed-size multi-payload enums have a special descriptor format that
115+
// encodes spare bits.
116+
//
117+
// FIXME: Actually implement this. For now, a descriptor with this kind
118+
// just means we also have a builtin descriptor from which we get the
119+
// size and alignment.
120+
MultiPayloadEnum,
121+
114122
// A Swift opaque protocol. There are no fields, just a record for the
115123
// type itself.
116124
Protocol,
@@ -145,6 +153,22 @@ class FieldDescriptor {
145153

146154
using const_iterator = FieldRecordIterator;
147155

156+
bool isEnum() const {
157+
return (Kind == FieldDescriptorKind::Enum ||
158+
Kind == FieldDescriptorKind::MultiPayloadEnum);
159+
}
160+
161+
bool isClass() const {
162+
return (Kind == FieldDescriptorKind::Class ||
163+
Kind == FieldDescriptorKind::ObjCClass);
164+
}
165+
166+
bool isProtocol() const {
167+
return (Kind == FieldDescriptorKind::Protocol ||
168+
Kind == FieldDescriptorKind::ClassProtocol ||
169+
Kind == FieldDescriptorKind::ObjCProtocol);
170+
}
171+
148172
const_iterator begin() const {
149173
auto Begin = getFieldRecordBuffer();
150174
auto End = Begin + NumFields;

include/swift/Reflection/TypeLowering.h

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,17 @@ class BuiltinTypeDescriptor;
3636

3737
// Defined in TypeLowering.cpp, not public -- they're friends below
3838
class LowerType;
39+
class EnumTypeInfoBuilder;
3940
class RecordTypeInfoBuilder;
4041
class ExistentialTypeInfoBuilder;
4142

4243
enum class RecordKind : unsigned {
44+
Invalid,
45+
46+
// A Swift tuple type.
4347
Tuple,
48+
49+
// A Swift struct type.
4450
Struct,
4551

4652
// An enum with no payload cases. The record will have no fields, but
@@ -51,6 +57,10 @@ enum class RecordKind : unsigned {
5157
// field, being the enum payload.
5258
SinglePayloadEnum,
5359

60+
// An enum with multiple payload cases. The record consists of a multiple
61+
// fields, one for each enum payload.
62+
MultiPayloadEnum,
63+
5464
// A Swift-native function is always a function pointer followed by a
5565
// retainable, nullable context pointer.
5666
ThickFunction,
@@ -237,6 +247,7 @@ class TypeConverter {
237247

238248
private:
239249
friend class swift::reflection::LowerType;
250+
friend class swift::reflection::EnumTypeInfoBuilder;
240251
friend class swift::reflection::RecordTypeInfoBuilder;
241252
friend class swift::reflection::ExistentialTypeInfoBuilder;
242253

@@ -272,15 +283,15 @@ class TypeConverter {
272283
/// tuples, structs, thick functions, etc.
273284
class RecordTypeInfoBuilder {
274285
TypeConverter &TC;
275-
unsigned Size, Alignment, Stride, NumExtraInhabitants;
286+
unsigned Size, Alignment, NumExtraInhabitants;
276287
RecordKind Kind;
277288
std::vector<FieldInfo> Fields;
278289
bool Empty;
279290
bool Invalid;
280291

281292
public:
282293
RecordTypeInfoBuilder(TypeConverter &TC, RecordKind Kind)
283-
: TC(TC), Size(0), Alignment(1), Stride(0), NumExtraInhabitants(0),
294+
: TC(TC), Size(0), Alignment(1), NumExtraInhabitants(0),
284295
Kind(Kind), Empty(true), Invalid(false) {}
285296

286297
bool isInvalid() const {
@@ -289,7 +300,10 @@ class RecordTypeInfoBuilder {
289300

290301
unsigned addField(unsigned fieldSize, unsigned fieldAlignment,
291302
unsigned numExtraInhabitants);
303+
304+
// Add a field of a record type, such as a struct.
292305
void addField(const std::string &Name, const TypeRef *TR);
306+
293307
const RecordTypeInfo *build();
294308

295309
unsigned getNumFields() const {
@@ -299,13 +313,6 @@ class RecordTypeInfoBuilder {
299313
unsigned getFieldOffset(unsigned Index) const {
300314
return Fields[Index].Offset;
301315
}
302-
303-
void setNumExtraInhabitants(unsigned numExtraInhabitants) {
304-
// We can only take away extra inhabitants while performing
305-
// record layout, never add new ones.
306-
assert(numExtraInhabitants <= NumExtraInhabitants);
307-
NumExtraInhabitants = numExtraInhabitants;
308-
}
309316
};
310317

311318
}

include/swift/SwiftRemoteMirror/SwiftRemoteMirrorTypes.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ typedef enum swift_layout_kind {
7272
// field, being the enum payload.
7373
SWIFT_SINGLE_PAYLOAD_ENUM,
7474

75+
// An enum with multiple payload cases. The record consists of a multiple
76+
// fields, one for each enum payload.
77+
SWIFT_MULTI_PAYLOAD_ENUM,
78+
7579
SWIFT_THICK_FUNCTION,
7680

7781
SWIFT_OPAQUE_EXISTENTIAL,

lib/IRGen/GenEnum.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -173,10 +173,10 @@ class EnumImplStrategy {
173173
/// storage type, calculates its size and alignment, and produces the
174174
/// TypeInfo for the enum.
175175
virtual TypeInfo *completeEnumTypeLayout(TypeConverter &TC,
176-
SILType Type,
177-
EnumDecl *theEnum,
178-
llvm::StructType *enumTy) = 0;
179-
176+
SILType Type,
177+
EnumDecl *theEnum,
178+
llvm::StructType *enumTy) = 0;
179+
180180
const TypeInfo &getTypeInfo() const {
181181
assert(TI);
182182
return *TI;

lib/IRGen/GenReflection.cpp

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ class ReflectionMetadataBuilder : public ConstantBuilder<> {
197197
else if (auto PD = dyn_cast<ProtocolDecl>(Nominal))
198198
IGM.ImportedProtocols.insert(PD);
199199
else
200-
IGM.BuiltinTypes.insert(CanType(t));
200+
IGM.OpaqueTypes.insert(Nominal);
201201
}
202202
});
203203
}
@@ -352,7 +352,18 @@ class FieldTypeMetadataBuilder : public ReflectionMetadataBuilder {
352352
IGM, enumDecl->getDeclaredTypeInContext()
353353
->getCanonicalType());
354354

355-
addConstantInt16(uint16_t(FieldDescriptorKind::Enum));
355+
auto kind = FieldDescriptorKind::Enum;
356+
357+
// If this is a fixed-size multi-payload enum, we have to emit a descriptor
358+
// with the size and alignment of the type, because the reflection library
359+
// cannot derive this information at runtime.
360+
if (strategy.getElementsWithPayload().size() > 1 &&
361+
!strategy.needsPayloadSizeInMetadata()) {
362+
kind = FieldDescriptorKind::MultiPayloadEnum;
363+
IGM.OpaqueTypes.insert(enumDecl);
364+
}
365+
366+
addConstantInt16(uint16_t(kind));
356367
addConstantInt16(fieldRecordSize);
357368
addConstantInt32(strategy.getElementsWithPayload().size() +
358369
strategy.getElementsWithNoPayload().size());
@@ -448,25 +459,40 @@ class FieldTypeMetadataBuilder : public ReflectionMetadataBuilder {
448459
}
449460
};
450461

451-
class BuiltinTypeMetadataBuilder : public ReflectionMetadataBuilder {
452-
void addBuiltinType(CanType builtinType) {
453-
addTypeRef(builtinType->getASTContext().TheBuiltinModule, builtinType);
462+
class FixedTypeMetadataBuilder : public ReflectionMetadataBuilder {
463+
void addFixedType(Module *module, CanType type,
464+
const FixedTypeInfo &ti) {
465+
addTypeRef(module, type);
454466

455-
auto &ti = cast<FixedTypeInfo>(IGM.getTypeInfoForUnlowered(builtinType));
456467
addConstantInt32(ti.getFixedSize().getValue());
457468
addConstantInt32(ti.getFixedAlignment().getValue());
458469
addConstantInt32(ti.getFixedStride().getValue());
459470
addConstantInt32(ti.getFixedExtraInhabitantCount(IGM));
460471
}
461472

473+
void addBuiltinType(CanType builtinType) {
474+
auto &ti = cast<FixedTypeInfo>(IGM.getTypeInfoForUnlowered(builtinType));
475+
addFixedType(builtinType->getASTContext().TheBuiltinModule, builtinType, ti);
476+
}
477+
478+
void addOpaqueType(const NominalTypeDecl *nominalDecl) {
479+
auto &ti = cast<FixedTypeInfo>(IGM.getTypeInfoForUnlowered(
480+
nominalDecl->getDeclaredTypeInContext()->getCanonicalType()));
481+
482+
addFixedType(nominalDecl->getParentModule(),
483+
nominalDecl->getDeclaredType()->getCanonicalType(), ti);
484+
}
485+
462486
void layout() {
463-
for (auto builtinType : IGM.BuiltinTypes) {
487+
for (auto builtinType : IGM.BuiltinTypes)
464488
addBuiltinType(builtinType);
465-
}
489+
490+
for (auto nominalDecl : IGM.OpaqueTypes)
491+
addOpaqueType(nominalDecl);
466492
}
467493

468494
public:
469-
BuiltinTypeMetadataBuilder(IRGenModule &IGM)
495+
FixedTypeMetadataBuilder(IRGenModule &IGM)
470496
: ReflectionMetadataBuilder(IGM) {}
471497

472498
llvm::GlobalVariable *emit() {
@@ -889,7 +915,7 @@ void IRGenModule::emitBuiltinReflectionMetadata() {
889915
for (auto PD : ImportedProtocols)
890916
emitFieldMetadataRecord(PD);
891917

892-
BuiltinTypeMetadataBuilder builder(*this);
918+
FixedTypeMetadataBuilder builder(*this);
893919
auto var = builder.emit();
894920
if (var)
895921
addUsedGlobal(var);

lib/IRGen/IRGenModule.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -719,12 +719,17 @@ class IRGenModule {
719719
/// Builtin types referenced by types in this module when emitting
720720
/// reflection metadata.
721721
llvm::SetVector<CanType> BuiltinTypes;
722+
/// Opaque but fixed-size types for which we also emit builtin type
723+
/// descriptors, allowing the reflection library to layout these types
724+
/// without knowledge of their contents. This includes imported structs
725+
/// and fixed-size multi-payload enums.
726+
llvm::SetVector<const NominalTypeDecl *> OpaqueTypes;
722727
/// Imported classes referenced by types in this module when emitting
723728
/// reflection metadata.
724-
llvm::SetVector<ClassDecl *> ImportedClasses;
729+
llvm::SetVector<const ClassDecl *> ImportedClasses;
725730
/// Imported protocols referenced by types in this module when emitting
726731
/// reflection metadata.
727-
llvm::SetVector<ProtocolDecl *> ImportedProtocols;
732+
llvm::SetVector<const ProtocolDecl *> ImportedProtocols;
728733

729734
llvm::Constant *getAddrOfStringForTypeRef(StringRef Str);
730735
llvm::Constant *getAddrOfFieldName(StringRef Name);

0 commit comments

Comments
 (0)