Skip to content

Commit 1feead8

Browse files
committed
[SILOptimizer] fix KeyPathProjector memory management
I was inconsistently providing initialized or uninitialized memory to the callback when projecting a settable address, depending on component type. We should always provide an uninitialized address.
1 parent a06fe96 commit 1feead8

File tree

4 files changed

+30
-10
lines changed

4 files changed

+30
-10
lines changed

include/swift/SILOptimizer/Utils/KeyPathProjector.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ class KeyPathProjector {
5858
/// \param accessType The access type of the projected address.
5959
/// \param callback A callback to invoke with the projected adddress.
6060
/// The projected address is only valid from within \p callback.
61+
/// If accessType is Get or Modify, the projected addres is an
62+
/// initialized address type. If accessType is set, the projected
63+
/// address points to uninitialized memory.
6164
virtual void project(AccessType accessType,
6265
std::function<void(SILValue addr)> callback) = 0;
6366

lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -219,13 +219,13 @@ bool SILCombiner::tryOptimizeKeypath(ApplyInst *AI) {
219219
return false;
220220

221221
SILValue keyPath, rootAddr, valueAddr;
222-
bool isModify = false;
222+
bool isSet = false;
223223
if (callee->getName() == "swift_setAtWritableKeyPath" ||
224224
callee->getName() == "swift_setAtReferenceWritableKeyPath") {
225225
keyPath = AI->getArgument(1);
226226
rootAddr = AI->getArgument(0);
227227
valueAddr = AI->getArgument(2);
228-
isModify = true;
228+
isSet = true;
229229
} else if (callee->getName() == "swift_getAtKeyPath") {
230230
keyPath = AI->getArgument(2);
231231
rootAddr = AI->getArgument(1);
@@ -240,13 +240,13 @@ bool SILCombiner::tryOptimizeKeypath(ApplyInst *AI) {
240240
return false;
241241

242242
KeyPathProjector::AccessType accessType;
243-
if (isModify) accessType = KeyPathProjector::AccessType::Set;
243+
if (isSet) accessType = KeyPathProjector::AccessType::Set;
244244
else accessType = KeyPathProjector::AccessType::Get;
245245

246246
projector->project(accessType, [&](SILValue projectedAddr) {
247-
if (isModify) {
247+
if (isSet) {
248248
Builder.createCopyAddr(AI->getLoc(), valueAddr, projectedAddr,
249-
IsTake, IsNotInitialization);
249+
IsTake, IsInitialization);
250250
} else {
251251
Builder.createCopyAddr(AI->getLoc(), projectedAddr, valueAddr,
252252
IsNotTake, IsInitialization);

lib/SILOptimizer/Utils/KeyPathProjector.cpp

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ class RootProjector : public KeyPathProjector {
3434

3535
void project(AccessType accessType,
3636
std::function<void (SILValue)> callback) override {
37+
if (accessType == AccessType::Set) {
38+
// We're setting the identity key path (\.self). The callback
39+
// expects an uninitialized address, so destroy the old value.
40+
builder.emitDestroyAddr(loc, root);
41+
}
3742
callback(root);
3843
}
3944

@@ -105,7 +110,11 @@ class StoredPropertyProjector : public ComponentProjector {
105110
parentAccessType = AccessType::Modify;
106111

107112
parent->project(parentAccessType, [&](SILValue parentValue) {
108-
callback(builder.createStructElementAddr(loc, parentValue, storedProperty));
113+
auto addr = builder.createStructElementAddr(loc, parentValue, storedProperty);
114+
// If we're setting, destroy the old value (the callback expects uninitialized memory)
115+
if (accessType == AccessType::Set)
116+
builder.createDestroyAddr(loc, addr);
117+
callback(addr);
109118
});
110119
} else {
111120
// Accessing a class member -> reading the class
@@ -143,6 +152,9 @@ class StoredPropertyProjector : public ComponentProjector {
143152
addr = beginAccess;
144153
}
145154

155+
// If we're setting, destroy the old value (the callback expects uninitialized memory)
156+
if (accessType == AccessType::Set)
157+
builder.createDestroyAddr(loc, addr);
146158
callback(addr);
147159

148160
// if a child hasn't started a new access (i.e. beginAccess is unchanged),
@@ -178,8 +190,11 @@ class TupleElementProjector : public ComponentProjector {
178190
parentAccessType = AccessType::Modify;
179191

180192
parent->project(parentAccessType, [&](SILValue parentValue) {
181-
callback(builder.createTupleElementAddr(loc, parentValue,
182-
component.getTupleIndex()));
193+
auto addr = builder.createTupleElementAddr(loc, parentValue, component.getTupleIndex());
194+
// If we're setting, destroy the old value (the callback expects uninitialized memory)
195+
if (accessType == AccessType::Set)
196+
builder.createDestroyAddr(loc, addr);
197+
callback(addr);
183198
});
184199
}
185200
};

test/SILOptimizer/optimize_keypath.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,8 @@ func testGenStructRead<T>(_ s: GenStruct<T>) -> T {
130130

131131
// CHECK-LABEL: sil {{.*}}testGenStructWrite
132132
// CHECK: [[A:%[0-9]+]] = struct_element_addr %0
133-
// CHECK: copy_addr %1 to [[A]]
133+
// CHECK: destroy_addr [[A]]
134+
// CHECK: copy_addr {{.*}} to [initialization] [[A]]
134135
// CHECK: return
135136
@inline(never)
136137
@_semantics("optimize.sil.specialize.generic.never")
@@ -183,7 +184,8 @@ func testDerivedClass2Read(_ c: DerivedClass2) -> Int {
183184
// CHECK: [[S:%[0-9]+]] = alloc_stack $T
184185
// CHECK: [[E:%[0-9]+]] = ref_element_addr %0
185186
// CHECK: [[A:%[0-9]+]] = begin_access [modify] [dynamic] [[E]]
186-
// CHECK: copy_addr [take] [[S]] to [[A]]
187+
// CHECK: destroy_addr [[A]]
188+
// CHECK: copy_addr [take] [[S]] to [initialization] [[A]]
187189
// CHECK: end_access [[A]]
188190
// CHECK: return
189191
@inline(never)

0 commit comments

Comments
 (0)