mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
RemoteMirror: Project some multi-payload enums (#30357)
* First part of multi-payload enum support This handles multi-payload enums with fixed layouts that don't use spare payload bits. It includes XI calculations that allow us to handle single-payload enums where the payload ultimately includes a multi-payload enum (For example, on 32-bit platforms, String uses a multi-payload enum, so this now supports single-payload enums carrying Strings.)
This commit is contained in:
@@ -788,7 +788,7 @@ public:
|
|||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
// No payload: Payload area is reused for more cases
|
// No payload: Payload area is reused for more cases
|
||||||
unsigned PayloadTag = 0;
|
uint32_t PayloadTag = 0;
|
||||||
auto PayloadTagSize = std::min(PayloadSize, decltype(PayloadSize)(sizeof(PayloadTag)));
|
auto PayloadTagSize = std::min(PayloadSize, decltype(PayloadSize)(sizeof(PayloadTag)));
|
||||||
if (!getReader().readInteger(EnumAddress, PayloadTagSize, &PayloadTag)) {
|
if (!getReader().readInteger(EnumAddress, PayloadTagSize, &PayloadTag)) {
|
||||||
return false;
|
return false;
|
||||||
@@ -801,7 +801,50 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
case RecordKind::MultiPayloadEnum: {
|
case RecordKind::MultiPayloadEnum: {
|
||||||
// TODO: Support multipayload enums
|
// Collect basic statistics about the enum
|
||||||
|
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 (EnumSize > PayloadSize) {
|
||||||
|
// If the compiler laid this out with a separate tag, use that.
|
||||||
|
unsigned tag = 0;
|
||||||
|
auto TagSize = EnumSize - PayloadSize;
|
||||||
|
auto TagAddress = remote::RemoteAddress(EnumAddress.getAddressData() + PayloadSize);
|
||||||
|
if (!getReader().readInteger(TagAddress, TagSize, &tag)
|
||||||
|
|| tag >= Fields.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (tag < PayloadCaseCount) {
|
||||||
|
*CaseIndex = tag;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
auto PayloadTagSize = std::min(PayloadSize, 4UL);
|
||||||
|
// Treat the tag as a page selector; payload carries the offset within the page
|
||||||
|
auto Page = tag - PayloadCaseCount;
|
||||||
|
// Zero for 32-bit because we'll never have more than one page
|
||||||
|
auto PageSize = PayloadTagSize >= 4 ? 0 : 1 << (PayloadSize * 8U);
|
||||||
|
auto PageStart = Page * PageSize;
|
||||||
|
unsigned PayloadTag;
|
||||||
|
if (!getReader().readInteger(EnumAddress, PayloadTagSize, &PayloadTag)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*CaseIndex = PageStart + PayloadTag + PayloadCaseCount;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
// XXX TODO: If the payloads have common spare bits (e.g., all pointers)
|
||||||
|
// then use those to decode the case.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -64,44 +64,20 @@ public:
|
|||||||
/// Attempts to read an integer of the specified size from the given
|
/// Attempts to read an integer of the specified size from the given
|
||||||
/// address in the remote process.
|
/// address in the remote process.
|
||||||
///
|
///
|
||||||
/// Returns false if the operation failed, the request size is not
|
/// Returns false if the operation failed, or the request size is
|
||||||
/// 1, 2, 4, or 8, or the request size is larger than the provided destination.
|
/// larger than the provided destination.
|
||||||
template <typename IntegerType>
|
template <typename IntegerType>
|
||||||
bool readInteger(RemoteAddress address, size_t bytes, IntegerType *dest) {
|
bool readInteger(RemoteAddress address, size_t bytes, IntegerType *dest) {
|
||||||
if (bytes > sizeof(IntegerType))
|
*dest = 0;
|
||||||
return false;
|
if ((bytes > sizeof(IntegerType))
|
||||||
switch (bytes) {
|
|| !readBytes(address, (uint8_t *)dest, 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:
|
|
||||||
return false;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -363,9 +363,53 @@ bool RecordTypeInfo::readExtraInhabitantIndex(remote::MemoryReader &reader,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
case RecordKind::MultiPayloadEnum:// XXX TODO
|
case RecordKind::MultiPayloadEnum: {
|
||||||
return false;
|
// 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;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1260,7 +1304,7 @@ public:
|
|||||||
Size += tagCounts.numTagBytes;
|
Size += tagCounts.numTagBytes;
|
||||||
// Dynamic multi-payload enums use the tag representations not assigned
|
// Dynamic multi-payload enums use the tag representations not assigned
|
||||||
// to cases for extra inhabitants.
|
// to cases for extra inhabitants.
|
||||||
if (tagCounts.numTagBytes >= 32) {
|
if (tagCounts.numTagBytes >= 4) {
|
||||||
NumExtraInhabitants = ValueWitnessFlags::MaxNumExtraInhabitants;
|
NumExtraInhabitants = ValueWitnessFlags::MaxNumExtraInhabitants;
|
||||||
} else {
|
} else {
|
||||||
NumExtraInhabitants =
|
NumExtraInhabitants =
|
||||||
|
|||||||
@@ -269,6 +269,30 @@ reflect(object: ClassWithTwoCaseTwoPayloadsEnum())
|
|||||||
// CHECK-32: (case name=none index=1)))
|
// CHECK-32: (case name=none index=1)))
|
||||||
// CHECK-32: (case name=none index=1))))
|
// CHECK-32: (case name=none index=1))))
|
||||||
|
|
||||||
|
reflect(enumValue: TwoCaseTwoPayloadsEnum.valid(Marker()))
|
||||||
|
|
||||||
|
// CHECK-64: Reflecting an enum value.
|
||||||
|
// CHECK-64-NEXT: Type reference:
|
||||||
|
// CHECK-64-NEXT: (enum reflect_Enum_TwoCaseTwoPayloads.TwoCaseTwoPayloadsEnum)
|
||||||
|
// CHECK-64-NEXT: Value: .valid(_)
|
||||||
|
|
||||||
|
// CHECK-32: Reflecting an enum value.
|
||||||
|
// CHECK-32-NEXT: Type reference:
|
||||||
|
// CHECK-32-NEXT: (enum reflect_Enum_TwoCaseTwoPayloads.TwoCaseTwoPayloadsEnum)
|
||||||
|
// CHECK-32-NEXT: Value: .valid(_)
|
||||||
|
|
||||||
|
reflect(enumValue: TwoCaseTwoPayloadsEnum.invalid(7))
|
||||||
|
|
||||||
|
// CHECK-64: Reflecting an enum value.
|
||||||
|
// CHECK-64-NEXT: Type reference:
|
||||||
|
// CHECK-64-NEXT: (enum reflect_Enum_TwoCaseTwoPayloads.TwoCaseTwoPayloadsEnum)
|
||||||
|
// CHECK-64-NEXT: Value: .invalid(_)
|
||||||
|
|
||||||
|
// CHECK-32: Reflecting an enum value.
|
||||||
|
// CHECK-32-NEXT: Type reference:
|
||||||
|
// CHECK-32-NEXT: (enum reflect_Enum_TwoCaseTwoPayloads.TwoCaseTwoPayloadsEnum)
|
||||||
|
// CHECK-32-NEXT: Value: .invalid(_)
|
||||||
|
|
||||||
doneReflecting()
|
doneReflecting()
|
||||||
|
|
||||||
// CHECK-64: Done.
|
// CHECK-64: Done.
|
||||||
|
|||||||
@@ -378,6 +378,107 @@ reflect(enumValue: OneIndirectPayload.leafF)
|
|||||||
// CHECK-NEXT: (enum reflect_Enum_value.OneIndirectPayload)
|
// CHECK-NEXT: (enum reflect_Enum_value.OneIndirectPayload)
|
||||||
// CHECK-NEXT: Value: .leafF
|
// 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))
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
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
|
// XXX TODO: test enum with thin function payload XXX
|
||||||
|
|
||||||
|
|||||||
@@ -1575,12 +1575,9 @@ reflect(enum: ManyCasesOneStringPayload.payload("hello, world"))
|
|||||||
// CHECK32-NEXT: (case name=otherC index=3))
|
// CHECK32-NEXT: (case name=otherC index=3))
|
||||||
|
|
||||||
// CHECKALL: Enum value:
|
// CHECKALL: Enum value:
|
||||||
// CHECK64-NEXT: (enum_value name=payload index=0
|
// CHECKALL-NEXT: (enum_value name=payload index=0
|
||||||
// CHECK64-NEXT: (struct Swift.String)
|
// CHECKALL-NEXT: (struct Swift.String)
|
||||||
// CHECK64-NEXT: )
|
// CHECKALL-NEXT: )
|
||||||
|
|
||||||
// XXX Note: 32-bit String contains a multi_payload_enum which projectEnumValue cannot yet handle.
|
|
||||||
// CHECK32-NEXT: swift_reflection_projectEnumValue failed.
|
|
||||||
|
|
||||||
reflect(enum: ManyCasesOneStringPayload.otherB)
|
reflect(enum: ManyCasesOneStringPayload.otherB)
|
||||||
|
|
||||||
@@ -1646,12 +1643,7 @@ reflect(enum: ManyCasesOneStringPayload.otherB)
|
|||||||
|
|
||||||
|
|
||||||
// CHECKALL: Enum value:
|
// CHECKALL: Enum value:
|
||||||
// CHECK64-NEXT: (enum_value name=otherB index=2)
|
// CHECKALL-NEXT: (enum_value name=otherB index=2)
|
||||||
|
|
||||||
// XXX Note: 32-bit String contains a multi_payload_enum which projectEnumValue cannot yet handle.
|
|
||||||
// CHECK32-NEXT: swift_reflection_projectEnumValue failed.
|
|
||||||
|
|
||||||
//reflect(enum: ManyCasesManyPayloads.a("hi, world"))
|
|
||||||
|
|
||||||
doneReflecting()
|
doneReflecting()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user