Implement MultiPayloadEnum support for projectEnumValue (#30635)

This code rearchitects and simplifies the projectEnumValue support by
introducing a new `TypeInfo` subclass for each kind of enum, including trivial,
no-payload, single-payload, and three different classes for multi-payload enums:

* "UnsupportedEnum" that we don't understand.  This returns "don't know" answers for all requests in cases where the runtime lacks enough information to accurately handle a particular enum.

* MP Enums that only use a separate tag value.  This includes generic enums and other dynamic layouts, as well as enums whose payloads have no spare bits.

* MP Enums that use spare bits, possibly in addition to a separate tag.  This logic can only be used, of course, if we can in fact compute a spare bit mask that agrees with the compiler.

The final challenge is to choose one of the above three handlings for every MPE.  Currently, we do not have an accurate source of information for the spare bit mask, so we never choose the third option above.  We use the second option for dynamic MPE layouts (including generics) and the first for everything else.

TODO: Once we can arrange for the compiler to expose spare bit mask data, we'll be able to use that to drive more MPE cases.
This commit is contained in:
tbkka
2020-03-31 15:12:44 -07:00
committed by GitHub
parent 8d68607681
commit 3c8fde7885
12 changed files with 1110 additions and 554 deletions

View File

@@ -324,12 +324,6 @@ swift_layout_kind_t getTypeInfoKind(const TypeInfo &TI) {
return SWIFT_TUPLE;
case RecordKind::Struct:
return SWIFT_STRUCT;
case RecordKind::NoPayloadEnum:
return SWIFT_NO_PAYLOAD_ENUM;
case RecordKind::SinglePayloadEnum:
return SWIFT_SINGLE_PAYLOAD_ENUM;
case RecordKind::MultiPayloadEnum:
return SWIFT_MULTI_PAYLOAD_ENUM;
case RecordKind::ThickFunction:
return SWIFT_THICK_FUNCTION;
case RecordKind::OpaqueExistential:
@@ -346,6 +340,17 @@ swift_layout_kind_t getTypeInfoKind(const TypeInfo &TI) {
return SWIFT_CLOSURE_CONTEXT;
}
}
case TypeInfoKind::Enum: {
auto &EnumTI = cast<EnumTypeInfo>(TI);
switch (EnumTI.getEnumKind()) {
case EnumKind::NoPayloadEnum:
return SWIFT_NO_PAYLOAD_ENUM;
case EnumKind::SinglePayloadEnum:
return SWIFT_SINGLE_PAYLOAD_ENUM;
case EnumKind::MultiPayloadEnum:
return SWIFT_MULTI_PAYLOAD_ENUM;
}
}
case TypeInfoKind::Reference: {
auto &ReferenceTI = cast<ReferenceTypeInfo>(TI);
switch (ReferenceTI.getReferenceKind()) {
@@ -372,8 +377,11 @@ static swift_typeinfo_t convertTypeInfo(const TypeInfo *TI) {
}
unsigned NumFields = 0;
if (auto *RecordTI = dyn_cast<RecordTypeInfo>(TI))
if (auto *RecordTI = dyn_cast<EnumTypeInfo>(TI)) {
NumFields = RecordTI->getNumCases();
} else if (auto *RecordTI = dyn_cast<RecordTypeInfo>(TI)) {
NumFields = RecordTI->getNumFields();
}
return {
getTypeInfoKind(*TI),
@@ -385,14 +393,20 @@ static swift_typeinfo_t convertTypeInfo(const TypeInfo *TI) {
}
static swift_childinfo_t convertChild(const TypeInfo *TI, unsigned Index) {
auto *RecordTI = cast<RecordTypeInfo>(TI);
auto &FieldInfo = RecordTI->getFields()[Index];
const FieldInfo *FieldInfo;
if (auto *EnumTI = dyn_cast<EnumTypeInfo>(TI)) {
FieldInfo = &(EnumTI->getCases()[Index]);
} else if (auto *RecordTI = dyn_cast<RecordTypeInfo>(TI)) {
FieldInfo = &(RecordTI->getFields()[Index]);
} else {
assert(false && "convertChild(TI): TI must be record or enum typeinfo");
}
return {
FieldInfo.Name.c_str(),
FieldInfo.Offset,
getTypeInfoKind(FieldInfo.TI),
reinterpret_cast<swift_typeref_t>(FieldInfo.TR),
FieldInfo->Name.c_str(),
FieldInfo->Offset,
getTypeInfoKind(FieldInfo->TI),
reinterpret_cast<swift_typeref_t>(FieldInfo->TR),
};
}
@@ -479,30 +493,16 @@ int swift_reflection_projectEnumValue(SwiftReflectionContextRef ContextRef,
auto Context = ContextRef->nativeContext;
auto EnumTR = reinterpret_cast<const TypeRef *>(EnumTypeRef);
auto RemoteEnumAddress = RemoteAddress(EnumAddress);
return Context->projectEnumValue(RemoteEnumAddress, EnumTR, CaseIndex);
}
int swift_reflection_getEnumCaseTypeRef(SwiftReflectionContextRef ContextRef,
swift_typeref_t EnumTypeRef,
int CaseIndex,
char **CaseName,
swift_typeref_t *PayloadTypeRef) {
*PayloadTypeRef = 0;
*CaseName = nullptr;
auto Context = ContextRef->nativeContext;
auto EnumTR = reinterpret_cast<const TypeRef *>(EnumTypeRef);
const TypeRef *PayloadTR = nullptr;
std::string Name;
auto success = Context->getEnumCaseTypeRef(EnumTR, CaseIndex,
Name, &PayloadTR);
if (success) {
*PayloadTypeRef = reinterpret_cast<swift_typeref_t>(PayloadTR);
// FIXME: Is there a better way to return a string here?
// Just returning Case.Name.c_str() doesn't work as the backing data gets
// released at the end of this function.
*CaseName = strdup(Name.c_str());
if (!Context->projectEnumValue(RemoteEnumAddress, EnumTR, CaseIndex)) {
return false;
}
return success;
auto TI = Context->getTypeInfo(EnumTR);
auto *RecordTI = dyn_cast<EnumTypeInfo>(TI);
assert(RecordTI != nullptr);
if (static_cast<size_t>(*CaseIndex) >= RecordTI->getNumCases()) {
return false;
}
return true;
}
void swift_reflection_dumpTypeRef(swift_typeref_t OpaqueTypeRef) {