Skip to content

Commit edc9bb8

Browse files
authored
Merge pull request #74194 from tbkka/tbkka-remotemirror-mpe-fixes-6.0
[6.0] Use direct spare bit calculation for more MPE layouts
2 parents c129356 + 79181a2 commit edc9bb8

File tree

4 files changed

+135
-47
lines changed

4 files changed

+135
-47
lines changed

include/swift/RemoteInspection/BitMask.h

Lines changed: 10 additions & 1 deletion
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

Lines changed: 40 additions & 46 deletions
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);
Lines changed: 10 additions & 0 deletions
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+
}
Lines changed: 75 additions & 0 deletions
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)