Skip to content

Commit 7f20d93

Browse files
committed
IRGen: Correctly compute instanceStart for a Swift class that starts with an empty field and is followed by a resilient field
We used to crash for classes that have an empty and a resilient field during intialization if the object was in the shared cache. class CrashInInit { var empty = EmptyStruct() var resilient = ResilientThing() } What happened was that for such a class we we would compute a instanceStart of 0. The shared cache builder would then slide the value of the constant ivar offset for the empty field from 0 to 16. However, the field offset for empty fields is assumed to be zero and the runtime does not compute a different value for the empty field and so the field offset for the empty field remains 0. The runtime then trys to reconcile the field offset (0) and the ivar offset (16) trying to write to the ivar offset. However, the ivar offset is marked as constant and so we crashed. This can be avoided by correctly computing the instanceStart for such a class to be 16 such that the shared cache builder does not update the value of the empty field. rdar://rdar://58458169
1 parent 6126fdb commit 7f20d93

File tree

4 files changed

+50
-4
lines changed

4 files changed

+50
-4
lines changed

lib/IRGen/ClassLayout.cpp

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,14 @@ ClassLayout::ClassLayout(const StructLayoutBuilder &builder,
2929
llvm::Type *classTy,
3030
ArrayRef<VarDecl *> allStoredProps,
3131
ArrayRef<FieldAccess> allFieldAccesses,
32-
ArrayRef<ElementLayout> allElements)
32+
ArrayRef<ElementLayout> allElements,
33+
Size headerSize)
3334
: MinimumAlign(builder.getAlignment()),
3435
MinimumSize(builder.getSize()),
3536
IsFixedLayout(builder.isFixedLayout()),
3637
Options(options),
3738
Ty(classTy),
39+
HeaderSize(headerSize),
3840
AllStoredProperties(allStoredProps),
3941
AllFieldAccesses(allFieldAccesses),
4042
AllElements(allElements) { }
@@ -46,12 +48,33 @@ Size ClassLayout::getInstanceStart() const {
4648
elements = elements.drop_front();
4749

4850
// Ignore empty elements.
51+
bool haveSeenEmpty = false;
4952
if (element.isEmpty()) {
5053
continue;
5154
} else if (element.hasByteOffset()) {
5255
// FIXME: assumes layout is always sequential!
5356
return element.getByteOffset();
5457
} else {
58+
// We used to crash for classes that have an empty and a resilient field
59+
// during intialization.
60+
// class CrashInInit {
61+
// var empty = EmptyStruct()
62+
// var resilient = ResilientThing()
63+
// }
64+
// What happened was that for such a class we we would compute a
65+
// instanceStart of 0. The shared cache builder would then slide the value
66+
// of the constant ivar offset for the empty field from 0 to 16. However
67+
// the field offset for empty fields is assume to be zero and the runtime
68+
// does not compute a different value for the empty field and so the field
69+
// offset for the empty field stays 0. The runtime then trys to reconcile
70+
// the field offset and the ivar offset trying to write to the ivar
71+
// offset. However, the ivar offset is marked as constant and so we
72+
// crashed.
73+
// This can be avoided by correctly computing the instanceStart for such a
74+
// class to be 16 such that the shared cache builder does not update the
75+
// value of the empty field.
76+
if (!Options.contains(ClassMetadataFlags::ClassHasObjCAncestry))
77+
return HeaderSize;
5578
return Size(0);
5679
}
5780
}

lib/IRGen/ClassLayout.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,9 @@ class ClassLayout {
114114
/// The LLVM type for instances of this class.
115115
llvm::Type *Ty;
116116

117+
/// The header size of this class.
118+
Size HeaderSize;
119+
117120
/// Lazily-initialized array of all fragile stored properties directly defined
118121
/// in the class itself.
119122
ArrayRef<VarDecl *> AllStoredProperties;
@@ -131,7 +134,8 @@ class ClassLayout {
131134
llvm::Type *classTy,
132135
ArrayRef<VarDecl *> allStoredProps,
133136
ArrayRef<FieldAccess> allFieldAccesses,
134-
ArrayRef<ElementLayout> allElements);
137+
ArrayRef<ElementLayout> allElements,
138+
Size headerSize);
135139

136140
Size getInstanceStart() const;
137141

lib/IRGen/GenClass.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,8 @@ namespace {
165165

166166
ClassMetadataOptions Options;
167167

168+
Size HeaderSize;
169+
168170
public:
169171
ClassLayoutBuilder(IRGenModule &IGM, SILType classType,
170172
ReferenceCounting refcounting,
@@ -178,11 +180,13 @@ namespace {
178180
case ReferenceCounting::Native:
179181
// For native classes, place a full object header.
180182
addHeapHeader();
183+
HeaderSize = CurSize;
181184
break;
182185
case ReferenceCounting::ObjC:
183186
// For ObjC-inheriting classes, we don't reliably know the size of the
184187
// base class, but NSObject only has an `isa` pointer at most.
185188
addNSObjectHeader();
189+
HeaderSize = CurSize;
186190
break;
187191
case ReferenceCounting::Block:
188192
case ReferenceCounting::Unknown:
@@ -222,7 +226,7 @@ namespace {
222226
auto allElements = IGM.Context.AllocateCopy(Elements);
223227

224228
return ClassLayout(*this, Options, classTy,
225-
allStoredProps, allFieldAccesses, allElements);
229+
allStoredProps, allFieldAccesses, allElements, HeaderSize);
226230
}
227231

228232
private:

test/IRGen/metadata.swift

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,21 @@
11
// RUN: %empty-directory(%t)
22
// RUN: %target-swift-frontend -emit-module -enable-library-evolution -emit-module-path=%t/resilient_struct.swiftmodule -module-name=resilient_struct %S/../Inputs/resilient_struct.swift
3-
// RUN: %target-swift-frontend -module-name A -I %t %S/Inputs/metadata2.swift -primary-file %s -emit-ir | %FileCheck %s
3+
// RUN: %target-swift-frontend -module-name A -I %t %S/Inputs/metadata2.swift -primary-file %s -emit-ir | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-os
4+
5+
import resilient_struct
6+
7+
enum Singleton {
8+
case only
9+
}
10+
11+
// CHECK: @"$s1A1GC14zeroSizedFieldAA9SingletonOvpWvd" = hidden constant i{{(64|32)}} 0
12+
// Check that the instance start is after the header (at 8 or 16).
13+
// CHECK: _DATA__TtC1A1G = private constant {{.*}} { i32 128, i32 {{(16|8)}}
14+
15+
class G {
16+
var zeroSizedField = Singleton.only
17+
var r = ResilientInt(i:1)
18+
}
419

520
// CHECK-LABEL: define {{.*}}swiftcc %swift.metadata_response @"$s1A12MyControllerCMr"(%swift.type*, i8*, i8**)
621
// CHECK-NOT: ret

0 commit comments

Comments
 (0)