Skip to content

Commit 48c9289

Browse files
slavapestovspestov@apple.com
authored and
spestov@apple.com
committed
Reflection: Single-payload enum layout
Attempt to lay out single-payload enums, using knowledge of extra inhabitants where possible. - The extra inhabitants of an aggregate are the extra inhabitants of the first field. If the first field is empty, there are no extra inhabitants, and subsequent fields do not affect anything. - Function pointers and metatypes have different extra inhabitants than Builtin.RawPointer, so have IRGen emit distinct builtin type descriptors for those. - Opaque existentials do not have extra inhabitants. - Weak references do not have extra inhabitants. Also, fix IRGen to emit more accurate enum reflection metadata in these two cases: - We now record whether enum cases are indirect or not. An indirect case is the same as a payload case with Builtin.NativeObject. - We now record whether a case is empty or not using the same logic as the rest of IRGen. Previously, we would incorrectly emit a payload type for a case with a payload that is an empty struct, for example. At this point we don't have a way to get the currently inhabited enum case from a value. However, this is still an improvement because we can still reflect other fields of aggregates containing enums, instead of just giving up. Finally make some methods on TypeCoverter private, and use 'friend' to allow them to be accessed from other internal classes, making the public API simpler.
1 parent d7d2ed5 commit 48c9289

File tree

15 files changed

+968
-404
lines changed

15 files changed

+968
-404
lines changed

include/swift/Reflection/Records.h

