Skip to content

Commit aee0c68

Browse files
committed
[Runtime] Pack TypeMetadataRecordKind into spare bits of protocol conformances
Use the spare bits within the type reference field to describe the kinds of type metadata records, so that we no longer need to rely on a separate "flags" field.
1 parent d645922 commit aee0c68

File tree

7 files changed

+164
-59
lines changed

7 files changed

+164
-59
lines changed

include/swift/ABI/MetadataValues.h

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,14 @@ enum : unsigned {
173173

174174
/// Kinds of type metadata/protocol conformance records.
175175
enum class TypeMetadataRecordKind : unsigned {
176+
/// The conformance is for a nominal type referenced directly;
177+
/// getNominalTypeDescriptor() points to the nominal type descriptor.
178+
DirectNominalTypeDescriptor = 0x00,
179+
180+
/// The conformance is for a nominal type referenced directly;
181+
/// getNominalTypeDescriptor() points to the nominal type descriptor.
182+
IndirectNominalTypeDescriptor = 0x01,
183+
176184
/// The conformance is for a nongeneric foreign struct or enum type.
177185
/// getDirectType() points to a nonunique metadata record for the type, which
178186
/// needs to be uniqued by the runtime.
@@ -186,11 +194,6 @@ enum class TypeMetadataRecordKind : unsigned {
186194
/// On platforms without ObjC interop, this indirection isn't necessary,
187195
/// and classes are emitted as UniqueDirectType.
188196
UniqueIndirectClass = 0x03,
189-
190-
/// The conformance is for a generic or resilient type.
191-
/// getNominalTypeDescriptor() points to the nominal type descriptor shared
192-
/// by all metadata instantiations of this type.
193-
UniqueNominalTypeDescriptor = 0x04,
194197
};
195198

196199
/// Kinds of reference to protocol conformance.

include/swift/Basic/RelativePointer.h

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,76 @@ class RelativeIndirectablePointer {
260260
}
261261
};
262262

263+
/// A relative reference to an aligned object stored in memory. The reference
264+
/// may be direct or indirect, and uses the low bit of the (assumed at least
265+
/// 2-byte-aligned) pointer to differentiate. The remaining low bits store
266+
/// an additional tiny integer value.
267+
template<typename ValueTy, typename IntTy, bool Nullable = false,
268+
typename Offset = int32_t>
269+
class RelativeIndirectablePointerIntPair {
270+
private:
271+
static_assert(std::is_integral<Offset>::value &&
272+
std::is_signed<Offset>::value,
273+
"offset type should be signed integer");
274+
275+
/// The relative offset of the pointer's memory from the `this` pointer.
276+
/// If the low bit is clear, this is a direct reference; otherwise, it is
277+
/// an indirect reference.
278+
Offset RelativeOffsetPlusIndirectAndInt;
279+
280+
/// RelativePointers should appear in statically-generated metadata. They
281+
/// shouldn't be constructed or copied.
282+
RelativeIndirectablePointerIntPair() = delete;
283+
RelativeIndirectablePointerIntPair(
284+
RelativeIndirectablePointerIntPair &&) = delete;
285+
RelativeIndirectablePointerIntPair(
286+
const RelativeIndirectablePointerIntPair &) = delete;
287+
RelativeIndirectablePointerIntPair& operator=(
288+
RelativeIndirectablePointerIntPair &&) = delete;
289+
RelativeIndirectablePointerIntPair &operator=(
290+
const RelativeIndirectablePointerIntPair &) = delete;
291+
292+
// Retrieve the mask for the stored integer value.
293+
static Offset getIntMask() {
294+
return (alignof(Offset) - 1) & ~(Offset)0x01;
295+
}
296+
297+
public:
298+
const ValueTy *getPointer() const & {
299+
static_assert(alignof(ValueTy) >= 2 && alignof(Offset) >= 2,
300+
"alignment of value and offset must be at least 2 to "
301+
"make room for indirectable flag");
302+
303+
Offset offset = (RelativeOffsetPlusIndirectAndInt & ~getIntMask());
304+
305+
// Check for null.
306+
if (Nullable && offset == 0)
307+
return nullptr;
308+
309+
Offset offsetPlusIndirect = offset;
310+
uintptr_t address = detail::applyRelativeOffset(this,
311+
offsetPlusIndirect & ~1);
312+
313+
// If the low bit is set, then this is an indirect address. Otherwise,
314+
// it's direct.
315+
if (offsetPlusIndirect & 1) {
316+
return *reinterpret_cast<const ValueTy * const *>(address);
317+
} else {
318+
return reinterpret_cast<const ValueTy *>(address);
319+
}
320+
}
321+
322+
/// A zero relative offset encodes a null reference.
323+
bool isNull() const & {
324+
Offset offset = (RelativeOffsetPlusIndirectAndInt & ~getIntMask());
325+
return offset == 0;
326+
}
327+
328+
IntTy getInt() const & {
329+
return IntTy((RelativeOffsetPlusIndirectAndInt & getIntMask()) >> 1);
330+
}
331+
};
332+
263333
/// A relative reference to a function, intended to reference private metadata
264334
/// functions for the current executable or dynamic library image from
265335
/// position-independent constant data.

include/swift/Runtime/Metadata.h

Lines changed: 36 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2481,7 +2481,8 @@ struct TargetTypeMetadataRecord {
24812481
break;
24822482

24832483
case TypeMetadataRecordKind::UniqueIndirectClass:
2484-
case TypeMetadataRecordKind::UniqueNominalTypeDescriptor:
2484+
case TypeMetadataRecordKind::DirectNominalTypeDescriptor:
2485+
case TypeMetadataRecordKind::IndirectNominalTypeDescriptor:
24852486
assert(false && "not direct type metadata");
24862487
}
24872488

@@ -2491,11 +2492,12 @@ struct TargetTypeMetadataRecord {
24912492
const TargetNominalTypeDescriptor<Runtime> *
24922493
getNominalTypeDescriptor() const {
24932494
switch (Flags.getTypeKind()) {
2494-
case TypeMetadataRecordKind::UniqueNominalTypeDescriptor:
2495+
case TypeMetadataRecordKind::DirectNominalTypeDescriptor:
24952496
break;
24962497

24972498
case TypeMetadataRecordKind::UniqueIndirectClass:
24982499
case TypeMetadataRecordKind::NonuniqueDirectType:
2500+
case TypeMetadataRecordKind::IndirectNominalTypeDescriptor:
24992501
assert(false && "not generic metadata pattern");
25002502
}
25012503

@@ -2522,24 +2524,32 @@ struct TargetProtocolConformanceRecord {
25222524

25232525
private:
25242526
/// The protocol being conformed to.
2525-
RelativeIndirectablePointer<ProtocolDescriptor> Protocol;
2527+
///
2528+
/// The remaining low bit is reserved for future use.
2529+
RelativeIndirectablePointerIntPair<ProtocolDescriptor, /*reserved=*/bool>
2530+
Protocol;
25262531

25272532
// Some description of the type that conforms to the protocol.
25282533
union {
2529-
/// A direct reference to the metadata.
2534+
/// A direct reference to the metadata, which must be uniqued before
2535+
/// being used.
25302536
///
2531-
/// Depending on the conformance kind, this may not be usable
2532-
/// metadata without being first processed by the runtime.
2533-
RelativeIndirectablePointer<TargetMetadata<Runtime>> DirectType;
2537+
/// Only valid when the \c IndirectClassOrDirectType value is
2538+
// \c IsDirectType.
2539+
RelativeDirectPointerIntPair<TargetMetadata<Runtime>,
2540+
TypeMetadataRecordKind> DirectType;
25342541

25352542
/// An indirect reference to the metadata.
2536-
RelativeIndirectablePointer<const TargetClassMetadata<Runtime> *>
2537-
IndirectClass;
2543+
///
2544+
/// Only valid when the \c IndirectClassOrDirectType value is
2545+
// \c IsIndirectClass.
2546+
RelativeDirectPointerIntPair<const TargetClassMetadata<Runtime> *,
2547+
TypeMetadataRecordKind> IndirectClass;
25382548

25392549
/// The nominal type descriptor for a resilient or generic type which has
25402550
/// instances that conform to the protocol.
2541-
RelativeIndirectablePointer<TargetNominalTypeDescriptor<Runtime>>
2542-
TypeDescriptor;
2551+
RelativeIndirectablePointerIntPair<TargetNominalTypeDescriptor<Runtime>,
2552+
/*always clear=*/bool> TypeDescriptor;
25432553
};
25442554

25452555

@@ -2558,63 +2568,62 @@ struct TargetProtocolConformanceRecord {
25582568
};
25592569

25602570
/// Flags describing the protocol conformance.
2561-
ProtocolConformanceFlags Flags;
2571+
ProtocolConformanceFlags UnusedFlags;
25622572

25632573
public:
25642574
const ProtocolDescriptor *getProtocol() const {
2565-
return Protocol;
2575+
return Protocol.getPointer();
25662576
}
25672577

2568-
ProtocolConformanceFlags getFlags() const {
2569-
return Flags;
2570-
}
2571-
25722578
TypeMetadataRecordKind getTypeKind() const {
2573-
return Flags.getTypeKind();
2579+
return DirectType.getInt();
25742580
}
25752581

25762582
ProtocolConformanceReferenceKind getConformanceKind() const {
25772583
return WitnessTable.getInt();
25782584
}
25792585

25802586
const TargetMetadata<Runtime> *getDirectType() const {
2581-
switch (Flags.getTypeKind()) {
2587+
switch (getTypeKind()) {
25822588
case TypeMetadataRecordKind::NonuniqueDirectType:
25832589
break;
25842590

25852591
case TypeMetadataRecordKind::UniqueIndirectClass:
2586-
case TypeMetadataRecordKind::UniqueNominalTypeDescriptor:
2592+
case TypeMetadataRecordKind::DirectNominalTypeDescriptor:
2593+
case TypeMetadataRecordKind::IndirectNominalTypeDescriptor:
25872594
assert(false && "not direct type metadata");
25882595
}
25892596

2590-
return DirectType;
2597+
return DirectType.getPointer();
25912598
}
25922599

25932600
const TargetClassMetadata<Runtime> * const *getIndirectClass() const {
2594-
switch (Flags.getTypeKind()) {
2601+
switch (getTypeKind()) {
25952602
case TypeMetadataRecordKind::UniqueIndirectClass:
25962603
break;
25972604

25982605
case TypeMetadataRecordKind::NonuniqueDirectType:
2599-
case TypeMetadataRecordKind::UniqueNominalTypeDescriptor:
2606+
case TypeMetadataRecordKind::DirectNominalTypeDescriptor:
2607+
case TypeMetadataRecordKind::IndirectNominalTypeDescriptor:
26002608
assert(false && "not indirect class object");
26012609
}
26022610

2603-
return IndirectClass;
2611+
return IndirectClass.getPointer();
26042612
}
26052613

26062614
const TargetNominalTypeDescriptor<Runtime> *
26072615
getNominalTypeDescriptor() const {
2608-
switch (Flags.getTypeKind()) {
2609-
case TypeMetadataRecordKind::UniqueNominalTypeDescriptor:
2616+
switch (getTypeKind()) {
2617+
case TypeMetadataRecordKind::DirectNominalTypeDescriptor:
2618+
case TypeMetadataRecordKind::IndirectNominalTypeDescriptor:
26102619
break;
26112620

26122621
case TypeMetadataRecordKind::UniqueIndirectClass:
26132622
case TypeMetadataRecordKind::NonuniqueDirectType:
26142623
assert(false && "not generic metadata pattern");
26152624
}
26162625

2617-
return TypeDescriptor;
2626+
return TypeDescriptor.getPointer();
26182627
}
26192628

26202629
/// Get the directly-referenced static witness table.

lib/IRGen/GenDecl.cpp

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2192,6 +2192,17 @@ struct TypeEntityInfo {
21922192
ProtocolConformanceFlags flags;
21932193
LinkEntity entity;
21942194
llvm::Type *defaultTy, *defaultPtrTy;
2195+
2196+
/// Adjust the flags once we know whether the reference to this entity
2197+
/// will be indirect.
2198+
void adjustForKnownRef(ConstantReference &ref) {
2199+
if (ref.isIndirect() &&
2200+
flags.getTypeKind()
2201+
== TypeMetadataRecordKind::DirectNominalTypeDescriptor) {
2202+
flags = flags.withTypeKind(
2203+
TypeMetadataRecordKind::IndirectNominalTypeDescriptor);
2204+
}
2205+
}
21952206
};
21962207
} // end anonymous namespace
21972208

@@ -2206,7 +2217,7 @@ getTypeEntityInfo(IRGenModule &IGM, CanType conformingType) {
22062217
if (doesConformanceReferenceNominalTypeDescriptor(IGM, conformingType)) {
22072218
// Conformances for generics and concrete subclasses of generics
22082219
// are represented by referencing the nominal type descriptor.
2209-
typeKind = TypeMetadataRecordKind::UniqueNominalTypeDescriptor;
2220+
typeKind = TypeMetadataRecordKind::DirectNominalTypeDescriptor;
22102221
entity = LinkEntity::forNominalTypeDescriptor(nom);
22112222
defaultTy = IGM.NominalTypeDescriptorTy;
22122223
defaultPtrTy = IGM.NominalTypeDescriptorPtrTy;
@@ -2307,23 +2318,29 @@ llvm::Constant *IRGenModule::emitProtocolConformances() {
23072318

23082319
emitAssociatedTypeMetadataRecord(conformance);
23092320

2310-
// Relative reference to the nominal type descriptor.
2321+
// Relative reference to the protocol descriptor.
23112322
auto descriptorRef = getAddrOfLLVMVariableOrGOTEquivalent(
23122323
LinkEntity::forProtocolDescriptor(conformance->getProtocol()),
23132324
getPointerAlignment(), ProtocolDescriptorStructTy);
23142325
record.addRelativeAddress(descriptorRef);
23152326

2316-
// Relative reference to the type entity info.
2317-
auto typeEntity = getTypeEntityInfo(*this,
2318-
conformance->getType()->getCanonicalType());
2327+
// Relative reference to the type entity info, with the type reference
2328+
// kind mangled in the lower bits.
2329+
auto typeEntity =
2330+
getTypeEntityInfo(*this, conformance->getType()->getCanonicalType());
2331+
23192332
auto typeRef = getAddrOfLLVMVariableOrGOTEquivalent(
23202333
typeEntity.entity, getPointerAlignment(), typeEntity.defaultTy);
2321-
record.addRelativeAddress(typeRef);
2334+
typeEntity.adjustForKnownRef(typeRef);
2335+
record.addTaggedRelativeOffset(RelativeAddressTy,
2336+
typeRef.getValue(),
2337+
typeEntity.flags.getValue());
23222338

23232339
// Figure out what kind of witness table we have.
23242340
auto flags = typeEntity.flags;
23252341
llvm::Constant *witnessTableVar;
23262342
ProtocolConformanceReferenceKind conformanceKind;
2343+
23272344
if (!isResilient(conformance->getProtocol(),
23282345
ResilienceExpansion::Maximal) &&
23292346
conformance->getConditionalRequirements().empty()) {

stdlib/public/runtime/ProtocolConformance.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ template<> void ProtocolConformanceRecord::dump() const {
6161
class_getName(*getIndirectClass()));
6262
break;
6363

64-
case TypeMetadataRecordKind::UniqueNominalTypeDescriptor:
64+
case TypeMetadataRecordKind::DirectNominalTypeDescriptor:
65+
case TypeMetadataRecordKind::IndirectNominalTypeDescriptor:
6566
printf("unique nominal type descriptor %s", symbolName(getNominalTypeDescriptor()));
6667
break;
6768
}
@@ -107,7 +108,8 @@ const Metadata *ProtocolConformanceRecord::getCanonicalTypeMetadata() const {
107108
return getMetadataForClass(ClassMetadata);
108109
return nullptr;
109110

110-
case TypeMetadataRecordKind::UniqueNominalTypeDescriptor:
111+
case TypeMetadataRecordKind::DirectNominalTypeDescriptor:
112+
case TypeMetadataRecordKind::IndirectNominalTypeDescriptor:
111113
return nullptr;
112114
}
113115

@@ -548,8 +550,9 @@ swift::swift_conformsToProtocol(const Metadata * const type,
548550
// An accessor function might still be necessary even if the witness table
549551
// can be shared.
550552
} else if (record.getTypeKind()
551-
== TypeMetadataRecordKind::UniqueNominalTypeDescriptor) {
552-
553+
== TypeMetadataRecordKind::DirectNominalTypeDescriptor ||
554+
record.getTypeKind()
555+
== TypeMetadataRecordKind::IndirectNominalTypeDescriptor) {
553556
auto R = record.getNominalTypeDescriptor();
554557
auto P = record.getProtocol();
555558

0 commit comments

Comments
 (0)