-
Notifications
You must be signed in to change notification settings - Fork 10.5k
RemoteMirror: Project some multi-payload enums #30357
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
Changes from all commits
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 |
---|---|---|
|
@@ -64,44 +64,20 @@ class MemoryReader { | |
/// Attempts to read an integer of the specified size from the given | ||
/// address in the remote process. | ||
/// | ||
/// Returns false if the operation failed, the request size is not | ||
/// 1, 2, 4, or 8, or the request size is larger than the provided destination. | ||
/// Returns false if the operation failed, or the request size is | ||
/// larger than the provided destination. | ||
template <typename IntegerType> | ||
bool readInteger(RemoteAddress address, size_t bytes, IntegerType *dest) { | ||
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. A more flexible |
||
if (bytes > sizeof(IntegerType)) | ||
return false; | ||
switch (bytes) { | ||
case 1: { | ||
uint8_t n; | ||
if (!readInteger(address, &n)) | ||
return false; | ||
*dest = n; | ||
break; | ||
} | ||
case 2: { | ||
uint16_t n; | ||
if (!readInteger(address, &n)) | ||
return false; | ||
*dest = n; | ||
break; | ||
} | ||
case 4: { | ||
uint32_t n; | ||
if (!readInteger(address, &n)) | ||
return false; | ||
*dest = n; | ||
break; | ||
} | ||
case 8: { | ||
uint64_t n; | ||
if (!readInteger(address, &n)) | ||
return false; | ||
*dest = n; | ||
break; | ||
} | ||
default: | ||
*dest = 0; | ||
if ((bytes > sizeof(IntegerType)) | ||
|| !readBytes(address, (uint8_t *)dest, bytes)) { | ||
return false; | ||
} | ||
// FIXME: Assumes host and target have the same endianness. | ||
// TODO: Query DLQ for endianness of target, compare to endianness of host. | ||
#if defined(__BIG_ENDIAN__) | ||
*dest >>= (sizeof(IntegerType) - bytes); | ||
#endif | ||
return true; | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -363,9 +363,53 @@ bool RecordTypeInfo::readExtraInhabitantIndex(remote::MemoryReader &reader, | |
return false; | ||
} | ||
|
||
case RecordKind::MultiPayloadEnum:// XXX TODO | ||
return false; | ||
|
||
case RecordKind::MultiPayloadEnum: { | ||
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. This block of code counts XIs in MultiPayloadEnums and returns the digested result for an enclosing single-payload enum to use. |
||
// Multi payload enums can export XIs from the tag, if any. | ||
// They never export XIs from their payloads. | ||
auto Fields = getFields(); | ||
unsigned long PayloadCaseCount = 0; | ||
unsigned long NonPayloadCaseCount = 0; | ||
unsigned long PayloadSize = 0; | ||
for (auto Field : Fields) { | ||
if (Field.TR != 0) { | ||
PayloadCaseCount += 1; | ||
if (Field.TI.getSize() > PayloadSize) { | ||
PayloadSize = Field.TI.getSize(); | ||
} | ||
} else { | ||
NonPayloadCaseCount += 1; | ||
} | ||
} | ||
if (getSize() > PayloadSize) { | ||
// Multipayload enums that do use a separate tag | ||
// export XIs from that tag. | ||
unsigned tag = 0; | ||
auto TagSize = getSize() - PayloadSize; | ||
auto TagAddress = remote::RemoteAddress(address.getAddressData() + PayloadSize); | ||
if (!reader.readInteger(TagAddress, TagSize, &tag)) | ||
return false; | ||
if (tag < Fields.size()) { | ||
*extraInhabitantIndex = -1; // Valid payload, not an XI | ||
} else if (TagSize >= 4) { | ||
// This is really just the 32-bit 2s-complement negation of `tag`, but | ||
// coded so as to ensure we cannot overflow or underflow. | ||
*extraInhabitantIndex = static_cast<int>( | ||
std::numeric_limits<uint32_t>::max() | ||
- (tag & std::numeric_limits<uint32_t>::max()) | ||
+ 1); | ||
} else { | ||
// XIs are coded starting from the highest value that fits | ||
// E.g., for 1-byte tag, tag 255 == XI #0, tag 254 == XI #1, etc. | ||
unsigned maxTag = (1U << (TagSize * 8U)) - 1; | ||
*extraInhabitantIndex = maxTag - tag; | ||
} | ||
return true; | ||
} else { | ||
// Multipayload enums that don't use a separate tag never | ||
// export XIs. | ||
return false; | ||
} | ||
} | ||
} | ||
return false; | ||
} | ||
|
@@ -1260,7 +1304,7 @@ class EnumTypeInfoBuilder { | |
Size += tagCounts.numTagBytes; | ||
// Dynamic multi-payload enums use the tag representations not assigned | ||
// to cases for extra inhabitants. | ||
if (tagCounts.numTagBytes >= 32) { | ||
if (tagCounts.numTagBytes >= 4) { | ||
tbkka marked this conversation as resolved.
Show resolved
Hide resolved
|
||
NumExtraInhabitants = ValueWitnessFlags::MaxNumExtraInhabitants; | ||
} else { | ||
NumExtraInhabitants = | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -378,6 +378,107 @@ reflect(enumValue: OneIndirectPayload.leafF) | |
// CHECK-NEXT: (enum reflect_Enum_value.OneIndirectPayload) | ||
// CHECK-NEXT: Value: .leafF | ||
|
||
enum MultiPayload { | ||
case stampA | ||
case envelopeA(Int64) | ||
case stampB | ||
case envelopeB(Double) | ||
case stampC | ||
case envelopeC((Int32, Int32)) | ||
case stampD | ||
case stampE | ||
} | ||
|
||
enum SinglePayloadEnumWithMultiPayloadEnumPayload { | ||
case payloadA(MultiPayload) | ||
case alsoA | ||
case alsoB | ||
case alsoC | ||
case alsoD | ||
} | ||
|
||
reflect(enumValue: SinglePayloadEnumWithMultiPayloadEnumPayload.payloadA(.stampB)) | ||
|
||
// CHECK: Reflecting an enum value. | ||
// CHECK-NEXT: Type reference: | ||
// CHECK-NEXT: (enum reflect_Enum_value.SinglePayloadEnumWithMultiPayloadEnumPayload) | ||
// CHECK-NEXT: Value: .payloadA(.stampB) | ||
|
||
reflect(enumValue: SinglePayloadEnumWithMultiPayloadEnumPayload.payloadA(.envelopeC((1,2)))) | ||
|
||
// CHECK: Reflecting an enum value. | ||
// CHECK-NEXT: Type reference: | ||
// CHECK-NEXT: (enum reflect_Enum_value.SinglePayloadEnumWithMultiPayloadEnumPayload) | ||
// CHECK-NEXT: Value: .payloadA(.envelopeC(_)) | ||
|
||
reflect(enumValue: SinglePayloadEnumWithMultiPayloadEnumPayload.alsoC) | ||
|
||
// CHECK: Reflecting an enum value. | ||
// CHECK-NEXT: Type reference: | ||
// CHECK-NEXT: (enum reflect_Enum_value.SinglePayloadEnumWithMultiPayloadEnumPayload) | ||
// CHECK-NEXT: Value: .alsoC | ||
|
||
reflect(enumValue: Optional<Optional<SinglePayloadEnumWithMultiPayloadEnumPayload>>.some(.none)) | ||
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. This test, in particular, verifies that XIs are being properly counted in the multi-payload enum for use by the surrounding single-payload optionals. |
||
|
||
// CHECK: Reflecting an enum value. | ||
// CHECK-NEXT: Type reference: | ||
// CHECK-NEXT: (bound_generic_enum Swift.Optional | ||
// CHECK-NEXT: (bound_generic_enum Swift.Optional | ||
// CHECK-NEXT: (enum reflect_Enum_value.SinglePayloadEnumWithMultiPayloadEnumPayload))) | ||
// CHECK-NEXT: Value: .some(.none) | ||
|
||
enum SmallMultiPayload { | ||
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 code internally handles payloads with size >= 4 in a different path (to avoid overflow issues on 32-bit platforms). So the test above is repeated with a multi-payload enum that has a 2-byte payload area. |
||
case stampA | ||
case envelopeA(Int8) | ||
case stampB | ||
case envelopeB(Int16) | ||
case stampC | ||
case envelopeC((UInt8, Int8)) | ||
case stampD | ||
case stampE | ||
} | ||
|
||
enum SinglePayloadEnumWithSmallMultiPayloadEnumPayload { | ||
case payloadA(SmallMultiPayload) | ||
case alsoA | ||
case alsoB | ||
case alsoC | ||
case alsoD | ||
} | ||
|
||
reflect(enumValue: SinglePayloadEnumWithSmallMultiPayloadEnumPayload.payloadA(.stampB)) | ||
|
||
// CHECK: Reflecting an enum value. | ||
// CHECK-NEXT: Type reference: | ||
// CHECK-NEXT: (enum reflect_Enum_value.SinglePayloadEnumWithSmallMultiPayloadEnumPayload) | ||
// CHECK-NEXT: Value: .payloadA(.stampB) | ||
|
||
reflect(enumValue: SinglePayloadEnumWithSmallMultiPayloadEnumPayload.payloadA(.envelopeC((1,2)))) | ||
|
||
// CHECK: Reflecting an enum value. | ||
// CHECK-NEXT: Type reference: | ||
// CHECK-NEXT: (enum reflect_Enum_value.SinglePayloadEnumWithSmallMultiPayloadEnumPayload) | ||
// CHECK-NEXT: Value: .payloadA(.envelopeC(_)) | ||
|
||
reflect(enumValue: SinglePayloadEnumWithSmallMultiPayloadEnumPayload.alsoC) | ||
|
||
// CHECK: Reflecting an enum value. | ||
// CHECK-NEXT: Type reference: | ||
// CHECK-NEXT: (enum reflect_Enum_value.SinglePayloadEnumWithSmallMultiPayloadEnumPayload) | ||
// CHECK-NEXT: Value: .alsoC | ||
|
||
reflect(enumValue: Optional<Optional<SinglePayloadEnumWithSmallMultiPayloadEnumPayload>>.some(.none)) | ||
|
||
// CHECK: Reflecting an enum value. | ||
// CHECK-NEXT: Type reference: | ||
// CHECK-NEXT: (bound_generic_enum Swift.Optional | ||
// CHECK-NEXT: (bound_generic_enum Swift.Optional | ||
// CHECK-NEXT: (enum reflect_Enum_value.SinglePayloadEnumWithSmallMultiPayloadEnumPayload))) | ||
// CHECK-NEXT: Value: .some(.none) | ||
|
||
|
||
// XXX TODO: Multipayload enums with pointer payloads | ||
|
||
|
||
// XXX TODO: test enum with thin function payload XXX | ||
|
||
|
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.
This block of code actually identifies the current case for multi-payload enums. Today, it only handles enums that use a separate tag (as inferred from the size data exposed in the type information). For other enum layouts it returns
false
to indicate that it is unable to parse that particular enum.