Skip to content

Commit 6c2b772

Browse files
authored
Merge pull request #74145 from tbkka/tbkka-remotemirror-mpe-fixes
Expand the work from #73491 to support more MPE layouts.
2 parents 11208ee + eba980a commit 6c2b772

File tree

4 files changed

+151
-51
lines changed

4 files changed

+151
-51
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

+56-50
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.ends_with("_")) {
257257
llvm::StringRef naturalRef = nameRef.drop_front(2).drop_back();
@@ -272,23 +272,36 @@ bool BuiltinTypeInfo::readExtraInhabitantIndex(
272272
*extraInhabitantIndex = -1;
273273
return true;
274274
}
275-
unsigned intSize = isIntType(Name);
276-
if (intSize > 0 && intSize < 64 && getSize() <= 8 && intSize < getSize() * 8) {
275+
// If it has extra inhabitants, it could be an integer type with extra
276+
// inhabitants (such as a bool) or a pointer.
277+
unsigned intSize = intTypeBitSize(Name);
278+
if (intSize > 0) {
279+
// This is an integer type
280+
281+
// If extra inhabitants are impossible, return early...
282+
// (assert in debug builds)
283+
assert(intSize < getSize() * 8
284+
&& "Standard-sized int cannot have extra inhabitants");
285+
if (intSize > 64 || getSize() > 8 || intSize >= getSize() * 8) {
286+
*extraInhabitantIndex = -1;
287+
return true;
288+
}
289+
290+
// Compute range of extra inhabitants
277291
uint64_t maxValidValue = (((uint64_t)1) << intSize) - 1;
278292
uint64_t maxAvailableValue = (((uint64_t)1) << (getSize() * 8)) - 1;
279293
uint64_t computedExtraInhabitants = maxAvailableValue - maxValidValue;
280294
if (computedExtraInhabitants > ValueWitnessFlags::MaxNumExtraInhabitants) {
281295
computedExtraInhabitants = ValueWitnessFlags::MaxNumExtraInhabitants;
282296
}
283297
assert(getNumExtraInhabitants() == computedExtraInhabitants &&
284-
"Unexpected number of extra inhabitants in an odd-sized integer");
298+
"Unexpected number of extra inhabitants in an odd-sized integer");
285299

300+
// Example: maxValidValue is 1 for a 1-bit bool, so any larger value
301+
// is an extra inhabitant.
286302
uint64_t rawValue;
287303
if (!reader.readInteger(address, getSize(), &rawValue))
288304
return false;
289-
290-
// Example: maxValidValue is 1 for a 1-bit bool, so any larger value
291-
// is an extra inhabitant.
292305
if (maxValidValue < rawValue) {
293306
*extraInhabitantIndex = rawValue - maxValidValue - 1;
294307
} else {
@@ -307,14 +320,15 @@ bool BuiltinTypeInfo::readExtraInhabitantIndex(
307320
}
308321

309322
BitMask BuiltinTypeInfo::getSpareBits(TypeConverter &TC, bool &hasAddrOnly) const {
310-
unsigned intSize = isIntType(Name);
323+
unsigned intSize = intTypeBitSize(Name);
311324
if (intSize > 0) {
312325
// Odd-sized integers export spare bits
313326
// In particular: bool fields are Int1 and export 7 spare bits
314327
auto mask = BitMask::oneMask(getSize());
315328
mask.keepOnlyMostSignificantBits(getSize() * 8 - intSize);
316329
return mask;
317330
} else if (
331+
Name == "ypXp" || // Any.Type
318332
Name == "yyXf" // 'yyXf' = @thin () -> Void function
319333
) {
320334
// Builtin types that expose pointer spare bits
@@ -578,7 +592,9 @@ class NoPayloadEnumTypeInfo: public EnumTypeInfo {
578592
}
579593

580594
BitMask getSpareBits(TypeConverter &TC, bool &hasAddrOnly) const override {
581-
return BitMask::zeroMask(getSize());
595+
auto mask = BitMask(getSize(), maskForCount(getNumCases()));
596+
mask.complement();
597+
return mask;
582598
}
583599

584600
bool projectEnumValue(remote::MemoryReader &reader,
@@ -648,7 +664,17 @@ class SinglePayloadEnumTypeInfo: public EnumTypeInfo {
648664
}
649665

650666
BitMask getSpareBits(TypeConverter &TC, bool &hasAddrOnly) const override {
651-
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;
652678
}
653679

654680
// Think of a single-payload enum as being encoded in "pages".
@@ -1961,6 +1987,15 @@ class EnumTypeInfoBuilder {
19611987
default: Kind = EnumKind::MultiPayloadEnum; break;
19621988
}
19631989

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+
19641999
if (Cases.size() == 1) {
19652000
if (EffectivePayloadCases == 0) {
19662001
// Zero-sized enum with only one empty case
@@ -2046,11 +2081,10 @@ class EnumTypeInfoBuilder {
20462081
//
20472082

20482083
// Do we have a fixed layout?
2049-
// TODO: Test whether a missing FixedDescriptor is actually relevant.
20502084
auto FixedDescriptor = TC.getBuilder().getBuiltinTypeDescriptor(TR);
20512085
if (!FixedDescriptor || GenericPayloadCases > 0) {
20522086
// This is a "dynamic multi-payload enum". For example,
2053-
// this occurs with:
2087+
// this occurs with generics such as:
20542088
// ```
20552089
// class ClassWithEnum<T> {
20562090
// enum E {
@@ -2060,6 +2094,12 @@ class EnumTypeInfoBuilder {
20602094
// var e: E?
20612095
// }
20622096
// ```
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+
// }
20632103
auto tagCounts = getEnumTagCounts(Size, EffectiveNoPayloadCases,
20642104
EffectivePayloadCases);
20652105
Size += tagCounts.numTagBytes;
@@ -2091,7 +2131,6 @@ class EnumTypeInfoBuilder {
20912131
unsigned Stride = ((Size + Alignment - 1) & ~(Alignment - 1));
20922132
if (Stride == 0)
20932133
Stride = 1;
2094-
auto PayloadSize = EnumTypeInfo::getPayloadSizeForCases(Cases);
20952134

20962135
// Compute the spare bit mask and determine if we have any address-only fields
20972136
auto localSpareBitMask = BitMask::oneMask(Size);
@@ -2103,44 +2142,11 @@ class EnumTypeInfoBuilder {
21032142
}
21042143
}
21052144

2106-
// See if we have MPE bit mask information from the compiler...
2107-
// TODO: drop this?
2108-
2109-
// Uncomment the following line to dump the MPE section every time we come through here...
2110-
//TC.getBuilder().dumpMultiPayloadEnumSection(std::cerr); // DEBUG helper
2111-
2112-
auto MPEDescriptor = TC.getBuilder().getMultiPayloadEnumDescriptor(TR);
2113-
if (MPEDescriptor && MPEDescriptor->usesPayloadSpareBits()) {
2114-
// We found compiler-provided spare bit data...
2115-
auto PayloadSpareBitMaskByteCount = MPEDescriptor->getPayloadSpareBitMaskByteCount();
2116-
auto PayloadSpareBitMaskByteOffset = MPEDescriptor->getPayloadSpareBitMaskByteOffset();
2117-
auto SpareBitMask = MPEDescriptor->getPayloadSpareBits();
2118-
BitMask compilerSpareBitMask(PayloadSize, SpareBitMask,
2119-
PayloadSpareBitMaskByteCount, PayloadSpareBitMaskByteOffset);
2120-
2121-
if (compilerSpareBitMask.isZero() || hasAddrOnly) {
2122-
// If there are no spare bits, use the "simple" tag-only implementation.
2123-
return TC.makeTypeInfo<TaggedMultiPayloadEnumTypeInfo>(
2124-
Size, Alignment, Stride, NumExtraInhabitants,
2125-
BitwiseTakable, Cases, EffectivePayloadCases);
2126-
}
2127-
2128-
#if 0 // TODO: This should be !defined(NDEBUG)
2129-
// Verify that compiler provided and local spare bit info agree...
2130-
// TODO: If we could make this actually work, then we wouldn't need the
2131-
// bulky compiler-provided info, would we?
2132-
assert(localSpareBitMask == compilerSpareBitMask);
2133-
#endif
2134-
2135-
// Use compiler-provided spare bit information
2136-
return TC.makeTypeInfo<MultiPayloadEnumTypeInfo>(
2137-
Size, Alignment, Stride, NumExtraInhabitants,
2138-
BitwiseTakable, Cases, compilerSpareBitMask,
2139-
EffectivePayloadCases);
2140-
}
2141-
21422145
if (localSpareBitMask.isZero() || hasAddrOnly) {
2143-
// 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.
21442150
return TC.makeTypeInfo<TaggedMultiPayloadEnumTypeInfo>(
21452151
Size, Alignment, Stride, NumExtraInhabitants,
21462152
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)