Skip to content

Commit 9a6126b

Browse files
committed
IRGen: Move fine-grained flags from ClassLayoutBuilder to ClassLayout
Another refactoring in preparation for adding the new type of class metadata emitted when deploying to a target with a new Objective-C runtime. Progress on <rdar://problem/47649465>.
1 parent 040323e commit 9a6126b

File tree

3 files changed

+115
-122
lines changed

3 files changed

+115
-122
lines changed

lib/IRGen/ClassLayout.cpp

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,19 +25,15 @@ using namespace swift;
2525
using namespace irgen;
2626

2727
ClassLayout::ClassLayout(const StructLayoutBuilder &builder,
28-
bool isFixedSize,
29-
bool metadataRequiresInitialization,
30-
bool metadataRequiresRelocation,
28+
ClassMetadataOptions options,
3129
llvm::Type *classTy,
3230
ArrayRef<VarDecl *> allStoredProps,
3331
ArrayRef<FieldAccess> allFieldAccesses,
3432
ArrayRef<ElementLayout> allElements)
3533
: MinimumAlign(builder.getAlignment()),
3634
MinimumSize(builder.getSize()),
3735
IsFixedLayout(builder.isFixedLayout()),
38-
IsFixedSize(isFixedSize),
39-
MetadataRequiresInitialization(metadataRequiresInitialization),
40-
MetadataRequiresRelocation(metadataRequiresRelocation),
36+
Options(options),
4137
Ty(classTy),
4238
AllStoredProperties(allStoredProps),
4339
AllFieldAccesses(allFieldAccesses),

lib/IRGen/ClassLayout.h

Lines changed: 81 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,65 @@ enum class FieldAccess : uint8_t {
3939
ConstantIndirect
4040
};
4141

