Skip to content

IRGen/Runtime: Relative-reference the nominal type descriptor and parent type from metadata. #1154

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

Closed
wants to merge 2 commits into from
Closed
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
107 changes: 101 additions & 6 deletions include/swift/Basic/RelativePointer.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,26 @@ static inline uintptr_t applyRelativeOffset(BasePtrTy *basePtr, Offset offset) {
return base + extendOffset;
}

/// Measure the relative offset between two pointers. This measures
/// (referent - base) using wrapping arithmetic. The result is truncated if
/// Offset is smaller than a pointer, with an assertion that the
/// pre-truncation result is a sign extension of the truncated result.
template<typename Offset, typename A, typename B>
static inline Offset measureRelativeOffset(A *referent, B *base) {
static_assert(std::is_integral<Offset>::value &&
std::is_signed<Offset>::value,
"offset type should be signed integer");

auto distance = (uintptr_t)referent - (uintptr_t)base;
// Truncate as unsigned, then wrap around to signed.
auto truncatedDistance =
(Offset)(typename std::make_unsigned<Offset>::type)distance;
// Assert that the truncation didn't discard any non-sign-extended bits.
assert((intptr_t)truncatedDistance == (intptr_t)distance
&& "pointers are too far apart to fit in offset type");
return truncatedDistance;
}

} // namespace detail