+11-9
Original file line numberDiff line numberDiff line change
@@ -27,19 +27,21 @@ namespace reflection {
2727
class FieldRecordFlags {
2828
using int_type = uint32_t;
2929
enum : int_type {
30-
IsObjC = 0x00000001,
30+
// Is this an indirect enum case?
31+
IsIndirectCase = 0x1
3132
};
3233
int_type Data;
34+
3335
public:
34-
bool isObjC() const {
35-
return Data & IsObjC;
36+
bool isIndirectCase() const {
37+
return (Data & IsIndirectCase) == IsIndirectCase;
3638
}
3739

38-
void setIsObjC(bool ObjC) {
39-
if (ObjC)
40-
Data |= IsObjC;
40+
void setIsIndirectCase(bool IndirectCase=true) {
41+
if (IndirectCase)
42+
Data |= IsIndirectCase;
4143
else
42-
Data &= ~IsObjC;
44+
Data &= ~IsIndirectCase;
4345
}
4446

4547
int_type getRawValue() const {
@@ -69,8 +71,8 @@ class FieldRecord {
6971
return "";
7072
}
7173

72-
bool isObjC() const {
73-
return Flags.isObjC();
74+
bool isIndirectCase() const {
75+
return Flags.isIndirectCase();
7476
}
7577
};
7678

include/swift/Reflection/ReflectionContext.h

+6-3
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,8 @@ class ReflectionContext
190190
// Class existentials have trivial layout.
191191
// It is itself the pointer to the instance followed by the witness tables.
192192
case RecordKind::ClassExistential:
193-
*OutInstanceTR = getBuilder().getTypeConverter().getUnknownObjectTypeRef();
193+
// This is just Builtin.UnknownObject
194+
*OutInstanceTR = ExistentialRecordTI->getFields()[0].TR;
194195
*OutInstanceAddress = ExistentialAddress;
195196
return true;
196197

@@ -342,11 +343,13 @@ class ReflectionContext
342343
return nullptr;
343344

344345
// Initialize the builder.
345-
Builder.addField(OffsetToFirstCapture.second, sizeof(StoredPointer));
346+
Builder.addField(OffsetToFirstCapture.second, sizeof(StoredPointer),
347+
/*numExtraInhabitants=*/0);
346348

347349
// Skip the closure's necessary bindings struct, if it's present.
348350
auto SizeOfNecessaryBindings = Info.NumBindings * sizeof(StoredPointer);
349-
Builder.addField(SizeOfNecessaryBindings, sizeof(StoredPointer));
351+
Builder.addField(SizeOfNecessaryBindings, sizeof(StoredPointer),
352+
/*numExtraInhabitants=*/0);
350353

351354
// FIXME: should be unordered_set but I'm too lazy to write a hash
352355
// functor

include/swift/Reflection/TypeLowering.h

+45-3
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,23 @@ class TypeRef;
3434
class TypeRefBuilder;
3535
class BuiltinTypeDescriptor;
3636

37+
// Defined in TypeLowering.cpp, not public -- they're friends below
38+
class LowerType;
39+
class RecordTypeInfoBuilder;
40+
class ExistentialTypeInfoBuilder;
41+
3742
enum class RecordKind : unsigned {
3843
Tuple,
3944
Struct,
4045

46+
// An enum with no payload cases. The record will have no fields, but
47+
// will have the correct size.
48+
NoPayloadEnum,
49+
50+
// An enum with a single payload case. The record consists of a single
51+
// field, being the enum payload.
52+
SinglePayloadEnum,
53+
4154
// A Swift-native function is always a function pointer followed by a
4255
// retainable, nullable context pointer.
4356
ThickFunction,
@@ -185,10 +198,16 @@ class TypeConverter {
185198
llvm::DenseMap<const TypeRef *, const TypeInfo *> Cache;
186199
llvm::DenseMap<std::pair<unsigned, unsigned>,
187200
const ReferenceTypeInfo *> ReferenceCache;
201+
188202
const TypeRef *RawPointerTR = nullptr;
189203
const TypeRef *NativeObjectTR = nullptr;
190204
const TypeRef *UnknownObjectTR = nullptr;
205+
const TypeRef *ThinFunctionTR = nullptr;
206+
const TypeRef *AnyMetatypeTR = nullptr;
207+
208+
const TypeInfo *ThinFunctionTI = nullptr;
191209
const TypeInfo *ThickFunctionTI = nullptr;
210+
const TypeInfo *AnyMetatypeTI = nullptr;
192211
const TypeInfo *EmptyTI = nullptr;
193212

194213
public:
@@ -216,15 +235,29 @@ class TypeConverter {
216235
unsigned start,
217236
unsigned align);
218237

219-
/* Not really public */
238+
private:
239+
friend class swift::reflection::LowerType;
240+
friend class swift::reflection::RecordTypeInfoBuilder;
241+
friend class swift::reflection::ExistentialTypeInfoBuilder;
242+
220243
const ReferenceTypeInfo *
221244
getReferenceTypeInfo(ReferenceKind Kind,
222245
ReferenceCounting Refcounting);
223246

247+
/// TypeRefs for special types for which we need to know the layout
248+
/// intrinsically in order to layout anything else.
249+
///
250+
/// IRGen emits BuiltinTypeDescriptors for these when compiling the
251+
/// standard library.
224252
const TypeRef *getRawPointerTypeRef();
225253
const TypeRef *getNativeObjectTypeRef();
226254
const TypeRef *getUnknownObjectTypeRef();
255+
const TypeRef *getThinFunctionTypeRef();
256+
const TypeRef *getAnyMetatypeTypeRef();
257+
258+
const TypeInfo *getThinFunctionTypeInfo();
227259
const TypeInfo *getThickFunctionTypeInfo();
260+
const TypeInfo *getAnyMetatypeTypeInfo();
228261
const TypeInfo *getEmptyTypeInfo();
229262

230263
template <typename TypeInfoTy, typename... Args>
@@ -242,18 +275,20 @@ class RecordTypeInfoBuilder {
242275
unsigned Size, Alignment, Stride, NumExtraInhabitants;
243276
RecordKind Kind;
244277
std::vector<FieldInfo> Fields;
278+
bool Empty;
245279
bool Invalid;
246280

247281
public:
248282
RecordTypeInfoBuilder(TypeConverter &TC, RecordKind Kind)
249283
: TC(TC), Size(0), Alignment(1), Stride(0), NumExtraInhabitants(0),
250-
Kind(Kind), Invalid(false) {}
284+
Kind(Kind), Empty(true), Invalid(false) {}
251285

252286
bool isInvalid() const {
253287
return Invalid;
254288
}
255289

256-
unsigned addField(unsigned fieldSize, unsigned fieldAlignment);
290+
unsigned addField(unsigned fieldSize, unsigned fieldAlignment,
291+
unsigned numExtraInhabitants);
257292
void addField(const std::string &Name, const TypeRef *TR);
258293
const RecordTypeInfo *build();
259294

@@ -264,6 +299,13 @@ class RecordTypeInfoBuilder {
264299
unsigned getFieldOffset(unsigned Index) const {
265300
return Fields[Index].Offset;
266301
}
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+
}
267309
};
268310

269311
}

include/swift/Reflection/TypeRefBuilder.h

+23-1
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,28 @@ struct ClosureContextInfo {
9292
void dump(std::ostream &OS) const;
9393
};
9494

95+
struct FieldTypeInfo {
96+
std::string Name;
97+
const TypeRef *TR;
98+
bool Indirect;
99+
100+
FieldTypeInfo() : Name(""), TR(nullptr), Indirect(false) {}
101+
FieldTypeInfo(const std::string &Name, const TypeRef *TR, bool Indirect)
102+
: Name(Name), TR(TR), Indirect(Indirect) {}
103+
104+
static FieldTypeInfo forEmptyCase(std::string Name) {
105+
return FieldTypeInfo(Name, nullptr, false);
106+
}
107+
108+
static FieldTypeInfo forIndirectCase(std::string Name, const TypeRef *TR) {
109+
return FieldTypeInfo(Name, TR, true);
110+
}
111+
112+
static FieldTypeInfo forField(std::string Name, const TypeRef *TR) {
113+
return FieldTypeInfo(Name, TR, false);
114+
}
115+
};
116+
95117
/// An implementation of MetadataReader's BuilderType concept for
96118
/// building TypeRefs, and parsing field metadata from any images
97119
/// it has been made aware of.
@@ -286,7 +308,7 @@ class TypeRefBuilder {
286308
const FieldDescriptor *getFieldTypeInfo(const TypeRef *TR);
287309

288310
/// Get the parsed and substituted field types for a nominal type.
289-
std::vector<std::pair<std::string, const TypeRef *>>
311+
std::vector<FieldTypeInfo>
290312
getFieldTypeRefs(const TypeRef *TR, const FieldDescriptor *FD);
291313

292314
/// Get the primitive type lowering for a builtin type.

include/swift/SwiftRemoteMirror/SwiftRemoteMirrorTypes.h

+10
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,17 @@ typedef enum swift_layout_kind {
6363
// Value types consisting of zero or more fields.
6464
SWIFT_TUPLE,
6565
SWIFT_STRUCT,
66+
67+
// An enum with no payload cases. The record will have no fields, but
68+
// will have the correct size.
69+
SWIFT_NO_PAYLOAD_ENUM,
70+
71+
// An enum with a single payload case. The record consists of a single
72+
// field, being the enum payload.
73+
SWIFT_SINGLE_PAYLOAD_ENUM,
74+
6675
SWIFT_THICK_FUNCTION,
76+
6777
SWIFT_OPAQUE_EXISTENTIAL,
6878
SWIFT_CLASS_EXISTENTIAL,
6979
SWIFT_ERROR_EXISTENTIAL,

0 commit comments

Comments
 (0)