Skip to content

Commit d969611

Browse files
authored
Merge pull request swiftlang#62614 from tbkka/tbkka-92945673-RemoteMirror-MPE
[RemoteMirror] Fix handling of zero-sized payloads and extra inhabitant calculations
2 parents 72d678a + 7c7ad18 commit d969611

File tree

5 files changed

+299
-37
lines changed

5 files changed

+299
-37
lines changed

include/swift/RemoteInspection/TypeLowering.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,17 @@ class EnumTypeInfo : public TypeInfo {
239239
return std::count_if(Cases.begin(), Cases.end(),
240240
[](const FieldInfo &Case){return Case.TR != 0;});
241241
}
242+
unsigned getNumNonEmptyPayloadCases() const {
243+
auto Cases = getCases();
244+
return std::count_if(Cases.begin(), Cases.end(),
245+
[](const FieldInfo &Case){
246+
// For our purposes here, assume any case
247+
// with invalid (missing) typeinfo is non-empty
248+
return Case.TR != 0
249+
&& (Case.TI.getKind() == TypeInfoKind::Invalid
250+
|| Case.TI.getSize() > 0);
251+
});
252+
}
242253
// Size of the payload area.
243254
unsigned getPayloadSize() const {
244255
return EnumTypeInfo::getPayloadSizeForCases(Cases);

stdlib/public/RemoteInspection/TypeLowering.cpp

Lines changed: 72 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -418,13 +418,13 @@ class EmptyEnumTypeInfo: public EnumTypeInfo {
418418
// Enum with a single non-payload case
419419
class TrivialEnumTypeInfo: public EnumTypeInfo {
420420
public:
421-
TrivialEnumTypeInfo(const std::vector<FieldInfo> &Cases)
421+
TrivialEnumTypeInfo(EnumKind Kind, const std::vector<FieldInfo> &Cases)
422422
: EnumTypeInfo(/*Size*/ 0,
423423
/* Alignment*/ 1,
424424
/*Stride*/ 1,
425425
/*NumExtraInhabitants*/ 0,
426426
/*BitwiseTakable*/ true,
427-
EnumKind::NoPayloadEnum, Cases) {}
427+
Kind, Cases) {}
428428

429429
bool readExtraInhabitantIndex(remote::MemoryReader &reader,
430430
remote::RemoteAddress address,
@@ -446,12 +446,13 @@ class NoPayloadEnumTypeInfo: public EnumTypeInfo {
446446
public:
447447
NoPayloadEnumTypeInfo(unsigned Size, unsigned Alignment,
448448
unsigned Stride, unsigned NumExtraInhabitants,
449+
EnumKind Kind,
449450
const std::vector<FieldInfo> &Cases)
450451
: EnumTypeInfo(Size, Alignment, Stride, NumExtraInhabitants,
451452
/*BitwiseTakable*/ true,
452-
EnumKind::NoPayloadEnum, Cases) {
453+
Kind, Cases) {
453454
assert(Cases.size() >= 2);
454-
assert(getNumPayloadCases() == 0);
455+
assert(getNumNonEmptyPayloadCases() == 0);
455456
}
456457

457458
bool readExtraInhabitantIndex(remote::MemoryReader &reader,
@@ -491,11 +492,12 @@ class SinglePayloadEnumTypeInfo: public EnumTypeInfo {
491492
SinglePayloadEnumTypeInfo(unsigned Size, unsigned Alignment,
492493
unsigned Stride, unsigned NumExtraInhabitants,
493494
bool BitwiseTakable,
495+
EnumKind Kind,
494496
const std::vector<FieldInfo> &Cases)
495497
: EnumTypeInfo(Size, Alignment, Stride, NumExtraInhabitants,
496-
BitwiseTakable, EnumKind::SinglePayloadEnum, Cases) {
498+
BitwiseTakable, Kind, Cases) {
497499
assert(Cases[0].TR != 0);
498-
assert(getNumPayloadCases() == 1);
500+
assert(getNumNonEmptyPayloadCases() == 1);
499501
}
500502

501503
bool readExtraInhabitantIndex(remote::MemoryReader &reader,
@@ -611,7 +613,7 @@ class SimpleMultiPayloadEnumTypeInfo: public EnumTypeInfo {
611613
BitwiseTakable, EnumKind::MultiPayloadEnum, Cases) {
612614
assert(Cases[0].TR != 0);
613615
assert(Cases[1].TR != 0);
614-
assert(getNumPayloadCases() > 1);
616+
assert(getNumNonEmptyPayloadCases() > 1);
615617
assert(getSize() > getPayloadSize());
616618
assert(getCases().size() > 1);
617619
}
@@ -986,7 +988,7 @@ class MultiPayloadEnumTypeInfo: public EnumTypeInfo {
986988
spareBitsMask(spareBitsMask) {
987989
assert(Cases[0].TR != 0);
988990
assert(Cases[1].TR != 0);
989-
assert(getNumPayloadCases() > 1);
991+
assert(getNumNonEmptyPayloadCases() > 1);
990992
}
991993

992994
bool readExtraInhabitantIndex(remote::MemoryReader &reader,
@@ -1043,7 +1045,7 @@ class MultiPayloadEnumTypeInfo: public EnumTypeInfo {
10431045

10441046
// Check whether this tag is used for valid content
10451047
auto payloadCases = getNumPayloadCases();
1046-
auto nonPayloadCases = getNumCases() - getNumPayloadCases();
1048+
auto nonPayloadCases = getNumCases() - payloadCases;
10471049
uint32_t inhabitedTags;
10481050
if (nonPayloadCases == 0) {
10491051
inhabitedTags = payloadCases;
@@ -2031,7 +2033,8 @@ class EnumTypeInfoBuilder {
20312033
const TypeInfo *build(const TypeRef *TR, RemoteRef<FieldDescriptor> FD,
20322034
remote::TypeInfoProvider *ExternalTypeInfo) {
20332035
// Sort enum into payload and no-payload cases.
2034-
unsigned NoPayloadCases = 0;
2036+
unsigned TrueNoPayloadCases = 0;
2037+
unsigned EmptyPayloadCases = 0;
20352038
std::vector<FieldTypeInfo> PayloadCases;
20362039

20372040
std::vector<FieldTypeInfo> Fields;
@@ -2042,41 +2045,77 @@ class EnumTypeInfoBuilder {
20422045

20432046
for (auto Case : Fields) {
20442047
if (Case.TR == nullptr) {
2045-
++NoPayloadCases;
2048+
++TrueNoPayloadCases;
20462049
addCase(Case.Name);
20472050
} else {
2048-
PayloadCases.push_back(Case);
20492051
auto *CaseTR = getCaseTypeRef(Case);
20502052
assert(CaseTR != nullptr);
20512053
auto *CaseTI = TC.getTypeInfo(CaseTR, ExternalTypeInfo);
2054+
if (CaseTI == nullptr) {
2055+
// We don't have typeinfo; assume it's not
2056+
// zero-sized to match earlier behavior.
2057+
// TODO: Maybe this should prompt us to fall
2058+
// back to UnsupportedEnumTypeInfo??
2059+
PayloadCases.push_back(Case);
2060+
} else if (CaseTI->getSize() == 0) {
2061+
// Zero-sized payloads get special treatment
2062+
++EmptyPayloadCases;
2063+
} else {
2064+
PayloadCases.push_back(Case);
2065+
}
20522066
addCase(Case.Name, CaseTR, CaseTI);
20532067
}
20542068
}
2069+
// For layout purposes, cases w/ empty payload are
2070+
// treated the same as cases with no payload.
2071+
unsigned EffectiveNoPayloadCases = TrueNoPayloadCases + EmptyPayloadCases;
20552072

20562073
if (Cases.empty()) {
20572074
return TC.makeTypeInfo<EmptyEnumTypeInfo>(Cases);
20582075
}
20592076

2077+
// `Kind` is used when dumping data, so it reflects how the enum was
2078+
// declared in source; the various *TypeInfo classes mentioned below reflect
2079+
// the in-memory layout, which may be different because cases whose
2080+
// payload is zero-sized get treated (for layout purposes) as non-payload
2081+
// cases.
2082+
EnumKind Kind;
2083+
switch (PayloadCases.size() + EmptyPayloadCases) {
2084+
case 0: Kind = EnumKind::NoPayloadEnum; break;
2085+
case 1: Kind = EnumKind::SinglePayloadEnum; break;
2086+
default: Kind = EnumKind::MultiPayloadEnum; break;
2087+
}
2088+
20602089
if (PayloadCases.empty()) {
20612090
// NoPayloadEnumImplStrategy
2062-
if (NoPayloadCases == 1) {
2063-
return TC.makeTypeInfo<TrivialEnumTypeInfo>(Cases);
2064-
} else if (NoPayloadCases < 256) {
2065-
return TC.makeTypeInfo<NoPayloadEnumTypeInfo>(
2066-
/* Size */ 1, /* Alignment */ 1, /* Stride */ 1,
2067-
/* NumExtraInhabitants */ 256 - NoPayloadCases, Cases);
2068-
} else if (NoPayloadCases < 65536) {
2069-
return TC.makeTypeInfo<NoPayloadEnumTypeInfo>(
2070-
/* Size */ 2, /* Alignment */ 2, /* Stride */ 2,
2071-
/* NumExtraInhabitants */ 65536 - NoPayloadCases, Cases);
2091+
if (EffectiveNoPayloadCases == 1) {
2092+
return TC.makeTypeInfo<TrivialEnumTypeInfo>(Kind, Cases);
20722093
} else {
2073-
auto extraInhabitants = std::numeric_limits<uint32_t>::max() - NoPayloadCases + 1;
2074-
if (extraInhabitants > ValueWitnessFlags::MaxNumExtraInhabitants) {
2075-
extraInhabitants = ValueWitnessFlags::MaxNumExtraInhabitants;
2094+
unsigned Size, NumExtraInhabitants;
2095+
if (EffectiveNoPayloadCases < 256) {
2096+
Size = 1;
2097+
NumExtraInhabitants = 256 - EffectiveNoPayloadCases;
2098+
} else if (EffectiveNoPayloadCases < 65536) {
2099+
Size = 2;
2100+
NumExtraInhabitants = 65536 - EffectiveNoPayloadCases;
2101+
} else {
2102+
Size = 4;
2103+
NumExtraInhabitants = std::numeric_limits<uint32_t>::max() - EffectiveNoPayloadCases + 1;
2104+
}
2105+
if (EmptyPayloadCases > 0) {
2106+
// This enum uses no-payload layout, but the source actually does
2107+
// have payloads (they're just all zero-sized).
2108+
// If this is really a single-payload enum, we take extra inhabitants
2109+
// from the first payload, which is zero sized in this case.
2110+
// If this is really a multi-payload enum, ...
2111+
NumExtraInhabitants = 0;
2112+
}
2113+
if (NumExtraInhabitants > ValueWitnessFlags::MaxNumExtraInhabitants) {
2114+
NumExtraInhabitants = ValueWitnessFlags::MaxNumExtraInhabitants;
20762115
}
20772116
return TC.makeTypeInfo<NoPayloadEnumTypeInfo>(
2078-
/* Size */ 4, /* Alignment */ 4, /* Stride */ 4,
2079-
/* NumExtraInhabitants */ extraInhabitants, Cases);
2117+
/* Size */ Size, /* Alignment */ Size, /* Stride */ Size,
2118+
NumExtraInhabitants, Kind, Cases);
20802119
}
20812120
} else if (PayloadCases.size() == 1) {
20822121
// SinglePayloadEnumImplStrategy
@@ -2087,26 +2126,26 @@ class EnumTypeInfoBuilder {
20872126
}
20882127
// An enum consisting of a single payload case and nothing else
20892128
// is lowered as the payload type.
2090-
if (NoPayloadCases == 0)
2129+
if (EffectiveNoPayloadCases == 0)
20912130
return CaseTI;
20922131
// Below logic should match the runtime function
20932132
// swift_initEnumMetadataSinglePayload().
20942133
auto PayloadExtraInhabitants = CaseTI->getNumExtraInhabitants();
2095-
if (PayloadExtraInhabitants >= NoPayloadCases) {
2134+
if (PayloadExtraInhabitants >= EffectiveNoPayloadCases) {
20962135
// Extra inhabitants can encode all no-payload cases.
2097-
NumExtraInhabitants = PayloadExtraInhabitants - NoPayloadCases;
2136+
NumExtraInhabitants = PayloadExtraInhabitants - EffectiveNoPayloadCases;
20982137
} else {
20992138
// Not enough extra inhabitants for all cases. We have to add an
21002139
// extra tag field.
21012140
NumExtraInhabitants = 0;
2102-
auto tagCounts = getEnumTagCounts(Size, NoPayloadCases,
2141+
auto tagCounts = getEnumTagCounts(Size, EffectiveNoPayloadCases,
21032142
/*payloadCases=*/1);
21042143
Size += tagCounts.numTagBytes;
21052144
Alignment = std::max(Alignment, tagCounts.numTagBytes);
21062145
}
21072146
unsigned Stride = ((Size + Alignment - 1) & ~(Alignment - 1));
21082147
return TC.makeTypeInfo<SinglePayloadEnumTypeInfo>(
2109-
Size, Alignment, Stride, NumExtraInhabitants, BitwiseTakable, Cases);
2148+
Size, Alignment, Stride, NumExtraInhabitants, BitwiseTakable, Kind, Cases);
21102149
} else {
21112150
// MultiPayloadEnumImplStrategy
21122151

@@ -2207,7 +2246,7 @@ class EnumTypeInfoBuilder {
22072246
} else {
22082247
// Dynamic multi-payload enums cannot use spare bits, so they
22092248
// always use a separate tag value:
2210-
auto tagCounts = getEnumTagCounts(Size, NoPayloadCases,
2249+
auto tagCounts = getEnumTagCounts(Size, EffectiveNoPayloadCases,
22112250
PayloadCases.size());
22122251
Size += tagCounts.numTagBytes;
22132252
// Dynamic multi-payload enums use the tag representations not assigned

test/Reflection/typeref_lowering.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1086,7 +1086,7 @@
10861086
// CHECK-64-NEXT: (case name=C index=2)
10871087
// CHECK-64-NEXT: (case name=D index=3)))
10881088
// CHECK-64-NEXT: (field name=sillyNoPayload offset=1
1089-
// CHECK-64-NEXT: (multi_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=252 bitwise_takable=1
1089+
// CHECK-64-NEXT: (multi_payload_enum size=1 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1
10901090
// CHECK-64-NEXT: (case name=A index=0 offset=0
10911091
// CHECK-64-NEXT: (no_payload_enum size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1))
10921092
// CHECK-64-NEXT: (case name=B index=1 offset=0

0 commit comments

Comments
 (0)