/// A relative reference to an object stored in memory. The reference may be
Expand Down Expand Up @@ -70,7 +90,34 @@ class RelativeIndirectablePointer {
= delete;

public:
/// Allow construction and reassignment from an absolute pointer.
/// These always produce a direct relative offset.
RelativeIndirectablePointer(ValueTy *absolute)
: RelativeOffsetPlusIndirect(
Nullable && absolute == nullptr
? 0
: detail::measureRelativeOffset<Offset>(absolute, this)) {
if (!Nullable)
assert(absolute != nullptr &&
"constructing non-nullable relative pointer from null");
}

RelativeIndirectablePointer &operator=(ValueTy *absolute) & {
if (!Nullable)
assert(absolute != nullptr &&
"constructing non-nullable relative pointer from null");

RelativeOffsetPlusIndirect = Nullable && absolute == nullptr
? 0
: detail::measureRelativeOffset<Offset>(absolute, this);
return *this;
}

const ValueTy *get() 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");

// Check for null.
if (Nullable && RelativeOffsetPlusIndirect == 0)
return nullptr;
Expand Down Expand Up @@ -118,17 +165,42 @@ class RelativeDirectPointerImpl {
/// RelativePointers should appear in statically-generated metadata. They
/// shouldn't be constructed or copied.
RelativeDirectPointerImpl() = delete;
RelativeDirectPointerImpl(RelativeDirectPointerImpl &&) = delete;
RelativeDirectPointerImpl(const RelativeDirectPointerImpl &) = delete;
RelativeDirectPointerImpl &operator=(RelativeDirectPointerImpl &&)
= delete;
RelativeDirectPointerImpl &operator=(const RelativeDirectPointerImpl&)
= delete;

public:
using ValueTy = T;
using PointerTy = T*;

// Allow construction and reassignment from an absolute pointer.
RelativeDirectPointerImpl(PointerTy absolute)
: RelativeOffset(Nullable && absolute == nullptr
? 0
: detail::measureRelativeOffset<Offset>(absolute, this))
{
if (!Nullable)
assert(absolute != nullptr &&
"constructing non-nullable relative pointer from null");
}
explicit constexpr RelativeDirectPointerImpl(std::nullptr_t)
: RelativeOffset (0) {
static_assert(Nullable, "can't construct non-nullable pointer from null");
}

RelativeDirectPointerImpl &operator=(PointerTy absolute) & {
if (!Nullable)
assert(absolute != nullptr &&
"constructing non-nullable relative pointer from null");
RelativeOffset = Nullable && absolute == nullptr
? 0
: detail::measureRelativeOffset<Offset>(absolute, this);
return *this;
}

// Can copy-construct by recalculating the relative offset at the new
// position.
RelativeDirectPointerImpl(const RelativeDirectPointerImpl &p) {
*this = p.get();
}

PointerTy get() const & {
// Check for null.
if (Nullable && RelativeOffset == 0)
Expand All @@ -153,6 +225,12 @@ class RelativeDirectPointer :
using super = RelativeDirectPointerImpl<T, Nullable, Offset>;
public:
using super::get;
using super::super;

RelativeDirectPointer &operator=(T *absolute) & {
super::operator=(absolute);
return *this;
}

operator typename super::PointerTy() const & {
return this->get();
Expand All @@ -178,7 +256,13 @@ class RelativeDirectPointer<RetTy (ArgTy...), Nullable, Offset> :
using super = RelativeDirectPointerImpl<RetTy (ArgTy...), Nullable, Offset>;
public:
using super::get;
using super::super;

RelativeDirectPointer &operator=(RetTy (*absolute)(ArgTy...)) & {
super::operator=(absolute);
return *this;
}

operator typename super::PointerTy() const & {
return this->get();
}
Expand Down Expand Up @@ -229,5 +313,16 @@ class RelativeDirectPointerIntPair {
}
};

// Type aliases for "far" relative pointers, which need to be able to reach
// across the full address space instead of only across a single small-code-
// model image.

template<typename T, bool Nullable = false>
using FarRelativeIndirectablePointer =
RelativeIndirectablePointer<T, Nullable, intptr_t>;

template<typename T, bool Nullable = false>
using FarRelativeDirectPointer = RelativeDirectPointer<T, Nullable, intptr_t>;

}

103 changes: 54 additions & 49 deletions include/swift/Runtime/Metadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -1246,7 +1246,7 @@ struct EnumTypeDescriptor;
/// descriptor is shared for all instantiations of the generic type.
struct NominalTypeDescriptor {
/// The mangled name of the nominal type, with no generic parameters.
RelativeDirectPointer<char> Name;
RelativeDirectPointer<const char> Name;

/// The following fields are kind-dependent.
union {
Expand All @@ -1266,7 +1266,7 @@ struct NominalTypeDescriptor {

/// The field names. A doubly-null-terminated list of strings, whose
/// length and order is consistent with that of the field offset vector.
RelativeDirectPointer<char, /*nullable*/ true> FieldNames;
RelativeDirectPointer<const char, /*nullable*/ true> FieldNames;

/// The field type vector accessor. Returns a pointer to an array of
/// type metadata references whose order is consistent with that of the
Expand All @@ -1290,7 +1290,7 @@ struct NominalTypeDescriptor {

/// The field names. A doubly-null-terminated list of strings, whose
/// length and order is consistent with that of the field offset vector.
RelativeDirectPointer<char, /*nullable*/ true> FieldNames;
RelativeDirectPointer<const char, /*nullable*/ true> FieldNames;

/// The field type vector accessor. Returns a pointer to an array of
/// type metadata references whose order is consistent with that of the
Expand All @@ -1313,7 +1313,7 @@ struct NominalTypeDescriptor {
/// The names of the cases. A doubly-null-terminated list of strings,
/// whose length is NumNonEmptyCases + NumEmptyCases. Cases are named in
/// tag order, non-empty cases first, followed by empty cases.
RelativeDirectPointer<char, /*nullable*/ true> CaseNames;
RelativeDirectPointer<const char, /*nullable*/ true> CaseNames;
/// The field type vector accessor. Returns a pointer to an array of
/// type metadata references whose order is consistent with that of the
/// CaseNames. Only types for payload cases are provided.
Expand Down Expand Up @@ -1372,20 +1372,19 @@ typedef void (*ClassIVarDestroyer)(HeapObject *);
struct ClassMetadata : public HeapMetadata {
ClassMetadata() = default;
constexpr ClassMetadata(const HeapMetadata &base,
const ClassMetadata *superClass,
uintptr_t data,
ClassFlags flags,
const NominalTypeDescriptor *description,
ClassIVarDestroyer ivarDestroyer,
uintptr_t size, uintptr_t addressPoint,
uintptr_t alignMask,
uintptr_t classSize, uintptr_t classAddressPoint)
const ClassMetadata *superClass,
uintptr_t data,
ClassFlags flags,
ClassIVarDestroyer ivarDestroyer,
uintptr_t size, uintptr_t addressPoint,
uintptr_t alignMask,
uintptr_t classSize, uintptr_t classAddressPoint)
: HeapMetadata(base), SuperClass(superClass),
CacheData{nullptr, nullptr}, Data(data),
Flags(flags), InstanceAddressPoint(addressPoint),
InstanceSize(size), InstanceAlignMask(alignMask),
Reserved(0), ClassSize(classSize), ClassAddressPoint(classAddressPoint),
Description(description), IVarDestroyer(ivarDestroyer) {}
Description(nullptr), IVarDestroyer(ivarDestroyer) {}

/// The metadata for the superclass. This is null for the root class.
const ClassMetadata *SuperClass;
Expand Down Expand Up @@ -1443,7 +1442,8 @@ struct ClassMetadata : public HeapMetadata {
/// if this is an artificial subclass. We currently provide no
/// supported mechanism for making a non-artificial subclass
/// dynamically.
const NominalTypeDescriptor *Description;
FarRelativeDirectPointer<const NominalTypeDescriptor,
/*nullable*/ true> Description;

/// A function for destroying instance variables, used to clean up
/// after an early return from a constructor.
Expand All @@ -1462,6 +1462,10 @@ struct ClassMetadata : public HeapMetadata {
assert(!isArtificialSubclass());
return Description;
}

void setDescription(const NominalTypeDescriptor *description) {
Description = description;
}

ClassIVarDestroyer getIVarDestroyer() const {
assert(isTypeMetadata());
Expand Down Expand Up @@ -1713,14 +1717,40 @@ struct ForeignClassMetadata : public ForeignTypeMetadata {
}
};

/// The structure of type metadata for structs.
struct StructMetadata : public Metadata {
/// The common structure of metadata for structs and enums.
struct ValueMetadata : public Metadata {
ValueMetadata(MetadataKind Kind, const NominalTypeDescriptor *description,
const Metadata *parent)
: Metadata(Kind), Description(description), Parent(parent)
{}

/// An out-of-line description of the type.
const NominalTypeDescriptor *Description;
FarRelativeDirectPointer<const NominalTypeDescriptor> Description;

/// The parent type of this member type, or null if this is not a
/// member type.
const Metadata *Parent;
FarRelativeIndirectablePointer<const Metadata, /*nullable*/ true> Parent;

static bool classof(const Metadata *metadata) {
return metadata->getKind() == MetadataKind::Struct
|| metadata->getKind() == MetadataKind::Enum
|| metadata->getKind() == MetadataKind::Optional;
}

/// Retrieve the generic arguments of this type.
const Metadata * const *getGenericArgs() const {
if (Description->GenericParams.NumParams == 0)
return nullptr;

const void* const *asWords = reinterpret_cast<const void * const *>(this);
asWords += Description->GenericParams.Offset;
return reinterpret_cast<const Metadata * const *>(asWords);
}
};

/// The structure of type metadata for structs.
struct StructMetadata : public ValueMetadata {
using ValueMetadata::ValueMetadata;

/// Get a pointer to the field offset vector, if present, or null.
const uintptr_t *getFieldOffsets() const {
Expand All @@ -1740,29 +1770,14 @@ struct StructMetadata : public Metadata {
return getter(this);
}

/// Retrieve the generic arguments of this struct.
const Metadata * const *getGenericArgs() const {
if (Description->GenericParams.NumParams == 0)
return nullptr;

const void* const *asWords = reinterpret_cast<const void * const *>(this);
asWords += Description->GenericParams.Offset;
return reinterpret_cast<const Metadata * const *>(asWords);
}

static bool classof(const Metadata *metadata) {
return metadata->getKind() == MetadataKind::Struct;
}
};

/// The structure of type metadata for enums.
struct EnumMetadata : public Metadata {
/// An out-of-line description of the type.
const NominalTypeDescriptor *Description;

/// The parent type of this member type, or null if this is not a
/// member type.
const Metadata *Parent;
struct EnumMetadata : public ValueMetadata {
using ValueMetadata::ValueMetadata;

/// True if the metadata records the size of the payload area.
bool hasPayloadSize() const {
Expand All @@ -1788,16 +1803,6 @@ struct EnumMetadata : public Metadata {
return *asWords;
}

/// Retrieve the generic arguments of this enum.
const Metadata * const *getGenericArgs() const {
const void* const *asWords = reinterpret_cast<const void * const *>(this);
if (Description->GenericParams.NumParams == 0)
return nullptr;

asWords += Description->GenericParams.Offset;
return reinterpret_cast<const Metadata * const *>(asWords);
}

static bool classof(const Metadata *metadata) {
return metadata->getKind() == MetadataKind::Enum
|| metadata->getKind() == MetadataKind::Optional;
Expand Down Expand Up @@ -2215,7 +2220,7 @@ struct GenericWitnessTable {
/*nullable*/ true> Protocol;

/// The pattern.
RelativeDirectPointer<WitnessTable> Pattern;
RelativeDirectPointer<const WitnessTable> Pattern;

/// The instantiation function, which is called after the template is copied.
RelativeDirectPointer<void(WitnessTable *instantiatedTable,
Expand All @@ -2240,7 +2245,7 @@ struct TypeMetadataRecord {
// Some description of the type that is resolvable at runtime.
union {
/// A direct reference to the metadata.
RelativeDirectPointer<Metadata> DirectType;
RelativeDirectPointer<const Metadata> DirectType;

/// The nominal type descriptor for a resilient or generic type.
RelativeDirectPointer<NominalTypeDescriptor> TypeDescriptor;
Expand Down Expand Up @@ -2327,7 +2332,7 @@ struct ProtocolConformanceRecord {
// The conformance, or a generator function for the conformance.
union {
/// A direct reference to the witness table for the conformance.
RelativeDirectPointer<WitnessTable> WitnessTable;
RelativeDirectPointer<const WitnessTable> WitnessTable;

/// A function that produces the witness table given an instance of the
/// type. The function may return null if a specific instance does not
Expand Down Expand Up @@ -2502,7 +2507,7 @@ swift_allocateGenericClassMetadata(GenericMetadata *pattern,

// Callback to allocate a generic struct/enum metadata object.
SWIFT_RUNTIME_EXPORT
extern "C" Metadata *
extern "C" ValueMetadata *
swift_allocateGenericValueMetadata(GenericMetadata *pattern,
const void *arguments);

Expand Down
Loading