Skip to content

Commit 509735e

Browse files
committed
IRGen: Work around RemoteMirror bug generating reflection info for empty builtin types.
The RemoteMirror library in shipping versions of macOS/iOS/tvOS/watchOS crashes if the compiler emits a BuiltinTypeDescriptor with size zero. Although this is fixed in top-of-tree RemoteMirror, we want binaries built with the new compiler to still be inspectable when run on older OSes. Generate the metadata as an empty struct with no fields when deploying back to these older platforms, which should be functionally equivalent for most purposes. Fixes rdar://problem/57924984.
1 parent 93d07c6 commit 509735e

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)