Skip to content

Commit f087b5e

Browse files
committed
This PR aligns RemoteMirror more closely with the compiler's handling of enums
that contain zero-sized payloads, which should resolve a number of issues with RemoteMirror incorrectly reflecting enum cases and/or measuring the layout of structures containing enums. Background: Enum cases that have zero-sized payloads are handled differently from other payload-bearing cases. 1. For layout purposes, they're treated as non-payload cases. This can cause an MPE to actually get represented in memory as a single-payload or non-payload enum. 2. However, zero-sized payloads are still considered for extra inhabitant calculations. Since they have no extra inhabitants, this tends to cause such enums to also not expose extra inhabitants to containing enums. This commit makes several change to how RemoteMirror determines enum layout: * The various "*EnumTypeInfo" classes now represent layout mechanisms -- as described in (1) above, this can differ from the source code concept. * An Enum "kind" is separately computed to reflect the source code concept; this ensures that the dumped type information reflects the source code. * For single-payload and no-payload _layouts_, the extra inhabitant calculation has been adjusted to ensure that zero-sized payloads are correctly considered. Resolves: rdar://92945673
1 parent 9bbacdf commit f087b5e

File tree

5 files changed

+290
-37
lines changed

5 files changed

+290
-37
lines changed

include/swift/RemoteInspection/TypeLowering.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,14 @@ 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+
return Case.TR != 0
247+
&& Case.TI.getSize() != 0;
248+
});
249+
}
242250
// Size of the payload area.
243251
unsigned getPayloadSize() const {
244252
return EnumTypeInfo::getPayloadSizeForCases(Cases);

stdlib/public/RemoteInspection/TypeLowering.cpp

Lines changed: 66 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,71 @@ 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->getSize() == 0) {
2055+
// Zero-sized payloads get special treatment
2056+
++EmptyPayloadCases;
2057+
} else {
2058+
PayloadCases.push_back(Case);
2059+
}
20522060
addCase(Case.Name, CaseTR, CaseTI);
20532061
}
20542062
}
2063+
// For layout purposes, cases w/ empty payload are
2064+
// treated the same as cases with no payload.
2065+
unsigned EffectiveNoPayloadCases = TrueNoPayloadCases + EmptyPayloadCases;
20552066

20562067
if (Cases.empty()) {
20572068
return TC.makeTypeInfo<EmptyEnumTypeInfo>(Cases);
20582069
}
20592070

