Skip to content

Commit 6e7521a

Browse files
authored
Merge pull request #29037 from apple/zero-builtin-type-descriptor-workaround
IRGen: Work around RemoteMirror bug generating reflection info for empty builtin types.
2 parents 49b49f3 + 509735e commit 6e7521a

File tree

3 files changed

+148
-5
lines changed

3 files changed

+148
-5
lines changed

lib/IRGen/GenReflection.cpp

Lines changed: 71 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -671,7 +671,10 @@ class AssociatedTypeMetadataBuilder : public ReflectionMetadataBuilder {
671671
};
672672

673673
class FieldTypeMetadataBuilder : public ReflectionMetadataBuilder {
674-
const uint32_t fieldRecordSize = 12;
674+
public:
675+
static const uint32_t FieldRecordSize = 12;
676+
677+
private:
675678
const NominalTypeDecl *NTD;
676679

677680
void addFieldDecl(const ValueDecl *value, Type type,
@@ -714,7 +717,7 @@ class FieldTypeMetadataBuilder : public ReflectionMetadataBuilder {
714717
}
715718

716719
B.addInt16(uint16_t(kind));
717-
B.addInt16(fieldRecordSize);
720+
B.addInt16(FieldRecordSize);
718721

719722
auto properties = NTD->getStoredProperties();
720723
B.addInt32(properties.size());
@@ -737,7 +740,7 @@ class FieldTypeMetadataBuilder : public ReflectionMetadataBuilder {
737740
}
738741

739742
B.addInt16(uint16_t(kind));
740-
B.addInt16(fieldRecordSize);
743+
B.addInt16(FieldRecordSize);
741744
B.addInt32(strategy.getElementsWithPayload().size()
742745
+ strategy.getElementsWithNoPayload().size());
743746

@@ -764,7 +767,7 @@ class FieldTypeMetadataBuilder : public ReflectionMetadataBuilder {
764767
else
765768
Kind = FieldDescriptorKind::Protocol;
766769
B.addInt16(uint16_t(Kind));
767-
B.addInt16(fieldRecordSize);
770+
B.addInt16(FieldRecordSize);
768771
B.addInt32(0);
769772
}
770773

@@ -825,6 +828,57 @@ class FieldTypeMetadataBuilder : public ReflectionMetadataBuilder {
825828
}
826829
};
827830

831+
static bool
832+
deploymentTargetHasRemoteMirrorZeroSizedTypeDescriptorBug(IRGenModule &IGM) {
833+
auto target = IGM.Context.LangOpts.Target;
834+
835+
if (target.isMacOSX() && target.isMacOSXVersionLT(10, 16, 0)) {
836+
return true;
837+
}
838+
if (target.isiOS() && target.isOSVersionLT(14)) { // includes tvOS
839+
return true;
840+
}
841+
if (target.isWatchOS() && target.isOSVersionLT(7)) {
842+
return true;
843+
}
844+
845+
return false;
846+
}
847+
848+
/// Metadata builder that emits a fixed-layout empty type as an empty struct, as
849+
/// a workaround for a RemoteMirror crash in older OSes.
850+
class EmptyStructMetadataBuilder : public ReflectionMetadataBuilder {
851+
const NominalTypeDecl *NTD;
852+
853+
void layout() override {
854+
addNominalRef(NTD);
855+
B.addInt32(0);
856+
B.addInt16(uint16_t(FieldDescriptorKind::Struct));
857+
B.addInt16(FieldTypeMetadataBuilder::FieldRecordSize);
858+
B.addInt32(0);
859+
}
860+
861+
public:
862+
EmptyStructMetadataBuilder(IRGenModule &IGM,
863+
const NominalTypeDecl *NTD)
864+
: ReflectionMetadataBuilder(IGM), NTD(NTD) {
865+
assert(IGM.getTypeInfoForUnlowered(
866+
NTD->getDeclaredTypeInContext()->getCanonicalType())
867+
.isKnownEmpty(ResilienceExpansion::Maximal)
868+
&& "should only be used for known empty types");
869+
}
870+
871+
llvm::GlobalVariable *emit() {
872+
auto section = IGM.getFieldTypeMetadataSectionName();
873+
return ReflectionMetadataBuilder::emit(
874+
[&](IRGenModule &IGM, ConstantInit definition) -> llvm::Constant* {
875+
return IGM.getAddrOfReflectionFieldDescriptor(
876+
NTD->getDeclaredType()->getCanonicalType(), definition);
877+
},
878+
section);
879+
}
880+
};
881+
828882
class FixedTypeMetadataBuilder : public ReflectionMetadataBuilder {
829883
ModuleDecl *module;
830884
CanType type;
@@ -1338,6 +1392,19 @@ void IRGenModule::emitFieldDescriptor(const NominalTypeDecl *D) {
13381392
}
13391393

13401394
if (needsOpaqueDescriptor) {
1395+
// Work around an issue in the RemoteMirror library that ships in
1396+
// macOS 10.15/iOS 13 and earlier that causes it to crash on a
1397+
// BuiltinTypeDescriptor with zero size. If the type has zero size, emit it
1398+
// as an empty struct instead, which will have the same impact on the
1399+
// encoded type layout.
1400+
auto &TI = getTypeInfoForUnlowered(T);
1401+
if (deploymentTargetHasRemoteMirrorZeroSizedTypeDescriptorBug(*this)
1402+
&& TI.isKnownEmpty(ResilienceExpansion::Maximal)) {
1403+
EmptyStructMetadataBuilder builder(*this, D);
1404+
builder.emit();
1405+
return;
1406+
}
1407+
13411408
FixedTypeMetadataBuilder builder(*this, D);
13421409
builder.emit();
13431410
}

validation-test/Reflection/reflect_empty_struct.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
// RUN: %empty-directory(%t)
2-
// RUN: %target-build-swift -lswiftSwiftReflectionTest -I %S/Inputs/EmptyStruct/ %s -o %t/reflect_empty_struct
2+
// RUN: %target-build-swift -lswiftSwiftReflectionTest -I %S/Inputs/EmptyStruct/ %s -o %t/reflect_empty_struct -target x86_64-apple-macosx10.99.0
33
// RUN: %target-codesign %t/reflect_empty_struct
44

55
// RUN: %target-run %target-swift-reflection-test %t/reflect_empty_struct | %FileCheck %s --check-prefix=CHECK-%target-ptrsize --dump-input fail
66

77
// REQUIRES: objc_interop
88
// REQUIRES: executable_test
9+
// REQUIRES: OS=macosx
910

1011
import SwiftReflectionTest
1112

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 -lswiftSwiftReflectionTest -I %S/Inputs/EmptyStruct/ %s -o %t/reflect_empty_struct -target x86_64-apple-macosx10.15.0
4+
// RUN: %target-codesign %t/reflect_empty_struct
5+
// RUN: %target-run %target-swift-reflection-test %t/reflect_empty_struct | %FileCheck %s --check-prefix=CHECK-%target-ptrsize --dump-input fail
6+
7+
// RUN: %target-build-swift -lswiftSwiftReflectionTest -I %S/Inputs/EmptyStruct/ %s -o %t/reflect_empty_struct -target x86_64-apple-macosx10.14.0
8+
// RUN: %target-codesign %t/reflect_empty_struct
9+
// RUN: %target-run %target-swift-reflection-test %t/reflect_empty_struct | %FileCheck %s --check-prefix=CHECK-%target-ptrsize --dump-input fail
10+
11+
// REQUIRES: objc_interop
12+
// REQUIRES: executable_test
13+
// REQUIRES: OS=macosx
14+
15+
import SwiftReflectionTest
16+
17+
import EmptyStruct
18+
19+
@_alignment(1) struct EmptyStruct { }
20+
class Class {
21+
var a = EmptyStruct()
22+
var b: Any = EmptyStruct()
23+
var c = EmptyStructC()
24+
var d: Any = EmptyStructC()
25+
}
26+
27+
var obj = Class()
28+
29+
reflect(object: obj)
30+
31+
// CHECK-64: Reflecting an object.
32+
// CHECK-64: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}}
33+
// CHECK-64: Type reference:
34+
// CHECK-64: (class reflect_empty_struct.Class)
35+
36+
// CHECK-64: Type info:
37+
// CHECK-64: (class_instance size=80 alignment=8 stride=80 num_extra_inhabitants=0 bitwise_takable=1
38+
// CHECK-64: (field name=a offset=16
39+
// CHECK-64: (struct size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1))
40+
// CHECK-64: (field name=b offset=16
41+
// CHECK-64: (opaque_existential size=32 alignment=8 stride=32 num_extra_inhabitants=2147483647 bitwise_takable=1
42+
// CHECK-64: (field name=metadata offset=24
43+
// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=2147483647 bitwise_takable=1))))
44+
// CHECK-64: (field name=c offset=48
45+
// CHECK-64: (struct size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1))
46+
// CHECK-64: (field name=d offset=48
47+
// CHECK-64: (opaque_existential size=32 alignment=8 stride=32 num_extra_inhabitants=2147483647 bitwise_takable=1
48+
// CHECK-64: (field name=metadata offset=24
49+
// CHECK-64: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=2147483647 bitwise_takable=1)))))
50+
51+
// CHECK-32: Reflecting an object.
52+
// CHECK-32: Instance pointer in child address space: 0x{{[0-9a-fA-F]+}}
53+
// CHECK-32: Type reference:
54+
// CHECK-32: (class reflect_empty_struct.Class)
55+
56+
// CHECK-32: Type info:
57+
// CHECK-32: (class_instance size=40 alignment=4 stride=40 num_extra_inhabitants=0 bitwise_takable=1
58+
// CHECK-32: (field name=a offset=8
59+
// CHECK-32: (struct size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1))
60+
// CHECK-32: (field name=b offset=8
61+
// CHECK-32: (opaque_existential size=16 alignment=4 stride=16 num_extra_inhabitants=4096 bitwise_takable=1
62+
// CHECK-32: (field name=metadata offset=12
63+
// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=4096 bitwise_takable=1))))
64+
// CHECK-32: (field name=c offset=24
65+
// CHECK-32: (struct size=0 alignment=1 stride=1 num_extra_inhabitants=0 bitwise_takable=1))
66+
// CHECK-32: (field name=d offset=24
67+
// CHECK-32: (opaque_existential size=16 alignment=4 stride=16 num_extra_inhabitants=4096 bitwise_takable=1
68+
// CHECK-32: (field name=metadata offset=12
69+
// CHECK-32: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=4096 bitwise_takable=1)))))
70+
71+
doneReflecting()
72+
73+
// CHECK-64: Done.
74+
75+
// CHECK-32: Done.

0 commit comments

Comments
 (0)