Skip to content

[Runtime] Improve representation of protocol conformance records #13685

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 15 additions & 12 deletions docs/ABI/TypeMetadata.rst
Original file line number Diff line number Diff line change
Expand Up @@ -417,21 +417,24 @@ section, which is scanned by the Swift runtime when needed (e.g., in response to
a `swift_conformsToProtocol()` query). Each protocol conformance record
contains:

- The `protocol descriptor`_ describing the protocol of the conformance.
- A reference to the metadata for the **conforming type**, whose form is
determined by the **protocol conformance flags** described below.
- The `protocol descriptor`_ describing the protocol of the conformance,
represented as an (possibly indirect) 32-bit offset relative to the field.
The low bit indicates whether it is an indirect offset; the second lowest
bit is reserved for future use.
- A reference to the **conforming type**, represented as a 32-bit offset
relative to the field. The lower two bits indicate how the conforming
type is represented:
0. A direct reference to a nominal type descriptor.
1. An indirect reference to a nominal type descriptor.
2. A reference to nonunique, foreign type metadata.
3. A reference to a pointer to an Objective-C class object.
- The **witness table field** that provides access to the witness table
describing the conformance itself; the form of this field is determined by the
**protocol conformance flags** described below.
- The **protocol conformance flags** is a 32-bit field comprised of:

* **Bits 0-3** contain the type metadata record kind, which indicates how
the **conforming type** field is encoded.
* **Bits 4-5** contain the kind of witness table. The value can be one of:

describing the conformance itself, represented as a direct 32-bit relative
offset. The lower two bits indicate how the witness table is represented:
0. The **witness table field** is a reference to a witness table.
1. The **witness table field** is a reference to a **witness table
accessor** function for an unconditional conformance.
2. The **witness table field** is a reference to a **witness table
accessor** function for a conditional conformance.

3. Reserved for future use.
- A 32-bit value reserved for future use.
63 changes: 18 additions & 45 deletions include/swift/ABI/MetadataValues.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,40 +173,27 @@ enum : unsigned {

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

/// The conformance is for a nongeneric native struct or enum type.
/// getDirectType() points to the canonical metadata for the type.
UniqueDirectType,
/// The conformance is for a nongeneric foreign struct or enum type.
/// The conformance is for a nominal type referenced indirectly;
/// getNominalTypeDescriptor() points to the nominal type descriptor.
IndirectNominalTypeDescriptor = 0x01,

/// The conformance is for a foreign type described by its type metadata.
/// getDirectType() points to a nonunique metadata record for the type, which
/// needs to be uniqued by the runtime.
NonuniqueDirectType,

/// The conformance is for a nongeneric class type.
/// getIndirectClass() points to a variable that contains the pointer to the
/// class object, which may be ObjC and thus require a runtime call to get
/// metadata.
///
/// On platforms without ObjC interop, this indirection isn't necessary,
/// and classes could be emitted as UniqueDirectType.
UniqueIndirectClass,
NonuniqueDirectType = 0x02,

/// The conformance is for a generic or resilient type.
/// getNominalTypeDescriptor() points to the nominal type descriptor shared
/// by all metadata instantiations of this type.
UniqueNominalTypeDescriptor,

/// The conformance is for a nongeneric class type.
/// getDirectType() points to the unique class object.
/// The conformance is for an Objective-C class that has no nominal type
/// descriptor.
/// getIndirectObjCClass() points to a variable that contains the pointer to
/// the class object, which then requires a runtime call to get metadata.
///
/// FIXME: This shouldn't exist. On ObjC interop platforms, class references
/// must be indirected (using UniqueIndirectClass). On non-ObjC interop
/// platforms, the class object always is the type metadata.
UniqueDirectClass = 0xF,
/// On platforms without Objective-C interoperability, this case is
/// unused.
IndirectObjCClass = 0x03,
};

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

