Skip to content

Commit c05ce11

Browse files
tbkkaktoso
authored andcommitted
Merge pull request swiftlang#74145 from tbkka/tbkka-remotemirror-mpe-fixes
Expand the work from swiftlang#73491 to support more MPE layouts. This is also switches the MPE layout code to exclusively use the new approach. The key observation: existing reflection metadata seems to already provide enough information in all cases, so we can abandon an earlier effort to add spare bitmask data. There is some risk that the old code may have handled some enums that aren't (yet) fully supported by the new code. However, I did a bunch of experiments and found that our existing test cases have decent coverage of the major capabilities and I've also added a new test case specifically to exercise enum layouts being accessed resiliently. So if there are regressions from this change, they should be minor and easily fixed. Resolves rdar://129281368
1 parent fbfcaa0 commit c05ce11

File tree

4 files changed

+135
-47
lines changed

4 files changed

+135
-47
lines changed

include/swift/RemoteInspection/BitMask.h

+10-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,16 @@ class BitMask {
7474

7575
BitMask(unsigned sizeInBytes, uint64_t sourceMask): size(sizeInBytes) {
7676
mask = (uint8_t *)calloc(1, sizeInBytes);
77-
memcpy(mask, &sourceMask, sizeInBytes);
77+
if (!mask) {
78+
assert(false && "Failed to allocate Bitmask");
79+
size = 0;
80+
return;
81+
}
82+
size_t toCopy = sizeInBytes;
83+
if (toCopy > sizeof(sourceMask)) {
84+
toCopy = sizeof(sourceMask);
85+
}
86+
memcpy(mask, &sourceMask, toCopy);
7887
}
7988

8089
// Construct a bitmask of the appropriate number of bytes

stdlib/public/RemoteInspection/TypeLowering.cpp

+40-46
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ BuiltinTypeInfo::BuiltinTypeInfo(unsigned Size, unsigned Alignment,
251251

252252
// Builtin.Int<N> is mangled as 'Bi' N '_'
253253
// Returns 0 if this isn't an Int
254-
static unsigned isIntType(std::string name) {
254+
static unsigned intTypeBitSize(std::string name) {
255255
llvm::StringRef nameRef(name);
256256
if (nameRef.starts_with("Bi") && nameRef.endswith("_")) {
257257
llvm::StringRef naturalRef = nameRef.drop_front(2).drop_back();
@@ -274,11 +274,12 @@ bool BuiltinTypeInfo::readExtraInhabitantIndex(
274274
}
275275
// If it has extra inhabitants, it could be an integer type with extra
276276
// inhabitants (such as a bool) or a pointer.
277-
unsigned intSize = isIntType(Name);
277+
unsigned intSize = intTypeBitSize(Name);
278278
if (intSize > 0) {
279279
// This is an integer type
280280

281-
// If it cannot have extra inhabitants, return early...
281+
// If extra inhabitants are impossible, return early...
282+
// (assert in debug builds)
282283
assert(intSize < getSize() * 8
283284
&& "Standard-sized int cannot have extra inhabitants");
284285
if (intSize > 64 || getSize() > 8 || intSize >= getSize() * 8) {
@@ -319,14 +320,15 @@ bool BuiltinTypeInfo::readExtraInhabitantIndex(
319320
}
320321

321322
BitMask BuiltinTypeInfo::getSpareBits(TypeConverter &TC, bool &hasAddrOnly) const {
322-
unsigned intSize = isIntType(Name);
323+
unsigned intSize = intTypeBitSize(Name);
323324
if (intSize > 0) {
324325
// Odd-sized integers export spare bits
325326
// In particular: bool fields are Int1 and export 7 spare bits
326327
auto mask = BitMask::oneMask(getSize());
327328
mask.keepOnlyMostSignificantBits(getSize() * 8 - intSize);
328329
return mask;
329330
} else if (
331+
Name == "ypXp" || // Any.Type
330332
Name == "yyXf" // 'yyXf' = @thin () -> Void function
331333
) {
332334
// Builtin types that expose pointer spare bits
@@ -590,7 +592,9 @@ class NoPayloadEnumTypeInfo: public EnumTypeInfo {
590592
}
591593

592594
BitMask getSpareBits(TypeConverter &TC, bool &hasAddrOnly) const override {
593-
return BitMask::zeroMask(getSize());
595+
auto mask = BitMask(getSize(), maskForCount(getNumCases()));
596+
mask.complement();
597+
return mask;
594598
}
595599

596600
bool projectEnumValue(remote::MemoryReader &reader,
@@ -660,7 +664,17 @@ class SinglePayloadEnumTypeInfo: public EnumTypeInfo {
660664
}
661665

662666
BitMask getSpareBits(TypeConverter &TC, bool &hasAddrOnly) const override {
663-
return BitMask::zeroMask(getSize());
667+
FieldInfo PayloadCase = getCases()[0];
668+
size_t payloadSize = PayloadCase.TI.getSize();
669+
if (getSize() <= payloadSize) {
670+
return BitMask::zeroMask(getSize());
671+
}
672+
size_t tagSize = getSize() - payloadSize;
673+
auto mask = BitMask::oneMask(getSize());
674+
mask.keepOnlyMostSignificantBits(tagSize * 8); // Clear payload bits
675+
auto tagMaskUsedBits = BitMask(getSize(), maskForCount(getNumCases()));
676+
mask.andNotMask(tagMaskUsedBits, payloadSize); // Clear used tag bits
677+
return mask;
664678
}
665679

666680
// Think of a single-payload enum as being encoded in "pages".
@@ -1973,6 +1987,15 @@ class EnumTypeInfoBuilder {
19731987
default: Kind = EnumKind::MultiPayloadEnum; break;
19741988
}
19751989

1990+
// Sanity: Ignore any enum that claims to have a size more than 1MiB
1991+
// This avoids allocating lots of memory for spare bit mask calculations
1992+
// when clients try to interpret random chunks of memory as type descriptions.
1993+
if (Size > (1024ULL * 1024)) {
1994+
unsigned Stride = ((Size + Alignment - 1) & ~(Alignment - 1));
1995+
return TC.makeTypeInfo<UnsupportedEnumTypeInfo>(
1996+
Size, Alignment, Stride, NumExtraInhabitants, BitwiseTakable, Kind, Cases);
1997+
}
1998+
19761999
if (Cases.size() == 1) {
19772000
if (EffectivePayloadCases == 0) {
19782001
// Zero-sized enum with only one empty case
@@ -2058,11 +2081,10 @@ class EnumTypeInfoBuilder {
20582081
//
20592082

20602083
// Do we have a fixed layout?
2061-
// TODO: Test whether a missing FixedDescriptor is actually relevant.
20622084
auto FixedDescriptor = TC.getBuilder().getBuiltinTypeDescriptor(TR);
20632085
if (!FixedDescriptor || GenericPayloadCases > 0) {
20642086
// This is a "dynamic multi-payload enum". For example,
2065-
// this occurs with:
2087+
// this occurs with generics such as:
20662088
// ```
20672089
// class ClassWithEnum<T> {
20682090
// enum E {
@@ -2072,6 +2094,12 @@ class EnumTypeInfoBuilder {
20722094
// var e: E?
20732095
// }
20742096
// ```
2097+
// and when we have a resilient inner enum, such as:
2098+
// ```
2099+
// enum E2 {
2100+
// case y(E1_resilient)
2101+
// case z(Int)
2102+
// }
20752103
auto tagCounts = getEnumTagCounts(Size, EffectiveNoPayloadCases,
20762104
EffectivePayloadCases);
20772105
Size += tagCounts.numTagBytes;
@@ -2103,7 +2131,6 @@ class EnumTypeInfoBuilder {
21032131
unsigned Stride = ((Size + Alignment - 1) & ~(Alignment - 1));
21042132
if (Stride == 0)
21052133
Stride = 1;
2106-
auto PayloadSize = EnumTypeInfo::getPayloadSizeForCases(Cases);
21072134

21082135
// Compute the spare bit mask and determine if we have any address-only fields
21092136
auto localSpareBitMask = BitMask::oneMask(Size);
@@ -2115,44 +2142,11 @@ class EnumTypeInfoBuilder {
21152142
}
21162143
}
21172144

2118-
// See if we have MPE bit mask information from the compiler...
2119-
// TODO: drop this?
2120-
2121-
// Uncomment the following line to dump the MPE section every time we come through here...
2122-
//TC.getBuilder().dumpMultiPayloadEnumSection(std::cerr); // DEBUG helper
2123-
2124-
auto MPEDescriptor = TC.getBuilder().getMultiPayloadEnumDescriptor(TR);
2125-
if (MPEDescriptor && MPEDescriptor->usesPayloadSpareBits()) {
2126-
// We found compiler-provided spare bit data...
2127-
auto PayloadSpareBitMaskByteCount = MPEDescriptor->getPayloadSpareBitMaskByteCount();
2128-
auto PayloadSpareBitMaskByteOffset = MPEDescriptor->getPayloadSpareBitMaskByteOffset();
2129-
auto SpareBitMask = MPEDescriptor->getPayloadSpareBits();
2130-
BitMask compilerSpareBitMask(PayloadSize, SpareBitMask,
2131-
PayloadSpareBitMaskByteCount, PayloadSpareBitMaskByteOffset);
2132-
2133-
if (compilerSpareBitMask.isZero() || hasAddrOnly) {
2134-
// If there are no spare bits, use the "simple" tag-only implementation.
2135-
return TC.makeTypeInfo<TaggedMultiPayloadEnumTypeInfo>(
2136-
Size, Alignment, Stride, NumExtraInhabitants,
2137-
BitwiseTakable, Cases, EffectivePayloadCases);
2138-
}
2139-
2140-
#if 0 // TODO: This should be !defined(NDEBUG)
2141-
// Verify that compiler provided and local spare bit info agree...
2142-
// TODO: If we could make this actually work, then we wouldn't need the
2143-
// bulky compiler-provided info, would we?
2144-
assert(localSpareBitMask == compilerSpareBitMask);
2145-
#endif
2146-
2147-
// Use compiler-provided spare bit information
2148-
return TC.makeTypeInfo<MultiPayloadEnumTypeInfo>(
2149-
Size, Alignment, Stride, NumExtraInhabitants,
2150-
BitwiseTakable, Cases, compilerSpareBitMask,
2151-
EffectivePayloadCases);
2152-
}
2153-
21542145
if (localSpareBitMask.isZero() || hasAddrOnly) {
2155-
// Simple case that does not use spare bits
2146+
// Simple tag-only layout does not use spare bits.
2147+
// Either:
2148+
// * There are no spare bits, or
2149+
// * We can't copy it to strip spare bits.
21562150
return TC.makeTypeInfo<TaggedMultiPayloadEnumTypeInfo>(
21572151
Size, Alignment, Stride, NumExtraInhabitants,
21582152
BitwiseTakable, Cases, EffectivePayloadCases);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
2+
public enum E1_resilient {
3+
case a
4+
case b
5+
}
6+
7+
public enum E2_resilient {
8+
case c(E1_resilient)
9+
case d(E1_resilient)
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// RUN: %empty-directory(%t)
2+
3+
// RUN: %target-build-swift-dylib(%t/%target-library-name(resilient_enums)) -enable-library-evolution %S/Inputs/reflect_Enum_values_resilient_enums.swift -emit-module -emit-module-path %t/resilient_enums.swiftmodule -module-name resilient_enums
4+
// RUN: %target-codesign %t/%target-library-name(resilient_enums)
5+
6+
// RUN: %target-build-swift -lswiftSwiftReflectionTest %s -L %t -I %t -lresilient_enums -o %t/reflect_Enum_values_resilient %target-rpath(%t)
7+
// RUN: %target-codesign %t/reflect_Enum_values_resilient
8+
9+
// RUN: %target-run %target-swift-reflection-test %t/reflect_Enum_values_resilient | tee /dev/stderr | %FileCheck %s --dump-input=fail
10+
11+
// REQUIRES: executable_test
12+
// REQUIRES: objc_interop, OS=macosx
13+
// UNSUPPORTED: use_os_stdlib
14+
15+
import SwiftReflectionTest
16+
17+
import resilient_enums
18+
19+
// Non-resilient enum wrapping a resilient enum
20+
// This doesn't use spare bits of the inner enum
21+
enum E2 {
22+
case y(E1_resilient)
23+
case z(E1_resilient)
24+
}
25+
26+
// Contrast:
27+
// E2_resilient is a resilient enum wrapping a resilient enum
28+
// This does use spare bits of the inner enum
29+
30+
31+
// CHECK: Reflecting an enum value.
32+
// CHECK-NEXT: Type reference:
33+
// CHECK-NEXT: (enum reflect_Enum_values_resilient.E2)
34+
// CHECK-NEXT: Value: .y(.a)
35+
36+
reflect(enumValue: E2.y(.a))
37+
38+
// CHECK: Reflecting an enum value.
39+
// CHECK-NEXT: Type reference:
40+
// CHECK-NEXT: (enum reflect_Enum_values_resilient.E2)
41+
// CHECK-NEXT: Value: .z(.b)
42+
43+
reflect(enumValue: E2.z(.b))
44+
45+
// CHECK: Reflecting an enum value.
46+
// CHECK-NEXT: Type reference:
47+
// CHECK-NEXT: (enum resilient_enums.E1_resilient)
48+
// CHECK-NEXT: Value: .a
49+
50+
reflect(enumValue: E1_resilient.a)
51+
52+
// CHECK: Reflecting an enum value.
53+
// CHECK-NEXT: Type reference:
54+
// CHECK-NEXT: (enum resilient_enums.E1_resilient)
55+
// CHECK-NEXT: Value: .b
56+
57+
reflect(enumValue: E1_resilient.b)
58+
59+
// CHECK: Reflecting an enum value.
60+
// CHECK-NEXT: Type reference:
61+
// CHECK-NEXT: (enum resilient_enums.E2_resilient)
62+
// CHECK-NEXT: Value: .c(.a)
63+
64+
reflect(enumValue: E2_resilient.c(.a))
65+
66+
// CHECK: Reflecting an enum value.
67+
// CHECK-NEXT: Type reference:
68+
// CHECK-NEXT: (enum resilient_enums.E2_resilient)
69+
// CHECK-NEXT: Value: .d(.b)
70+
71+
reflect(enumValue: E2_resilient.d(.b))
72+
73+
doneReflecting()
74+
75+
// CHECK: Done.

0 commit comments

Comments
 (0)