Skip to content

Commit a71eaf1

Browse files
committed
[Runtime] Don't shrink class InstanceStart when initing field offset vectors.
When ObjC interop is enabled, we emit what we think will be the class's InstanceStart and InstanceSize based on what we know about the superclass. We then fix up those values at runtime if they don't match. The compiler will emit this data into read-only memory if it knows they will always match, and then the runtime avoids writing to these fields if they already contain the correct value. However, the compiler aligns the InstanceStart, but instance size is not aligned. For example: class Super<T> { var bool = true } class Sub: Super<Int> { var obj: AnyObject? } Super's InstanceSize is 17 (on 64-bit) but Sub's InstanceStart is 24. The compiler sees a fixed layout and emits Sub's rodata into constant memory. The runtime sees that 24 does not equal 17 and tries to update it, but we don't want it to. Instead, only update InstanceStart if it's too small to accommodate the superclass's InstanceSize. If it's overlay large then we'll just leave it alone. The compiler underestimates InstanceStart when it doesn't know the superclass's size so this should only happen due to alignment. rdar://123695998
1 parent bd46860 commit a71eaf1

File tree

2 files changed

+68
-2
lines changed

2 files changed

+68
-2
lines changed

stdlib/public/runtime/Metadata.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3514,9 +3514,14 @@ static void initClassFieldOffsetVector(ClassMetadata *self,
35143514
//
35153515
// The rodata may be in read-only memory if the compiler knows that the size
35163516
// it generates is already definitely correct. Don't write to this value
3517-
// unless it's necessary.
3518-
if (rodata->InstanceStart != size)
3517+
// unless it's necessary. We'll grow the space for the superclass if needed,
3518+
// but not shrink it, as the compiler may write an unaligned size that's less
3519+
// than our aligned InstanceStart.
3520+
if (rodata->InstanceStart < size)
35193521
rodata->InstanceStart = size;
3522+
else
3523+
size = rodata->InstanceStart;
3524+
35203525
#endif
35213526

35223527
// Okay, now do layout.
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift %s -o %t/subclass_instance_start_adjustment
3+
// RUN: %target-codesign %t/subclass_instance_start_adjustment
4+
// RUN: %target-run %t/subclass_instance_start_adjustment | %FileCheck %s
5+
6+
// REQUIRES: executable_test
7+
// UNSUPPORTED: use_os_stdlib
8+
// UNSUPPORTED: back_deployment_runtime
9+
10+
// Make sure we get the InstanceStart adjustment right for concrete subclasses
11+
// of generic classes with different sizes, and especially with an unaligned
12+
// size.
13+
14+
class GenericSuperclassWithAlignedSize<T> {
15+
var field = 42
16+
}
17+
18+
class SubclassOfGenericSuperclassWithAlignedSize: GenericSuperclassWithAlignedSize<Int> {
19+
var subfield = 43
20+
}
21+
22+
do {
23+
let obj = SubclassOfGenericSuperclassWithAlignedSize()
24+
print(obj, obj.field, obj.subfield)
25+
// CHECK: SubclassOfGenericSuperclassWithAlignedSize 42 43
26+
}
27+
28+
class GenericSuperclassWithMisalignedSize<T> {
29+
var field = true
30+
}
31+
32+
class SubclassOfGenericSuperclassWithMisalignedSize: GenericSuperclassWithMisalignedSize<Int> {
33+
var subfield = 44
34+
}
35+
36+
do {
37+
let obj = SubclassOfGenericSuperclassWithMisalignedSize()
38+
print(obj, obj.field, obj.subfield)
39+
// CHECK: SubclassOfGenericSuperclassWithMisalignedSize true 44
40+
}
41+
42+
#if canImport(Foundation)
43+
import Foundation
44+
45+
class GenericSuperclassWithURLField<T> {
46+
var field: URL?
47+
}
48+
49+
class SubclassOfGenericSuperclassWithURLField: GenericSuperclassWithURLField<Int> {
50+
var subfield = 45
51+
}
52+
53+
do {
54+
let obj = SubclassOfGenericSuperclassWithURLField()
55+
print(obj, obj.field as Any, obj.subfield)
56+
// CHECK: SubclassOfGenericSuperclassWithURLField nil 45
57+
}
58+
#else
59+
// Simulate the expected output.
60+
print("SubclassOfGenericSuperclassWithURLField nil 45")
61+
#endif

0 commit comments

Comments
 (0)