2071+
// `Kind` is used when dumping data, so it reflects how the enum was
2072+
// declared in source; the various *TypeInfo classes mentioned below reflect
2073+
// the in-memory layout, which may be different because cases whose
2074+
// payload is zero-sized get treated (for layout purposes) as non-payload
2075+
// cases.
2076+
EnumKind Kind;
2077+
switch (PayloadCases.size() + EmptyPayloadCases) {
2078+
case 0: Kind = EnumKind::NoPayloadEnum; break;
2079+
case 1: Kind = EnumKind::SinglePayloadEnum; break;
2080+
default: Kind = EnumKind::MultiPayloadEnum; break;
2081+
}
2082+
20602083
if (PayloadCases.empty()) {
20612084
// 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);
2085+
if (EffectiveNoPayloadCases == 1) {
2086+
return TC.makeTypeInfo<TrivialEnumTypeInfo>(Kind, Cases);
20722087
} else {
2073-
auto extraInhabitants = std::numeric_limits<uint32_t>::max() - NoPayloadCases + 1;
2074-
if (extraInhabitants > ValueWitnessFlags::MaxNumExtraInhabitants) {
2075-
extraInhabitants = ValueWitnessFlags::MaxNumExtraInhabitants;
2088+
unsigned Size, NumExtraInhabitants;
2089+
if (EffectiveNoPayloadCases < 256) {
2090+
Size = 1;
2091+
NumExtraInhabitants = 256 - EffectiveNoPayloadCases;
2092+
} else if (EffectiveNoPayloadCases < 65536) {
2093+
Size = 2;
2094+
NumExtraInhabitants = 65536 - EffectiveNoPayloadCases;
2095+
} else {
2096+
Size = 4;
2097+
NumExtraInhabitants = std::numeric_limits<uint32_t>::max() - EffectiveNoPayloadCases + 1;
2098+
}
2099+
if (EmptyPayloadCases > 0) {
2100+
// This enum uses no-payload layout, but the source actually does
2101+
// have payloads (they're just all zero-sized).
2102+
// If this is really a single-payload enum, we take extra inhabitants
2103+
// from the first payload, which is zero sized in this case.
2104+
// If this is really a multi-payload enum, ...
2105+
NumExtraInhabitants = 0;
2106+
}
2107+
if (NumExtraInhabitants > ValueWitnessFlags::MaxNumExtraInhabitants) {
2108+
NumExtraInhabitants = ValueWitnessFlags::MaxNumExtraInhabitants;
20762109
}
20772110
return TC.makeTypeInfo<NoPayloadEnumTypeInfo>(
2078-
/* Size */ 4, /* Alignment */ 4, /* Stride */ 4,
2079-
/* NumExtraInhabitants */ extraInhabitants, Cases);
2111+
/* Size */ Size, /* Alignment */ Size, /* Stride */ Size,
2112+
NumExtraInhabitants, Kind, Cases);
20802113
}
20812114
} else if (PayloadCases.size() == 1) {
20822115
// SinglePayloadEnumImplStrategy
@@ -2087,26 +2120,26 @@ class EnumTypeInfoBuilder {
20872120
}
20882121
// An enum consisting of a single payload case and nothing else
20892122
// is lowered as the payload type.
2090-
if (NoPayloadCases == 0)
2123+
if (EffectiveNoPayloadCases == 0)
20912124
return CaseTI;
20922125
// Below logic should match the runtime function
20932126
// swift_initEnumMetadataSinglePayload().
20942127
auto PayloadExtraInhabitants = CaseTI->getNumExtraInhabitants();
2095-
if (PayloadExtraInhabitants >= NoPayloadCases) {
2128+
if (PayloadExtraInhabitants >= EffectiveNoPayloadCases) {
20962129
// Extra inhabitants can encode all no-payload cases.
2097-
NumExtraInhabitants = PayloadExtraInhabitants - NoPayloadCases;
2130+
NumExtraInhabitants = PayloadExtraInhabitants - EffectiveNoPayloadCases;
20982131
} else {
20992132
// Not enough extra inhabitants for all cases. We have to add an
21002133
// extra tag field.
21012134
NumExtraInhabitants = 0;
2102-
auto tagCounts = getEnumTagCounts(Size, NoPayloadCases,
2135+
auto tagCounts = getEnumTagCounts(Size, EffectiveNoPayloadCases,
21032136
/*payloadCases=*/1);
21042137
Size += tagCounts.numTagBytes;
21052138
Alignment = std::max(Alignment, tagCounts.numTagBytes);
21062139
}
21072140
unsigned Stride = ((Size + Alignment - 1) & ~(Alignment - 1));
21082141
return TC.makeTypeInfo<SinglePayloadEnumTypeInfo>(
2109-
Size, Alignment, Stride, NumExtraInhabitants, BitwiseTakable, Cases);
2142+
Size, Alignment, Stride, NumExtraInhabitants, BitwiseTakable, Kind, Cases);
21102143
} else {
21112144
// MultiPayloadEnumImplStrategy
21122145

@@ -2207,7 +2240,7 @@ class EnumTypeInfoBuilder {
22072240
} else {
22082241
// Dynamic multi-payload enums cannot use spare bits, so they
22092242
// always use a separate tag value:
2210-
auto tagCounts = getEnumTagCounts(Size, NoPayloadCases,
2243+
auto tagCounts = getEnumTagCounts(Size, EffectiveNoPayloadCases,
22112244
PayloadCases.size());
22122245
Size += tagCounts.numTagBytes;
22132246
// 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)