mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
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:
@@ -711,176 +711,32 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/// Projects the value of an enum.
|
||||
///
|
||||
/// Takes the address and typeref for an enum and determines the
|
||||
/// index of the currently-selected case within the enum.
|
||||
/// You can use this index with `swift_reflection_childOfTypeRef`
|
||||
/// to get detailed information about the specific case.
|
||||
///
|
||||
/// Returns true if the enum case could be successfully determined. In
|
||||
/// particular, note that this code may return false for valid in-memory data
|
||||
/// if the compiler used a strategy we do not yet understand.
|
||||
bool projectEnumValue(RemoteAddress EnumAddress,
|
||||
const TypeRef *EnumTR,
|
||||
int *CaseIndex) {
|
||||
if (EnumTR == nullptr)
|
||||
// Get the TypeInfo and sanity-check it
|
||||
if (EnumTR == nullptr) {
|
||||
return false;
|
||||
auto EnumTI = getTypeInfo(EnumTR);
|
||||
if (EnumTI == nullptr)
|
||||
}
|
||||
auto TI = getTypeInfo(EnumTR);
|
||||
if (TI == nullptr) {
|
||||
return false;
|
||||
|
||||
auto EnumRecordTI = dyn_cast<const RecordTypeInfo>(EnumTI);
|
||||
if (EnumRecordTI == nullptr)
|
||||
}
|
||||
auto EnumTI = dyn_cast<const EnumTypeInfo>(TI);
|
||||
if (EnumTI == nullptr){
|
||||
return false;
|
||||
auto EnumSize = EnumRecordTI->getSize();
|
||||
|
||||
auto Fields = EnumRecordTI->getFields();
|
||||
auto FieldCount = Fields.size();
|
||||
if (FieldCount == 0) {
|
||||
return false; // No fields?
|
||||
}
|
||||
if (FieldCount == 1) {
|
||||
*CaseIndex = 0; // Only possible field
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (EnumRecordTI->getRecordKind()) {
|
||||
|
||||
case RecordKind::NoPayloadEnum: {
|
||||
if (EnumSize == 0) {
|
||||
*CaseIndex = 0;
|
||||
return true;
|
||||
}
|
||||
return getReader().readInteger(EnumAddress, EnumSize, CaseIndex);
|
||||
}
|
||||
|
||||
case RecordKind::SinglePayloadEnum: {
|
||||
FieldInfo PayloadCase = Fields[0];
|
||||
if (!PayloadCase.TR)
|
||||
return false;
|
||||
unsigned long NonPayloadCaseCount = FieldCount - 1;
|
||||
unsigned long PayloadExtraInhabitants = PayloadCase.TI.getNumExtraInhabitants();
|
||||
unsigned Discriminator = 0;
|
||||
auto PayloadSize = PayloadCase.TI.getSize();
|
||||
if (NonPayloadCaseCount >= PayloadExtraInhabitants) {
|
||||
// There are more cases than inhabitants, we need a separate discriminator.
|
||||
auto TagInfo = getEnumTagCounts(PayloadSize, NonPayloadCaseCount, 1);
|
||||
auto TagSize = TagInfo.numTagBytes;
|
||||
auto TagAddress = RemoteAddress(EnumAddress.getAddressData() + PayloadSize);
|
||||
if (!getReader().readInteger(TagAddress, TagSize, &Discriminator)) {
|
||||
printf(">>>> readXI failed to read discriminator\n\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (PayloadSize == 0) {
|
||||
// Payload carries no information, so discriminator fully determines the case
|
||||
*CaseIndex = Discriminator;
|
||||
return true;
|
||||
} else if (Discriminator == 0) {
|
||||
// The payload area carries all the information...
|
||||
if (PayloadExtraInhabitants == 0) {
|
||||
*CaseIndex = 0;
|
||||
return true;
|
||||
}
|
||||
int XITag = 0;
|
||||
if (!PayloadCase.TI.readExtraInhabitantIndex(getReader(), EnumAddress, &XITag)) {
|
||||
return false;
|
||||
}
|
||||
if (XITag < 0) { // Valid (not extra) inhabitant
|
||||
*CaseIndex = 0; // Payload case is always #0
|
||||
return true;
|
||||
} else if ((unsigned)XITag <= NonPayloadCaseCount) {
|
||||
*CaseIndex = XITag + 1;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
// No payload: Payload area is reused for more cases
|
||||
uint32_t PayloadTag = 0;
|
||||
auto PayloadTagSize = std::min(PayloadSize, decltype(PayloadSize)(sizeof(PayloadTag)));
|
||||
if (!getReader().readInteger(EnumAddress, PayloadTagSize, &PayloadTag)) {
|
||||
return false;
|
||||
}
|
||||
auto XICases = 1U + PayloadExtraInhabitants; // Cases coded with XIs when discriminator = 0
|
||||
auto PayloadCases = 1U << (PayloadTagSize * 8U);
|
||||
*CaseIndex = XICases + (Discriminator - 1) * PayloadCases + PayloadTag;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
case RecordKind::MultiPayloadEnum: {
|
||||
// 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;
|
||||
}
|
||||
|
||||
default:
|
||||
// Unknown record kind.
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool getEnumCaseTypeRef(const TypeRef *EnumTR,
|
||||
unsigned CaseIndex,
|
||||
std::string &Name,
|
||||
const TypeRef **OutPayloadTR) {
|
||||
*OutPayloadTR = nullptr;
|
||||
|
||||
if (EnumTR == nullptr)
|
||||
return false;
|
||||
|
||||
auto EnumTI = getTypeInfo(EnumTR);
|
||||
if (EnumTI == nullptr)
|
||||
return false;
|
||||
|
||||
auto EnumRecordTI = dyn_cast<const RecordTypeInfo>(EnumTI);
|
||||
if (EnumRecordTI == nullptr)
|
||||
return false;
|
||||
|
||||
auto NumCases = EnumRecordTI->getNumFields();
|
||||
if (CaseIndex >= NumCases) {
|
||||
return false;
|
||||
} else {
|
||||
const auto Case = EnumRecordTI->getFields()[CaseIndex];
|
||||
Name = Case.Name;
|
||||
*OutPayloadTR = Case.TR;
|
||||
return true;
|
||||
}
|
||||
return EnumTI->projectEnumValue(getReader(), EnumAddress, CaseIndex);
|
||||
}
|
||||
|
||||
/// Return a description of the layout of a value with the given type.
|
||||
|
||||
Reference in New Issue
Block a user