Skip to content

Commit 00b50ce

Browse files
committed
KeyPaths: Take a bit for encoding "let"-ness of stored properties.
If we know a key path component can be accessed as a stored property, then we should also know whether it's a `let` or not, so it should be safe to encode this in the key path pattern. Stage this change in by changing the number of bits used to store in-line offsets, fixing up the parts of the key path implementation that assumed that it took up the entire payload bitfield.
1 parent c11aacc commit 00b50ce

File tree

7 files changed

+73
-47
lines changed

7 files changed

+73
-47
lines changed

include/swift/ABI/KeyPath.h

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -88,55 +88,57 @@ class KeyPathComponentHeader {
8888
}
8989

9090
constexpr static KeyPathComponentHeader
91-
forStructComponentWithInlineOffset(unsigned offset) {
91+
forStructComponentWithInlineOffset(bool isLet,
92+
unsigned offset) {
9293
return KeyPathComponentHeader(
9394
(_SwiftKeyPathComponentHeader_StructTag
9495
<< _SwiftKeyPathComponentHeader_DiscriminatorShift)
9596
| validateInlineOffset(offset));
9697
}
9798

9899
constexpr static KeyPathComponentHeader
99-
forStructComponentWithOutOfLineOffset() {
100+
forStructComponentWithOutOfLineOffset(bool isLet) {
100101
return KeyPathComponentHeader(
101102
(_SwiftKeyPathComponentHeader_StructTag
102103
<< _SwiftKeyPathComponentHeader_DiscriminatorShift)
103104
| _SwiftKeyPathComponentHeader_OutOfLineOffsetPayload);
104105
}
105106

106107
constexpr static KeyPathComponentHeader
107-
forStructComponentWithUnresolvedFieldOffset() {
108+
forStructComponentWithUnresolvedFieldOffset(bool isLet) {
108109
return KeyPathComponentHeader(
109110
(_SwiftKeyPathComponentHeader_StructTag
110111
<< _SwiftKeyPathComponentHeader_DiscriminatorShift)
111112
| _SwiftKeyPathComponentHeader_UnresolvedFieldOffsetPayload);
112113
}
113114

114115
constexpr static KeyPathComponentHeader
115-
forClassComponentWithInlineOffset(unsigned offset) {
116+
forClassComponentWithInlineOffset(bool isLet,
117+
unsigned offset) {
116118
return KeyPathComponentHeader(
117119
(_SwiftKeyPathComponentHeader_ClassTag
118120
<< _SwiftKeyPathComponentHeader_DiscriminatorShift)
119121
| validateInlineOffset(offset));
120122
}
121123

122124
constexpr static KeyPathComponentHeader
123-
forClassComponentWithOutOfLineOffset() {
125+
forClassComponentWithOutOfLineOffset(bool isLet) {
124126
return KeyPathComponentHeader(
125127
(_SwiftKeyPathComponentHeader_ClassTag
126128
<< _SwiftKeyPathComponentHeader_DiscriminatorShift)
127129
| _SwiftKeyPathComponentHeader_OutOfLineOffsetPayload);
128130
}
129131

130132
constexpr static KeyPathComponentHeader
131-
forClassComponentWithUnresolvedFieldOffset() {
133+
forClassComponentWithUnresolvedFieldOffset(bool isLet) {
132134
return KeyPathComponentHeader(
133135
(_SwiftKeyPathComponentHeader_ClassTag
134136
<< _SwiftKeyPathComponentHeader_DiscriminatorShift)
135137
| _SwiftKeyPathComponentHeader_UnresolvedFieldOffsetPayload);
136138
}
137139

138140
constexpr static KeyPathComponentHeader
139-
forClassComponentWithUnresolvedIndirectOffset() {
141+
forClassComponentWithUnresolvedIndirectOffset(bool isLet) {
140142
return KeyPathComponentHeader(
141143
(_SwiftKeyPathComponentHeader_ClassTag
142144
<< _SwiftKeyPathComponentHeader_DiscriminatorShift)

lib/IRGen/GenKeyPath.cpp

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -773,22 +773,23 @@ emitKeyPathComponent(IRGenModule &IGM,
773773
case KeyPathPatternComponent::Kind::StoredProperty: {
774774
auto property = cast<VarDecl>(component.getStoredPropertyDecl());
775775

776-
auto addFixedOffset = [&](bool isStruct, llvm::Constant *offset) {
776+
auto addFixedOffset = [&](bool isStruct, bool isLet,
777+
llvm::Constant *offset) {
777778
if (auto offsetInt = dyn_cast_or_null<llvm::ConstantInt>(offset)) {
778779
auto offsetValue = offsetInt->getValue().getZExtValue();
779780
if (KeyPathComponentHeader::offsetCanBeInline(offsetValue)) {
780781
auto header = isStruct
781782
? KeyPathComponentHeader
782-
::forStructComponentWithInlineOffset(offsetValue)
783+
::forStructComponentWithInlineOffset(isLet, offsetValue)
783784
: KeyPathComponentHeader
784-
::forClassComponentWithInlineOffset(offsetValue);
785+
::forClassComponentWithInlineOffset(isLet, offsetValue);
785786
fields.addInt32(header.getData());
786787
return;
787788
}
788789
}
789790
auto header = isStruct
790-
? KeyPathComponentHeader::forStructComponentWithOutOfLineOffset()
791-
: KeyPathComponentHeader::forClassComponentWithOutOfLineOffset();
791+
? KeyPathComponentHeader::forStructComponentWithOutOfLineOffset(isLet)
792+
: KeyPathComponentHeader::forClassComponentWithOutOfLineOffset(isLet);
792793
fields.addInt32(header.getData());
793794
fields.add(llvm::ConstantExpr::getTruncOrBitCast(offset, IGM.Int32Ty));
794795
};
@@ -801,7 +802,7 @@ emitKeyPathComponent(IRGenModule &IGM,
801802
loweredBaseTy,
802803
property)) {
803804
// We have a known constant fixed offset.
804-
addFixedOffset(/*struct*/ true, offset);
805+
addFixedOffset(/*struct*/ true, property->isLet(), offset);
805806
break;
806807
}
807808

@@ -811,7 +812,7 @@ emitKeyPathComponent(IRGenModule &IGM,
811812
auto fieldOffset = metadataLayout.getStaticFieldOffset(property);
812813

813814
auto header = KeyPathComponentHeader
814-
::forStructComponentWithUnresolvedFieldOffset();
815+
::forStructComponentWithUnresolvedFieldOffset(property->isLet());
815816
fields.addInt32(header.getData());
816817
fields.addInt32(fieldOffset.getValue());
817818
break;
@@ -829,14 +830,14 @@ emitKeyPathComponent(IRGenModule &IGM,
829830
loweredBaseTy,
830831
property);
831832
assert(offset && "no constant offset for ConstantDirect field?!");
832-
addFixedOffset(/*struct*/ false, offset);
833+
addFixedOffset(/*struct*/ false, property->isLet(), offset);
833834
break;
834835
}
835836
case FieldAccess::NonConstantDirect: {
836837
// A constant offset that's determined at class realization time.
837838
// We have to load the offset from a global ivar.
838839
auto header = KeyPathComponentHeader
839-
::forClassComponentWithUnresolvedIndirectOffset();
840+
::forClassComponentWithUnresolvedIndirectOffset(property->isLet());
840841
fields.addInt32(header.getData());
841842
fields.addAlignmentPadding(IGM.getPointerAlignment());
842843
auto offsetVar = IGM.getAddrOfFieldOffset(property, NotForDefinition);
@@ -846,8 +847,8 @@ emitKeyPathComponent(IRGenModule &IGM,
846847
case FieldAccess::ConstantIndirect: {
847848
// An offset that depends on the instance's generic parameterization,
848849
// but whose field offset is at a known vtable offset.
849-
auto header =
850-
KeyPathComponentHeader::forClassComponentWithUnresolvedFieldOffset();
850+
auto header = KeyPathComponentHeader
851+
::forClassComponentWithUnresolvedFieldOffset(property->isLet());
851852
fields.addInt32(header.getData());
852853
auto fieldOffset =
853854
getClassFieldOffsetOffset(IGM,

stdlib/public/SwiftShims/KeyPath.h

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,17 +59,23 @@ static const __swift_uint32_t _SwiftKeyPathComponentHeader_ExternalTag
5959
static const __swift_uint32_t
6060
_SwiftKeyPathComponentHeader_TrivialPropertyDescriptorMarker = 0U;
6161

62+
static const __swift_uint32_t _SwiftKeyPathComponentHeader_StoredOffsetPayloadMask
63+
= 0x007FFFFFU;
64+
6265
static const __swift_uint32_t _SwiftKeyPathComponentHeader_MaximumOffsetPayload
63-
= 0x00FFFFFCU;
66+
= 0x007FFFFCU;
6467

6568
static const __swift_uint32_t _SwiftKeyPathComponentHeader_UnresolvedIndirectOffsetPayload
66-
= 0x00FFFFFDU;
69+
= 0x007FFFFDU;
6770

6871
static const __swift_uint32_t _SwiftKeyPathComponentHeader_UnresolvedFieldOffsetPayload
69-
= 0x00FFFFFEU;
72+
= 0x007FFFFEU;
7073

7174
static const __swift_uint32_t _SwiftKeyPathComponentHeader_OutOfLineOffsetPayload
72-
= 0x00FFFFFFU;
75+
= 0x007FFFFFU;
76+
77+
static const __swift_uint32_t _SwiftKeyPathComponentHeader_StoredMutableFlag
78+
= 0x00800000U;
7379

7480
static const __swift_uint32_t _SwiftKeyPathComponentHeader_OptionalChainPayload
7581
= 0;

stdlib/public/core/KeyPath.swift

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -758,6 +758,9 @@ internal struct RawKeyPathComponent {
758758
internal static var endOfReferencePrefixFlag: UInt32 {
759759
return _SwiftKeyPathComponentHeader_EndOfReferencePrefixFlag
760760
}
761+
internal static var storedOffsetPayloadMask: UInt32 {
762+
return _SwiftKeyPathComponentHeader_StoredOffsetPayloadMask
763+
}
761764
internal static var outOfLineOffsetPayload: UInt32 {
762765
return _SwiftKeyPathComponentHeader_OutOfLineOffsetPayload
763766
}
@@ -852,6 +855,20 @@ internal struct RawKeyPathComponent {
852855
_value = _value & ~Header.payloadMask | newValue
853856
}
854857
}
858+
internal var storedOffsetPayload: UInt32 {
859+
get {
860+
_sanityCheck(kind == .struct || kind == .class,
861+
"not a stored component")
862+
return _value & Header.storedOffsetPayloadMask
863+
}
864+
set {
865+
_sanityCheck(kind == .struct || kind == .class,
866+
"not a stored component")
867+
_sanityCheck(newValue & Header.storedOffsetPayloadMask == newValue,
868+
"payload too big")
869+
_value = _value & ~Header.storedOffsetPayloadMask | newValue
870+
}
871+
}
855872
internal var endOfReferencePrefix: Bool {
856873
get {
857874
return _value & Header.endOfReferencePrefixFlag != 0
@@ -913,12 +930,12 @@ internal struct RawKeyPathComponent {
913930
internal func _componentBodySize(forPropertyDescriptor: Bool) -> Int {
914931
switch kind {
915932
case .struct, .class:
916-
if payload == Header.unresolvedFieldOffsetPayload
917-
|| payload == Header.outOfLineOffsetPayload {
933+
if storedOffsetPayload == Header.unresolvedFieldOffsetPayload
934+
|| storedOffsetPayload == Header.outOfLineOffsetPayload {
918935
// A 32-bit offset is stored in the body.
919936
return MemoryLayout<UInt32>.size
920937
}
921-
if payload == Header.unresolvedIndirectOffsetPayload {
938+
if storedOffsetPayload == Header.unresolvedIndirectOffsetPayload {
922939
// A pointer-aligned, pointer-sized pointer is stored in the body.
923940
return Header.pointerAlignmentSkew + MemoryLayout<Int>.size
924941
}
@@ -959,7 +976,9 @@ internal struct RawKeyPathComponent {
959976
let ptrSize = MemoryLayout<Int>.size
960977
switch header.kind {
961978
case .struct, .class:
962-
if header.payload == Header.payloadMask { return 4 } // overflowed
979+
if header.storedOffsetPayload == Header.outOfLineOffsetPayload {
980+
return 4 // overflowed
981+
}
963982
return 0
964983
case .external:
965984
// align to pointer + pointer to external descriptor
@@ -993,13 +1012,13 @@ internal struct RawKeyPathComponent {
9931012
"no offset for this kind")
9941013
// An offset too large to fit inline is represented by a signal and stored
9951014
// in the body.
996-
if header.payload == Header.outOfLineOffsetPayload {
1015+
if header.storedOffsetPayload == Header.outOfLineOffsetPayload {
9971016
// Offset overflowed into body
9981017
_sanityCheck(body.count >= MemoryLayout<UInt32>.size,
9991018
"component not big enough")
10001019
return Int(body.load(as: UInt32.self))
10011020
}
1002-
return Int(header.payload)
1021+
return Int(header.storedOffsetPayload)
10031022
}
10041023

10051024
internal var _computedIDValue: Int {
@@ -1165,7 +1184,7 @@ internal struct RawKeyPathComponent {
11651184
switch header.kind {
11661185
case .struct,
11671186
.class:
1168-
if header.payload == Header.outOfLineOffsetPayload {
1187+
if header.storedOffsetPayload == Header.outOfLineOffsetPayload {
11691188
let overflowOffset = body.load(as: UInt32.self)
11701189
buffer.storeBytes(of: overflowOffset, toByteOffset: 4,
11711190
as: UInt32.self)
@@ -2292,11 +2311,11 @@ internal func _getKeyPathClassAndInstanceSizeFromPattern(
22922311
// reassigned.
22932312

22942313
// Check the final instantiated size of the offset.
2295-
if header.payload == RawKeyPathComponent.Header.unresolvedFieldOffsetPayload
2296-
|| header.payload == RawKeyPathComponent.Header.outOfLineOffsetPayload {
2314+
if header.storedOffsetPayload == RawKeyPathComponent.Header.unresolvedFieldOffsetPayload
2315+
|| header.storedOffsetPayload == RawKeyPathComponent.Header.outOfLineOffsetPayload {
22972316
_ = buffer.pop(UInt32.self)
22982317
}
2299-
if header.payload == RawKeyPathComponent.Header.unresolvedIndirectOffsetPayload {
2318+
if header.storedOffsetPayload == RawKeyPathComponent.Header.unresolvedIndirectOffsetPayload {
23002319
_ = buffer.pop(Int.self)
23012320
// On 64-bit systems the pointer to the ivar offset variable is
23022321
// pointer-sized and -aligned, but the resulting offset ought to be
@@ -2363,7 +2382,7 @@ internal func _getKeyPathClassAndInstanceSizeFromPattern(
23632382
// The final component will be a stored component with just an offset.
23642383
// If the offset requires resolution, then it'll be stored out of
23652384
// line after the header.
2366-
if descriptorHeader.payload
2385+
if descriptorHeader.storedOffsetPayload
23672386
> RawKeyPathComponent.Header.maximumOffsetPayload {
23682387
newComponentSize = MemoryLayout<RawKeyPathComponent.Header>.size
23692388
+ MemoryLayout<UInt32>.size
@@ -2617,7 +2636,7 @@ internal func _instantiateKeyPathBuffer(
26172636

26182637
func tryToResolveOffset(header: RawKeyPathComponent.Header,
26192638
getOutOfLineOffset: () -> UInt32) {
2620-
if header.payload == RawKeyPathComponent.Header.unresolvedFieldOffsetPayload {
2639+
if header.storedOffsetPayload == RawKeyPathComponent.Header.unresolvedFieldOffsetPayload {
26212640
// Look up offset in type metadata. The value in the pattern is the
26222641
// offset within the metadata object.
26232642
let metadataPtr = unsafeBitCast(base, to: UnsafeRawPointer.self)
@@ -2634,28 +2653,28 @@ internal func _instantiateKeyPathBuffer(
26342653

26352654
// Rewrite the header for a resolved offset.
26362655
var newHeader = header
2637-
newHeader.payload = RawKeyPathComponent.Header.outOfLineOffsetPayload
2656+
newHeader.storedOffsetPayload = RawKeyPathComponent.Header.outOfLineOffsetPayload
26382657
pushDest(newHeader)
26392658
pushDest(offset)
26402659
return
26412660
}
26422661

2643-
if header.payload == RawKeyPathComponent.Header.unresolvedIndirectOffsetPayload {
2662+
if header.storedOffsetPayload == RawKeyPathComponent.Header.unresolvedIndirectOffsetPayload {
26442663
// Look up offset in the indirectly-referenced variable we have a
26452664
// pointer.
26462665
let offsetVar = patternBuffer.pop(UnsafeRawPointer.self)
26472666
let offsetValue = UInt32(offsetVar.load(as: UInt.self))
26482667
// Rewrite the header for a resolved offset.
26492668
var newHeader = header
2650-
newHeader.payload = RawKeyPathComponent.Header.outOfLineOffsetPayload
2669+
newHeader.storedOffsetPayload = RawKeyPathComponent.Header.outOfLineOffsetPayload
26512670
pushDest(newHeader)
26522671
pushDest(offsetValue)
26532672
return
26542673
}
26552674

26562675
// Otherwise, just transfer the pre-resolved component.
26572676
pushDest(header)
2658-
if header.payload == RawKeyPathComponent.Header.outOfLineOffsetPayload {
2677+
if header.storedOffsetPayload == RawKeyPathComponent.Header.outOfLineOffsetPayload {
26592678
let offset = getOutOfLineOffset() //patternBuffer.pop(UInt32.self)
26602679
pushDest(offset)
26612680
}

test/IRGen/keypaths.sil

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ sil_vtable C {}
205205
// CHECK-SAME: i32 8,
206206
// CHECK-64-SAME: [4 x i8] zeroinitializer,
207207
// -- struct with runtime-resolved offset
208-
// CHECK-SAME: <i32 0x01fffffe>,
208+
// CHECK-SAME: <i32 0x017ffffe>,
209209
// CHECK-32-SAME: i32 16 }>
210210
// CHECK-64-SAME: i32 32 }>
211211

@@ -218,7 +218,7 @@ sil_vtable C {}
218218
// CHECK-SAME: i32 8,
219219
// CHECK-64-SAME: [4 x i8] zeroinitializer,
220220
// -- struct with runtime-resolved offset
221-
// CHECK-SAME: <i32 0x01fffffe>,
221+
// CHECK-SAME: <i32 0x017ffffe>,
222222
// CHECK-32-SAME: i32 20 }>
223223
// CHECK-64-SAME: i32 36 }>
224224

test/IRGen/keypaths_objc.sil

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ sil @x_get : $@convention(thin) (@in_guaranteed C) -> @out NSString
2323
// CHECK-SAME: i8** @"\01L_selector(x)"
2424

2525
// CHECK: [[KEYPATH_B:@keypath.*]] = private global
26-
// -- 0x03fffffd: class stored property with indirect offset
27-
// CHECK-SAME: <i32 0x03fffffd>,
26+
// -- 0x037ffffd: class stored property with indirect offset
27+
// CHECK-SAME: <i32 0x037ffffd>,
2828
// CHECK-SAME: @"$S13keypaths_objc1CC6storedSivpWvd"
2929

3030
// CHECK-LABEL: define swiftcc void @objc_only_property()

test/IRGen/property_descriptor.sil

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,17 +39,15 @@ public struct ExternalReabstractions<T> {
3939
@sil_stored public var reabstracted: () -> () { get set }
4040
}
4141

42-
// -- 0xff_fffe - struct property, offset resolved from field offset vector in metadata
42+
// -- struct property, offset resolved from field offset vector in metadata
4343
// CHECK-64: @"$S19property_descriptor15ExternalGenericV2roxvpMV" =
44-
// CHECK-64-SAME: <{ <i32 0x01ff_fffe>, i32 32 }>, align 8
4544
// CHECK-32: @"$S19property_descriptor15ExternalGenericV2roxvpMV" =
46-
// CHECK-32-SAME: <{ <i32 0x01ff_fffe>, i32 16 }>, align 4
45+
// CHECK-SAME: <{ <i32 0x017f_fffe>,
4746
sil_property #ExternalGeneric.ro <T: Comparable> (
4847
stored_property #ExternalGeneric.ro : $T)
4948
// CHECK-64: @"$S19property_descriptor15ExternalGenericV2rwxvpMV" =
50-
// CHECK-64-SAME: <{ <i32 0x01ff_fffe>, i32 36 }>, align 8
5149
// CHECK-32: @"$S19property_descriptor15ExternalGenericV2rwxvpMV" =
52-
// CHECK-32-SAME: <{ <i32 0x01ff_fffe>, i32 20 }>, align 4
50+
// CHECK-SAME: <{ <i32 0x017f_fffe>,
5351
sil_property #ExternalGeneric.rw <T: Comparable> (
5452
stored_property #ExternalGeneric.rw : $T)
5553

0 commit comments

Comments
 (0)