42+
/// A set of flags describing properties of a class's metadata layout.
43+
/// The presence or absence of these flags determines how much static
44+
/// knowledge the compiler has of the layout of this class and its
45+
/// metadata, which in turn will determine the strategy used to emit
46+
/// and initialize class metadata.
47+
enum class ClassMetadataFlags {
48+
/// Does the class or any of its superclasses have stored properties that
49+
/// where dropped due to the Swift language version availability of
50+
/// their types?
51+
ClassHasMissingMembers = (1 << 0),
52+
53+
/// Does the class or any of its fragile superclasses have stored
54+
/// properties of unknown size, which do *not* depend on generic
55+
/// parameters?
56+
///
57+
/// This is different from the class itself being resilient or
58+
/// having resilient ancestry, because we still have a fixed layout
59+
/// for the class metadata in this case.
60+
///
61+
/// In fact, for a class with resilient ancestry, this can still be
62+
/// false if all of the fields known to us are fixed size.
63+
ClassHasResilientMembers = (1 << 1),
64+
65+
/// Is this class or any of its superclasses generic?
66+
ClassHasGenericAncestry = (1 << 2),
67+
68+
/// Is this class itself generic via the Swift generic system, ie. not a
69+
/// lightweight Objective-C generic class?
70+
ClassIsGeneric = (1 << 3),
71+
72+
/// Does the class layout depend on the size or alignment of its
73+
/// generic parameters?
74+
///
75+
/// This can be the case if the class has generic resilient ancestry
76+
/// that depends on the class's generic parameters, of it it has
77+
/// fields of generic type that are not fixed size.
78+
ClassHasGenericLayout = (1 << 4),
79+
80+
/// Is this class or any of its superclasses resilient from the viewpoint
81+
/// of the current module? This means that their metadata can change size,
82+
/// hence field offsets, generic arguments and virtual methods must be
83+
/// accessed relative to a metadata base global variable.
84+
///
85+
/// Note that a @_fixed_layout class in a resilient module still has
86+
/// resilient metadata, so any subclasses will have this flag set;
87+
/// to check for resilient stored property layout, check for
88+
/// ClassHasResilientMembers.
89+
ClassHasResilientAncestry = (1 << 5),
90+
91+
/// Are any of this class's superclasses defined in Objective-C?
92+
/// This means that field offsets must be loaded from field offset globals
93+
/// or the field offset vector in the metadata, and the Objective-C runtime
94+
/// will slide offsets based on the actual superclass size, which is not
95+
/// known at compile time.
96+
ClassHasObjCAncestry = (1 << 6)
97+
};
98+
99+
using ClassMetadataOptions = OptionSet<ClassMetadataFlags>;
100+
42101
class ClassLayout {
43102
/// The statically-known minimum bound on the alignment.
44103
Alignment MinimumAlign;
@@ -50,18 +109,7 @@ class ClassLayout {
50109
/// alignment are exact.
51110
bool IsFixedLayout;
52111

53-
/// Do instances of this class have a size and layout known at compile time?
54-
///
55-
/// Note: This is a stronger condition than having a fixed layout. The latter
56-
/// is true even when the class requires sliding ivars by the Objective-C
57-
/// runtime.
58-
bool IsFixedSize;
59-
60-
/// Does the class metadata require initialization?
61-
bool MetadataRequiresInitialization;
62-
63-
/// Does the class metadata require relocation?
64-
bool MetadataRequiresRelocation;
112+
ClassMetadataOptions Options;
65113

66114
/// The LLVM type for instances of this class.
67115
llvm::Type *Ty;
@@ -79,9 +127,7 @@ class ClassLayout {
79127

80128
public:
81129
ClassLayout(const StructLayoutBuilder &builder,
82-
bool isFixedSize,
83-
bool metadataRequiresInitialization,
84-
bool metadataRequiresRelocation,
130+
ClassMetadataOptions options,
85131
llvm::Type *classTy,
86132
ArrayRef<VarDecl *> allStoredProps,
87133
ArrayRef<FieldAccess> allFieldAccesses,
@@ -96,14 +142,31 @@ class ClassLayout {
96142

97143
bool isFixedLayout() const { return IsFixedLayout; }
98144

99-
bool isFixedSize() const { return IsFixedSize; }
145+
/// Returns true if the stored property layout of instances of this class
146+
/// is known at compile time.
147+
///
148+
/// Note that ClassHasResilientAncestry or ClassHasGenericAncestry might
149+
/// still be true; the former means the class has resilient metadata, so
150+
/// it might still be @_fixed_layout; the latter means we have a generic
151+
/// superclass, but it doesn't mean the layout actually depends on any
152+
/// generic parameters.
153+
bool isFixedSize() const {
154+
return !(Options.contains(ClassMetadataFlags::ClassHasMissingMembers) ||
155+
Options.contains(ClassMetadataFlags::ClassHasResilientMembers) ||
156+
Options.contains(ClassMetadataFlags::ClassHasGenericLayout) ||
157+
Options.contains(ClassMetadataFlags::ClassHasObjCAncestry));
158+
}
100159

101160
bool doesMetadataRequireInitialization() const {
102-
return MetadataRequiresInitialization;
161+
return (Options.contains(ClassMetadataFlags::ClassHasResilientAncestry) ||
162+
Options.contains(ClassMetadataFlags::ClassHasGenericAncestry) ||
163+
Options.contains(ClassMetadataFlags::ClassHasResilientMembers) ||
164+
Options.contains(ClassMetadataFlags::ClassHasMissingMembers));
103165
}
104166

105167
bool doesMetadataRequireRelocation() const {
106-
return MetadataRequiresRelocation;
168+
return (Options.contains(ClassMetadataFlags::ClassHasResilientAncestry) ||
169+
Options.contains(ClassMetadataFlags::ClassIsGeneric));
107170
}
108171

109172
std::pair<FieldAccess, ElementLayout>

lib/IRGen/GenClass.cpp

Lines changed: 32 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -163,54 +163,7 @@ namespace {
163163
// ancestry, or has a superclass that is itself resilient.
164164
bool CompletelyFragileLayout;
165165

166-
// The below flags indicate various things about the metadata of the
167-
// class that might require dynamic initialization or resilient
168-
// access patterns:
169-
170-
// Does the class or any of its superclasses have stored properties that
171-
// where dropped due to the Swift language version availability of
172-
// their types?
173-
bool ClassHasMissingMembers = false;
174-
175-
// Does the class or any of its fragile superclasses have stored
176-
// properties of unknown size, which do *not* depend on generic
177-
// parameters?
178-
//
179-
// This is different from the class itself being resilient or
180-
// having resilient ancestry, because we still have a fixed layout
181-
// for the class metadata in this case.
182-
//
183-
// In fact, for a class with resilient ancestry, this can still be
184-
// false if all of the fields known to us are fixed size.
185-
bool ClassHasResilientMembers = false;
186-
187-
// Is this class or any of its superclasses generic?
188-
bool ClassHasGenericAncestry = false;
189-
190-
// Is this class itself generic via the Swift generic system, ie. not a
191-
// lightweight Objective-C generic class?
192-
bool ClassIsGeneric = false;
193-
194-
// Does the class layout depend on the size or alignment of its
195-
// generic parameters?
196-
//
197-
// This can be the case if the class has generic resilient ancestry
198-
// that depends on the class's generic parameters, of it it has
199-
// fields of generic type that are not fixed size.
200-
bool ClassHasGenericLayout = false;
201-
202-
// Is this class or any of its superclasses resilient from the viewpoint
203-
// of the current module? This means that their metadata can change size,
204-
// hence field offsets, generic arguments and virtual methods must be
205-
// accessed relative to a metadata base global variable.
206-
bool ClassHasResilientAncestry = false;
207-
208-
// Are any of this class's superclasses defined in Objective-C?
209-
// This means that field offsets must be loaded from field offset globals
210-
// or the field offset vector in the metadata, and the Objective-C runtime
211-
// will slide offsets based on the actual superclass size, which is not
212-
// known at compile time.
213-
bool ClassHasObjCAncestry = false;
166+
ClassMetadataOptions Options;
214167

215168
public:
216169
ClassLayoutBuilder(IRGenModule &IGM, SILType classType,
@@ -243,7 +196,7 @@ namespace {
243196
assert(theClass);
244197

245198
if (theClass->isGenericContext() && !theClass->hasClangNode())
246-
ClassIsGeneric = true;
199+
Options |= ClassMetadataFlags::ClassIsGeneric;
247200

248201
addFieldsForClass(theClass, classType, /*superclass=*/false);
249202

@@ -261,42 +214,15 @@ namespace {
261214
return Elements;
262215
}
263216

264-
/// Do instances of the class have a completely known, static layout?
265-
bool isFixedSize() const {
266-
return !(ClassHasMissingMembers ||
267-
ClassHasResilientMembers ||
268-
ClassHasGenericLayout ||
269-
ClassHasObjCAncestry);
270-
}
271-
272-
bool doesMetadataRequireInitialization() const {
273-
return (ClassHasMissingMembers ||
274-
ClassHasResilientMembers ||
275-
ClassHasResilientAncestry ||
276-
ClassHasGenericAncestry ||
277-
IGM.getOptions().LazyInitializeClassMetadata);
278-
}
279-
280-
bool doesMetadataRequireRelocation() const {
281-
return (ClassHasResilientAncestry ||
282-
ClassIsGeneric);
283-
}
284-
285217
ClassLayout getClassLayout(llvm::Type *classTy) const {
286218
assert(!TailTypes);
287219

288220
auto allStoredProps = IGM.Context.AllocateCopy(AllStoredProperties);
289221
auto allFieldAccesses = IGM.Context.AllocateCopy(AllFieldAccesses);
290222
auto allElements = IGM.Context.AllocateCopy(Elements);
291223

292-
return ClassLayout(*this,
293-
isFixedSize(),
294-
doesMetadataRequireInitialization(),
295-
doesMetadataRequireRelocation(),
296-
classTy,
297-
allStoredProps,
298-
allFieldAccesses,
299-
allElements);
224+
return ClassLayout(*this, Options, classTy,
225+
allStoredProps, allFieldAccesses, allElements);
300226
}
301227

302228
private:
@@ -320,7 +246,7 @@ namespace {
320246
void addFieldsForClass(ClassDecl *theClass, SILType classType,
321247
bool superclass) {
322248
if (theClass->hasClangNode()) {
323-
ClassHasObjCAncestry = true;
249+
Options |= ClassMetadataFlags::ClassHasObjCAncestry;
324250
return;
325251
}
326252

@@ -330,18 +256,18 @@ namespace {
330256
assert(superclassType && superclassDecl);
331257

332258
if (IGM.hasResilientMetadata(superclassDecl, ResilienceExpansion::Maximal))
333-
ClassHasResilientAncestry = true;
259+
Options |= ClassMetadataFlags::ClassHasResilientAncestry;
334260

335261
// If the superclass has resilient storage, don't walk its fields.
336262
if (IGM.isResilient(superclassDecl, ResilienceExpansion::Maximal)) {
337-
ClassHasResilientMembers = true;
263+
Options |= ClassMetadataFlags::ClassHasResilientMembers;
338264

339265
// If the superclass is generic, we have to assume that its layout
340266
// depends on its generic parameters. But this only propagates down to
341267
// subclasses whose superclass type depends on the subclass's generic
342268
// context.
343269
if (superclassType.hasArchetype())
344-
ClassHasGenericLayout = true;
270+
Options |= ClassMetadataFlags::ClassHasGenericLayout;
345271
} else {
346272
// Otherwise, we are allowed to have total knowledge of the superclass
347273
// fields, so walk them to compute the layout.
@@ -350,16 +276,16 @@ namespace {
350276
}
351277

352278
if (theClass->isGenericContext())
353-
ClassHasGenericAncestry = true;
279+
Options |= ClassMetadataFlags::ClassHasGenericAncestry;
354280

355281
if (classHasIncompleteLayout(IGM, theClass))
356-
ClassHasMissingMembers = true;
282+
Options |= ClassMetadataFlags::ClassHasMissingMembers;
357283

358284
if (IGM.hasResilientMetadata(theClass, ResilienceExpansion::Maximal))
359-
ClassHasResilientAncestry = true;
285+
Options |= ClassMetadataFlags::ClassHasResilientAncestry;
360286

361287
if (IGM.isResilient(theClass, ResilienceExpansion::Maximal)) {
362-
ClassHasResilientMembers = true;
288+
Options |= ClassMetadataFlags::ClassHasResilientMembers;
363289
return;
364290
}
365291

@@ -382,9 +308,9 @@ namespace {
382308

383309
if (!eltType->isFixedSize()) {
384310
if (type.hasArchetype())
385-
ClassHasGenericLayout = true;
311+
Options |= ClassMetadataFlags::ClassHasGenericLayout;
386312
else
387-
ClassHasResilientMembers = true;
313+
Options |= ClassMetadataFlags::ClassHasResilientMembers;
388314
}
389315

390316
auto element = ElementLayout::getIncomplete(*eltType);
@@ -445,7 +371,8 @@ namespace {
445371
// var y : AKlass = AKlass()
446372
// var t : T?
447373
// }
448-
if (ClassHasGenericLayout && ClassHasObjCAncestry) {
374+
if (Options.contains(ClassMetadataFlags::ClassHasGenericLayout) &&
375+
Options.contains(ClassMetadataFlags::ClassHasObjCAncestry)) {
449376
for (auto &access : AllFieldAccesses) {
450377
if (access == FieldAccess::NonConstantDirect)
451378
access = FieldAccess::ConstantIndirect;
@@ -455,23 +382,26 @@ namespace {
455382
}
456383

457384
FieldAccess getFieldAccess() {
458-
// If the layout so far has a fixed size, the field offset is known
459-
// statically.
460-
if (isFixedSize())
461-
return FieldAccess::ConstantDirect;
462-
463385
// If layout so far depends on generic parameters, we have to load the
464386
// offset from the field offset vector in class metadata.
465-
if (ClassHasGenericLayout)
387+
if (Options.contains(ClassMetadataFlags::ClassHasGenericLayout))
466388
return FieldAccess::ConstantIndirect;
467389

468390
// If layout so far doesn't depend on any generic parameters, but it's
469-
// nonetheless not statically known (because either a superclass
470-
// or a member type was resilient), then we can rely on the existence
391+
// nonetheless not statically known (because either the stored property
392+
// layout of a superclass is resilient, or one of our own members is a
393+
// resilient value type), then we can rely on the existence
471394
// of a global field offset variable which will be initialized by
472395
// either the Objective-C or Swift runtime, depending on the
473396
// class's heritage.
474-
return FieldAccess::NonConstantDirect;
397+
if (Options.contains(ClassMetadataFlags::ClassHasMissingMembers) ||
398+
Options.contains(ClassMetadataFlags::ClassHasResilientMembers) ||
399+
Options.contains(ClassMetadataFlags::ClassHasObjCAncestry))
400+
return FieldAccess::NonConstantDirect;
401+
402+
// If the layout so far has a fixed size, the field offset is known
403+
// statically.
404+
return FieldAccess::ConstantDirect;
475405
}
476406
};
477407
} // end anonymous namespace
@@ -2316,6 +2246,10 @@ IRGenModule::getClassMetadataStrategy(const ClassDecl *theClass) {
23162246
if (fragileLayout.doesMetadataRequireInitialization())
23172247
return ClassMetadataStrategy::Singleton;
23182248

2249+
// If the legacy type info was sufficient to produce a fixed fragile layout,
2250+
// but our resilient layout requires initialization, we can use the fixed
2251+
// layout on older Objective-C runtimes, and emit an update callback for
2252+
// newer Objective-C runtimes.
23192253
if (resilientLayout.doesMetadataRequireInitialization())
23202254
return ClassMetadataStrategy::FixedOrUpdate;
23212255

0 commit comments

Comments
 (0)