// Type metadata record discriminant
Expand Down Expand Up @@ -251,13 +240,6 @@ struct TypeMetadataRecordFlags {

// Protocol conformance discriminant
struct ProtocolConformanceFlags : public TypeMetadataRecordFlags {
private:
enum : int_type {
ConformanceKindMask = 0x00000030U,
ConformanceKindShift = 4,
};

public:
constexpr ProtocolConformanceFlags() : TypeMetadataRecordFlags(0) {}
constexpr ProtocolConformanceFlags(int_type Data) : TypeMetadataRecordFlags(Data) {}

Expand All @@ -266,15 +248,6 @@ struct ProtocolConformanceFlags : public TypeMetadataRecordFlags {
return ProtocolConformanceFlags(
(Data & ~TypeKindMask) | (int_type(ptk) << TypeKindShift));
}
constexpr ProtocolConformanceReferenceKind getConformanceKind() const {
return ProtocolConformanceReferenceKind((Data & ConformanceKindMask)
>> ConformanceKindShift);
}
constexpr ProtocolConformanceFlags withConformanceKind(
ProtocolConformanceReferenceKind pck) const {
return ProtocolConformanceFlags(
(Data & ~ConformanceKindMask) | (int_type(pck) << ConformanceKindShift));
}
};

/// Flag that indicates whether an existential type is class-constrained or not.
Expand Down
73 changes: 70 additions & 3 deletions include/swift/Basic/RelativePointer.h
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,76 @@ class RelativeIndirectablePointer {
}
};

/// A relative reference to an aligned object stored in memory. The reference
/// may be direct or indirect, and uses the low bit of the (assumed at least
/// 2-byte-aligned) pointer to differentiate. The remaining low bits store
/// an additional tiny integer value.
template<typename ValueTy, typename IntTy, bool Nullable = false,
typename Offset = int32_t>
class RelativeIndirectablePointerIntPair {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can go away now, right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Nevermind, I see it's still used to reserve the unused bit in the protocol reference.)

private:
static_assert(std::is_integral<Offset>::value &&
std::is_signed<Offset>::value,
"offset type should be signed integer");

/// The relative offset of the pointer's memory from the `this` pointer.
/// If the low bit is clear, this is a direct reference; otherwise, it is
/// an indirect reference.
Offset RelativeOffsetPlusIndirectAndInt;

/// RelativePointers should appear in statically-generated metadata. They
/// shouldn't be constructed or copied.
RelativeIndirectablePointerIntPair() = delete;
RelativeIndirectablePointerIntPair(
RelativeIndirectablePointerIntPair &&) = delete;
RelativeIndirectablePointerIntPair(
const RelativeIndirectablePointerIntPair &) = delete;
RelativeIndirectablePointerIntPair& operator=(
RelativeIndirectablePointerIntPair &&) = delete;
RelativeIndirectablePointerIntPair &operator=(
const RelativeIndirectablePointerIntPair &) = delete;

// Retrieve the mask for the stored integer value.
static Offset getIntMask() {
return (alignof(Offset) - 1) & ~(Offset)0x01;
}

public:
const ValueTy *getPointer() const & {
static_assert(alignof(ValueTy) >= 2 && alignof(Offset) >= 2,
"alignment of value and offset must be at least 2 to "
"make room for indirectable flag");

Offset offset = (RelativeOffsetPlusIndirectAndInt & ~getIntMask());

// Check for null.
if (Nullable && offset == 0)
return nullptr;

Offset offsetPlusIndirect = offset;
uintptr_t address = detail::applyRelativeOffset(this,
offsetPlusIndirect & ~1);

// If the low bit is set, then this is an indirect address. Otherwise,
// it's direct.
if (offsetPlusIndirect & 1) {
return *reinterpret_cast<const ValueTy * const *>(address);
} else {
return reinterpret_cast<const ValueTy *>(address);
}
}

/// A zero relative offset encodes a null reference.
bool isNull() const & {
Offset offset = (RelativeOffsetPlusIndirectAndInt & ~getIntMask());
return offset == 0;
}

IntTy getInt() const & {
return IntTy((RelativeOffsetPlusIndirectAndInt & getIntMask()) >> 1);
}
};

/// A relative reference to a function, intended to reference private metadata
/// functions for the current executable or dynamic library image from
/// position-independent constant data.
Expand Down Expand Up @@ -398,9 +468,6 @@ class RelativeDirectPointerIntPair {
= delete;

static Offset getMask() {
static_assert(alignof(PointeeTy) >= alignof(Offset),
"pointee alignment must be at least as strict as offset type");

return alignof(Offset) - 1;
}

Expand Down
Loading