Skip to content

Commit 4cf5c2e

Browse files
Merge pull request #29219 from aschwaighofer/irgen_fix_zero_size_field_instanceStart_2
IRGen: Correctly compute instanceStart for a Swift class that starts …
2 parents fdd1154 + 32a5bf0 commit 4cf5c2e

File tree

4 files changed

+53
-4
lines changed

4 files changed

+53
-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: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,24 @@
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-macosx: _DATA__TtC1A1G = private constant {{.*}} { i32 128, i32 {{(16|8)}}
14+
// CHECK-ios: _DATA__TtC1A1G = private constant {{.*}} { i32 128, i32 {{(16|8)}}
15+
// CHECK-watchos: _DATA__TtC1A1G = private constant {{.*}} { i32 128, i32 {{(16|8)}}
16+
// CHECK-tvos: _DATA__TtC1A1G = private constant {{.*}} { i32 128, i32 {{(16|8)}}
17+
18+
class G {
19+
var zeroSizedField = Singleton.only
20+
var r = ResilientInt(i:1)
21+
}
422

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

0 commit comments

Comments
 (0)