Skip to content

Commit 0632d87

Browse files
authored
Merge pull request #13685 from DougGregor/protocol-conformance-record-cleanup
[Runtime] Improve representation of protocol conformance records
2 parents e053dab + 3219047 commit 0632d87

18 files changed

+332
-334
lines changed

docs/ABI/TypeMetadata.rst

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -417,21 +417,24 @@ section, which is scanned by the Swift runtime when needed (e.g., in response to
417417
a `swift_conformsToProtocol()` query). Each protocol conformance record
418418
contains:
419419

420-
- The `protocol descriptor`_ describing the protocol of the conformance.
421-
- A reference to the metadata for the **conforming type**, whose form is
422-
determined by the **protocol conformance flags** described below.
420+
- The `protocol descriptor`_ describing the protocol of the conformance,
421+
represented as an (possibly indirect) 32-bit offset relative to the field.
422+
The low bit indicates whether it is an indirect offset; the second lowest
423+
bit is reserved for future use.
424+
- A reference to the **conforming type**, represented as a 32-bit offset
425+
relative to the field. The lower two bits indicate how the conforming
426+
type is represented:
427+
0. A direct reference to a nominal type descriptor.
428+
1. An indirect reference to a nominal type descriptor.
429+
2. A reference to nonunique, foreign type metadata.
430+
3. A reference to a pointer to an Objective-C class object.
423431
- The **witness table field** that provides access to the witness table
424-
describing the conformance itself; the form of this field is determined by the
425-
**protocol conformance flags** described below.
426-
- The **protocol conformance flags** is a 32-bit field comprised of:
427-
428-
* **Bits 0-3** contain the type metadata record kind, which indicates how
429-
the **conforming type** field is encoded.
430-
* **Bits 4-5** contain the kind of witness table. The value can be one of:
431-
432+
describing the conformance itself, represented as a direct 32-bit relative
433+
offset. The lower two bits indicate how the witness table is represented:
432434
0. The **witness table field** is a reference to a witness table.
433435
1. The **witness table field** is a reference to a **witness table
434436
accessor** function for an unconditional conformance.
435437
2. The **witness table field** is a reference to a **witness table
436438
accessor** function for a conditional conformance.
437-
439+
3. Reserved for future use.
440+
- A 32-bit value reserved for future use.

include/swift/ABI/MetadataValues.h

Lines changed: 18 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -173,40 +173,27 @@ enum : unsigned {
173173

174174
/// Kinds of type metadata/protocol conformance records.
175175
enum class TypeMetadataRecordKind : unsigned {
176-
/// The conformance is universal and might apply to any type.
177-
/// getDirectType() is nil.
178-
Universal,
176+
/// The conformance is for a nominal type referenced directly;
177+
/// getNominalTypeDescriptor() points to the nominal type descriptor.
178+
DirectNominalTypeDescriptor = 0x00,
179179

180-
/// The conformance is for a nongeneric native struct or enum type.
181-
/// getDirectType() points to the canonical metadata for the type.
182-
UniqueDirectType,
183-
184-
/// The conformance is for a nongeneric foreign struct or enum type.
180+
/// The conformance is for a nominal type referenced indirectly;
181+
/// getNominalTypeDescriptor() points to the nominal type descriptor.
182+
IndirectNominalTypeDescriptor = 0x01,
183+
184+
/// The conformance is for a foreign type described by its type metadata.
185185
/// getDirectType() points to a nonunique metadata record for the type, which
186186
/// needs to be uniqued by the runtime.
187-
NonuniqueDirectType,
188-
189-
/// The conformance is for a nongeneric class type.
190-
/// getIndirectClass() points to a variable that contains the pointer to the
191-
/// class object, which may be ObjC and thus require a runtime call to get
192-
/// metadata.
193-
///
194-
/// On platforms without ObjC interop, this indirection isn't necessary,
195-
/// and classes could be emitted as UniqueDirectType.
196-
UniqueIndirectClass,
187+
NonuniqueDirectType = 0x02,
197188

198-
/// The conformance is for a generic or resilient type.
199-
/// getNominalTypeDescriptor() points to the nominal type descriptor shared
200-
/// by all metadata instantiations of this type.
201-
UniqueNominalTypeDescriptor,
202-
203-
/// The conformance is for a nongeneric class type.
204-
/// getDirectType() points to the unique class object.
189+
/// The conformance is for an Objective-C class that has no nominal type
190+
/// descriptor.
191+
/// getIndirectObjCClass() points to a variable that contains the pointer to
192+
/// the class object, which then requires a runtime call to get metadata.
205193
///
206-
/// FIXME: This shouldn't exist. On ObjC interop platforms, class references
207-
/// must be indirected (using UniqueIndirectClass). On non-ObjC interop
208-
/// platforms, the class object always is the type metadata.
209-
UniqueDirectClass = 0xF,
194+
/// On platforms without Objective-C interoperability, this case is
195+
/// unused.
196+
IndirectObjCClass = 0x03,
210197
};
211198

212199
/// Kinds of reference to protocol conformance.
@@ -220,6 +207,8 @@ enum class ProtocolConformanceReferenceKind : unsigned {
220207
/// table whose conformance is conditional on additional requirements that
221208
/// must first be evaluated and then provided to the accessor function.
222209
ConditionalWitnessTableAccessor,
210+
/// Reserved for future use.
211+
Reserved,
223212
};
224213

225214
// Type metadata record discriminant
@@ -251,13 +240,6 @@ struct TypeMetadataRecordFlags {
251240

252241
// Protocol conformance discriminant
253242
struct ProtocolConformanceFlags : public TypeMetadataRecordFlags {
254-
private:
255-
enum : int_type {
256-
ConformanceKindMask = 0x00000030U,
257-
ConformanceKindShift = 4,
258-
};
259-
260-
public:
261243
constexpr ProtocolConformanceFlags() : TypeMetadataRecordFlags(0) {}
262244
constexpr ProtocolConformanceFlags(int_type Data) : TypeMetadataRecordFlags(Data) {}
263245

@@ -266,15 +248,6 @@ struct ProtocolConformanceFlags : public TypeMetadataRecordFlags {
266248
return ProtocolConformanceFlags(
267249
(Data & ~TypeKindMask) | (int_type(ptk) << TypeKindShift));
268250
}
269-
constexpr ProtocolConformanceReferenceKind getConformanceKind() const {
270-
return ProtocolConformanceReferenceKind((Data & ConformanceKindMask)
271-
>> ConformanceKindShift);
272-
}
273-
constexpr ProtocolConformanceFlags withConformanceKind(
274-
ProtocolConformanceReferenceKind pck) const {
275-
return ProtocolConformanceFlags(
276-
(Data & ~ConformanceKindMask) | (int_type(pck) << ConformanceKindShift));
277-
}
278251
};
279252

280253
/// Flag that indicates whether an existential type is class-constrained or not.

include/swift/Basic/RelativePointer.h

Lines changed: 70 additions & 3 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.
@@ -398,9 +468,6 @@ class RelativeDirectPointerIntPair {
398468
= delete;
399469

400470
static Offset getMask() {
401-
static_assert(alignof(PointeeTy) >= alignof(Offset),
402-
"pointee alignment must be at least as strict as offset type");
403-
404471
return alignof(Offset) - 1;
405472
}
406473

0 commit comments

Comments
 (0)