-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Class resilience part 10 #13687
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
Class resilience part 10 #13687
Changes from all commits
e3d6a2b
22f08f7
fea92a0
66ba75c
6af8d18
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -992,6 +992,35 @@ class MetadataReader { | |
swift_runtime_unreachable("Unhandled IsaEncodingKind in switch."); | ||
} | ||
|
||
/// Read the offset of the generic parameters of a class from the nominal | ||
/// type descriptor. If the class has a resilient superclass, we also | ||
/// have to read the superclass size and add that to the offset. | ||
/// | ||
/// The offset is in units of words, from the start of the class's | ||
/// metadata. | ||
std::pair<bool, uint32_t> | ||
readGenericArgsOffset(MetadataRef metadata, | ||
NominalTypeDescriptorRef descriptor) { | ||
if (metadata->getKind() == MetadataKind::Class) { | ||
auto *classMetadata = cast<TargetClassMetadata<Runtime>>(metadata); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we support There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks. |
||
if (classMetadata->SuperClass) { | ||
auto superMetadata = readMetadata(classMetadata->SuperClass); | ||
if (!superMetadata) | ||
return std::make_pair(false, 0); | ||
|
||
auto result = | ||
descriptor->GenericParams.getOffset( | ||
classMetadata, | ||
cast<TargetClassMetadata<Runtime>>(superMetadata)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's a little unfortunate that we have to read the superclass unconditionally here, even if the superclass is not resilient — but it works, and we can improve it later. |
||
|
||
return std::make_pair(true, result); | ||
} | ||
} | ||
|
||
auto result = descriptor->GenericParams.getOffset(); | ||
return std::make_pair(true, result); | ||
} | ||
|
||
/// Read a single generic type argument from a bound generic type | ||
/// metadata. | ||
std::pair<bool, StoredPointer> | ||
|
@@ -1010,11 +1039,14 @@ class MetadataReader { | |
return std::make_pair(false, 0); | ||
|
||
auto numGenericParams = descriptor->GenericParams.NumPrimaryParams; | ||
auto offsetToGenericArgs = | ||
sizeof(StoredPointer) * (descriptor->GenericParams.Offset); | ||
auto offsetToGenericArgs = readGenericArgsOffset(Meta, descriptor); | ||
if (!offsetToGenericArgs.first) | ||
return std::make_pair(false, 0); | ||
|
||
auto addressOfGenericArgAddress = | ||
Meta.getAddress() + offsetToGenericArgs + | ||
index * sizeof(StoredPointer); | ||
(Meta.getAddress() + | ||
offsetToGenericArgs.second * sizeof(StoredPointer) + | ||
index * sizeof(StoredPointer)); | ||
|
||
if (index >= numGenericParams) | ||
return std::make_pair(false, 0); | ||
|
@@ -1357,10 +1389,13 @@ class MetadataReader { | |
std::vector<BuiltType> substitutions; | ||
|
||
auto numGenericParams = descriptor->GenericParams.NumPrimaryParams; | ||
auto offsetToGenericArgs = | ||
sizeof(StoredPointer) * (descriptor->GenericParams.Offset); | ||
auto offsetToGenericArgs = readGenericArgsOffset(metadata, descriptor); | ||
if (!offsetToGenericArgs.first) | ||
return {}; | ||
|
||
auto addressOfGenericArgAddress = | ||
metadata.getAddress() + offsetToGenericArgs; | ||
(metadata.getAddress() + sizeof(StoredPointer) * | ||
offsetToGenericArgs.second); | ||
|
||
using ArgIndex = decltype(descriptor->GenericParams.NumPrimaryParams); | ||
for (ArgIndex i = 0; i < numGenericParams; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -39,6 +39,7 @@ | |
#if SWIFT_OBJC_INTEROP | ||
#include <objc/runtime.h> | ||
#endif | ||
#include "llvm/Support/Casting.h" | ||
|
||
namespace swift { | ||
|
||
|
@@ -1029,13 +1030,22 @@ struct GenericContextDescriptor { | |
}; | ||
|
||
/// Header for a generic parameter descriptor. | ||
struct GenericParameterDescriptorHeader { | ||
template<typename Runtime> | ||
struct TargetGenericParameterDescriptorHeader { | ||
using StoredPointer = typename Runtime::StoredPointer; | ||
using ClassMetadata = TargetClassMetadata<Runtime>; | ||
|
||
private: | ||
/// The offset to the first generic argument from the start of | ||
/// metadata record. | ||
/// metadata record, in words. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably best to say that this is from the address point of the metadata record. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed. |
||
/// | ||
/// This is meaningful if NumGenericRequirements is nonzero. | ||
/// | ||
/// If this class has a resilient superclass, this offset is relative to the | ||
/// size of the resilient superclass metadata. Otherwise, it is absolute. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you really ought to introduce a term of art for the relative base here. I believe you use "immediate class data" in the compiler, so maybe "the start of the class's immediate class data"? If this is absolute, I think the runtime really ought to validate its correctness, i.e. that it doesn't overlap the superclass's class members. It would be okay to fatal-error if that check fails. I think we probably ought to prepare the ABI to allow class data to be placed at negative offsets, even if we're not planning to take advantage of that in Swift 5. I think all you really there is (1) some way to know the total size of the immediate class data, not just the offset of its start, and (2) some way to record that it should be placed at a negative offset. Also, I guess, it means all of these offsets should use signed types rather than unsigned types. |
||
uint32_t Offset; | ||
|
||
public: | ||
/// The amount of generic requirement data in the metadata record, in | ||
/// words, excluding the lexical parent type. A value of zero means | ||
/// there is no generic requirement data. | ||
|
@@ -1056,6 +1066,30 @@ struct GenericParameterDescriptorHeader { | |
/// Flags for this generic parameter descriptor. | ||
GenericParameterDescriptorFlags Flags; | ||
|
||
/// This is factored in a silly way because remote mirrors cannot directly | ||
/// dereference the SuperClass field of class metadata. | ||
uint32_t getOffset(const ClassMetadata *classMetadata, | ||
const ClassMetadata *superMetadata) const { | ||
if (Flags.hasResilientSuperclass()) | ||
return superMetadata->getSizeInWords() + Offset; | ||
|
||
return Offset; | ||
} | ||
|
||
uint32_t getOffset() const { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's generally better to name overloads differently when their semantics are different like this. Here I think you should call out that this method assumes that the offset isn't resilient. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've added comments, but I prefer to keep the names as-is. |
||
assert(!Flags.hasResilientSuperclass()); | ||
return Offset; | ||
} | ||
|
||
uint32_t getOffset(const TargetMetadata<Runtime> *metadata) const { | ||
if (auto *classMetadata = llvm::dyn_cast<ClassMetadata>(metadata)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe this could check for a resilient superclass first instead of checking N things involving the metadata? And if it's resilient, hey, you know it's a class. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
if (auto *superclass = classMetadata->SuperClass) | ||
if (auto *superMetadata = llvm::dyn_cast<ClassMetadata>(superclass)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The superclass will always be a class; you don't need to check this. ClassMetadata::classof doesn't check whether it's type metadata, just that it's a class. Is it possible to have a resilient superclass that ends up being an ObjC class (i.e. not type metadata)? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks, I've simplified it. I don't think an Objective-C class can be "resilient" in this manner because Objective-C class metadata always has a fixed size, no? There are no "members" in the sense of generic arguments, field offsets, vtable entries, etc. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right, I think that's reasonable. Since that's true, you don't need to check for isTypeMetadata() in the case that you actually have to pull out the superclass size. |
||
return getOffset(classMetadata, superMetadata); | ||
|
||
return getOffset(); | ||
} | ||
|
||
/// True if the nominal type has generic requirements other than its | ||
/// parent metadata. | ||
bool hasGenericRequirements() const { return NumGenericRequirements > 0; } | ||
|
@@ -1080,14 +1114,31 @@ struct TargetMethodDescriptor { | |
/// Header for a class vtable descriptor. This is a variable-sized | ||
/// structure that describes how to find and parse a vtable | ||
/// within the type metadata for a class. | ||
struct VTableDescriptorHeader { | ||
/// The offset of the vtable for this class in its metadata, if any. | ||
template <typename Runtime> | ||
struct TargetVTableDescriptorHeader { | ||
using StoredPointer = typename Runtime::StoredPointer; | ||
|
||
private: | ||
/// The offset of the vtable for this class in its metadata, if any, | ||
/// in words. | ||
/// | ||
/// If this class has a resilient superclass, this offset is relative to the | ||
/// size of the resilient superclass metadata. Otherwise, it is absolute. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another place to use the term of art for the relative base. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
uint32_t VTableOffset; | ||
|
||
public: | ||
/// The number of vtable entries. This is the number of MethodDescriptor | ||
/// records following the vtable header in the class's nominal type | ||
/// descriptor, which is equal to the number of words this subclass's vtable | ||
/// entries occupy in instantiated class metadata. | ||
uint32_t VTableSize; | ||
|
||
uint32_t getVTableOffset(const TargetClassMetadata<Runtime> *metadata) const { | ||
const auto *description = metadata->getDescription(); | ||
if (description->GenericParams.Flags.hasResilientSuperclass()) | ||
return metadata->SuperClass->getSizeInWords() + VTableOffset; | ||
return VTableOffset; | ||
} | ||
}; | ||
|
||
template<typename Runtime> struct TargetNominalTypeDescriptor; | ||
|
@@ -1096,7 +1147,7 @@ template<typename Runtime> | |
using TargetNominalTypeDescriptorTrailingObjects | ||
= swift::ABI::TrailingObjects<TargetNominalTypeDescriptor<Runtime>, | ||
GenericContextDescriptor, | ||
VTableDescriptorHeader, | ||
TargetVTableDescriptorHeader<Runtime>, | ||
TargetMethodDescriptor<Runtime>>; | ||
|
||
/// Common information about all nominal types. For generic types, this | ||
|
@@ -1116,6 +1167,9 @@ struct TargetNominalTypeDescriptor final | |
public: | ||
using StoredPointer = typename Runtime::StoredPointer; | ||
using MethodDescriptor = TargetMethodDescriptor<Runtime>; | ||
using VTableDescriptorHeader = TargetVTableDescriptorHeader<Runtime>; | ||
using GenericParameterDescriptorHeader = TargetGenericParameterDescriptorHeader<Runtime>; | ||
using ClassMetadata = TargetClassMetadata<Runtime>; | ||
|
||
/// The mangled name of the nominal type. | ||
TargetRelativeDirectPointer<Runtime, const char> Name; | ||
|
@@ -1127,19 +1181,22 @@ struct TargetNominalTypeDescriptor final | |
/// The number of stored properties in the class, not including its | ||
/// superclasses. If there is a field offset vector, this is its length. | ||
uint32_t NumFields; | ||
|
||
private: | ||
/// The offset of the field offset vector for this class's stored | ||
/// properties in its metadata, if any. 0 means there is no field offset | ||
/// properties in its metadata, in words. 0 means there is no field offset | ||
/// vector. | ||
/// | ||
/// To deal with resilient superclasses correctly, this will | ||
/// eventually need to be relative to the start of this class's | ||
/// metadata area. | ||
/// If this class has a resilient superclass, this offset is relative to | ||
/// the size of the resilient superclass metadata. Otherwise, it is | ||
/// absolute. | ||
uint32_t FieldOffsetVectorOffset; | ||
|
||
|
||
public: | ||
/// The field names. A doubly-null-terminated list of strings, whose | ||
/// length and order is consistent with that of the field offset vector. | ||
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 | ||
/// field offset vector. | ||
|
@@ -1148,7 +1205,16 @@ struct TargetNominalTypeDescriptor final | |
|
||
/// True if metadata records for this type have a field offset vector for | ||
/// its stored properties. | ||
bool hasFieldOffsetVector() const { return FieldOffsetVectorOffset != 0; } | ||
bool hasFieldOffsetVector() const { return FieldOffsetVectorOffset != 0; } | ||
|
||
unsigned getFieldOffsetVectorOffset(const ClassMetadata *metadata) const { | ||
const auto *description = metadata->getDescription(); | ||
|
||
if (description->GenericParams.Flags.hasResilientSuperclass()) | ||
return metadata->SuperClass->getSizeInWords() + FieldOffsetVectorOffset; | ||
|
||
return FieldOffsetVectorOffset; | ||
} | ||
} Class; | ||
|
||
/// Information about struct types. | ||
|
@@ -1503,7 +1569,7 @@ struct TargetClassMetadata : public TargetHeapMetadata<Runtime> { | |
/// Get a pointer to the field offset vector, if present, or null. | ||
const StoredPointer *getFieldOffsets() const { | ||
assert(isTypeMetadata()); | ||
auto offset = getDescription()->Class.FieldOffsetVectorOffset; | ||
auto offset = getDescription()->Class.getFieldOffsetVectorOffset(this); | ||
if (offset == 0) | ||
return nullptr; | ||
auto asWords = reinterpret_cast<const void * const*>(this); | ||
|
@@ -1520,6 +1586,13 @@ struct TargetClassMetadata : public TargetHeapMetadata<Runtime> { | |
return getter(this); | ||
} | ||
|
||
uint32_t getSizeInWords() const { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this should be signed, and I think the name should use your term of art. |
||
assert(isTypeMetadata()); | ||
uint32_t size = getClassSize() - getClassAddressPoint(); | ||
assert(size % sizeof(StoredPointer) == 0); | ||
return size / sizeof(StoredPointer); | ||
} | ||
|
||
static bool classof(const TargetMetadata<Runtime> *metadata) { | ||
return metadata->getKind() == MetadataKind::Class; | ||
} | ||
|
@@ -1768,7 +1841,7 @@ struct TargetValueMetadata : public TargetMetadata<Runtime> { | |
|
||
auto asWords = reinterpret_cast< | ||
ConstTargetMetadataPointer<Runtime, swift::TargetMetadata> const *>(this); | ||
return (asWords + Description->GenericParams.Offset); | ||
return (asWords + Description->GenericParams.getOffset(this)); | ||
} | ||
|
||
const TargetNominalTypeDescriptor<Runtime> *getDescription() const { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we not use
Optional<>
in this file? I guess we seem not to be.