[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.
This commit is contained in:
Jonathan Keller
2020-02-15 11:00:46 -08:00
parent a06fe96fd9
commit 1feead804b
4 changed files with 30 additions and 10 deletions

View File

@@ -58,6 +58,9 @@ public:
/// \param accessType The access type of the projected address. /// \param accessType The access type of the projected address.
/// \param callback A callback to invoke with the projected adddress. /// \param callback A callback to invoke with the projected adddress.
/// The projected address is only valid from within \p callback. /// The projected address is only valid from within \p callback.
/// If accessType is Get or Modify, the projected addres is an
/// initialized address type. If accessType is set, the projected
/// address points to uninitialized memory.
virtual void project(AccessType accessType, virtual void project(AccessType accessType,
std::function<void(SILValue addr)> callback) = 0; std::function<void(SILValue addr)> callback) = 0;

View File

@@ -219,13 +219,13 @@ bool SILCombiner::tryOptimizeKeypath(ApplyInst *AI) {
return false; return false;
SILValue keyPath, rootAddr, valueAddr; SILValue keyPath, rootAddr, valueAddr;
bool isModify = false; bool isSet = false;
if (callee->getName() == "swift_setAtWritableKeyPath" || if (callee->getName() == "swift_setAtWritableKeyPath" ||
callee->getName() == "swift_setAtReferenceWritableKeyPath") { callee->getName() == "swift_setAtReferenceWritableKeyPath") {
keyPath = AI->getArgument(1); keyPath = AI->getArgument(1);
rootAddr = AI->getArgument(0); rootAddr = AI->getArgument(0);
valueAddr = AI->getArgument(2); valueAddr = AI->getArgument(2);
isModify = true; isSet = true;
} else if (callee->getName() == "swift_getAtKeyPath") { } else if (callee->getName() == "swift_getAtKeyPath") {
keyPath = AI->getArgument(2); keyPath = AI->getArgument(2);
rootAddr = AI->getArgument(1); rootAddr = AI->getArgument(1);
@@ -240,13 +240,13 @@ bool SILCombiner::tryOptimizeKeypath(ApplyInst *AI) {
return false; return false;
KeyPathProjector::AccessType accessType; KeyPathProjector::AccessType accessType;
if (isModify) accessType = KeyPathProjector::AccessType::Set; if (isSet) accessType = KeyPathProjector::AccessType::Set;
else accessType = KeyPathProjector::AccessType::Get; else accessType = KeyPathProjector::AccessType::Get;
projector->project(accessType, [&](SILValue projectedAddr) { projector->project(accessType, [&](SILValue projectedAddr) {
if (isModify) { if (isSet) {
Builder.createCopyAddr(AI->getLoc(), valueAddr, projectedAddr, Builder.createCopyAddr(AI->getLoc(), valueAddr, projectedAddr,
IsTake, IsNotInitialization); IsTake, IsInitialization);
} else { } else {
Builder.createCopyAddr(AI->getLoc(), projectedAddr, valueAddr, Builder.createCopyAddr(AI->getLoc(), projectedAddr, valueAddr,
IsNotTake, IsInitialization); IsNotTake, IsInitialization);

View File

@@ -34,6 +34,11 @@ public:
void project(AccessType accessType, void project(AccessType accessType,
std::function<void (SILValue)> callback) override { std::function<void (SILValue)> callback) override {
if (accessType == AccessType::Set) {
// We're setting the identity key path (\.self). The callback
// expects an uninitialized address, so destroy the old value.
builder.emitDestroyAddr(loc, root);
}
callback(root); callback(root);
} }
@@ -105,7 +110,11 @@ public:
parentAccessType = AccessType::Modify; parentAccessType = AccessType::Modify;
parent->project(parentAccessType, [&](SILValue parentValue) { parent->project(parentAccessType, [&](SILValue parentValue) {
callback(builder.createStructElementAddr(loc, parentValue, storedProperty)); auto addr = builder.createStructElementAddr(loc, parentValue, storedProperty);
// If we're setting, destroy the old value (the callback expects uninitialized memory)
if (accessType == AccessType::Set)
builder.createDestroyAddr(loc, addr);
callback(addr);
}); });
} else { } else {
// Accessing a class member -> reading the class // Accessing a class member -> reading the class
@@ -143,6 +152,9 @@ public:
addr = beginAccess; addr = beginAccess;
} }
// If we're setting, destroy the old value (the callback expects uninitialized memory)
if (accessType == AccessType::Set)
builder.createDestroyAddr(loc, addr);
callback(addr); callback(addr);
// if a child hasn't started a new access (i.e. beginAccess is unchanged), // if a child hasn't started a new access (i.e. beginAccess is unchanged),
@@ -178,8 +190,11 @@ public:
parentAccessType = AccessType::Modify; parentAccessType = AccessType::Modify;
parent->project(parentAccessType, [&](SILValue parentValue) { parent->project(parentAccessType, [&](SILValue parentValue) {
callback(builder.createTupleElementAddr(loc, parentValue, auto addr = builder.createTupleElementAddr(loc, parentValue, component.getTupleIndex());
component.getTupleIndex())); // If we're setting, destroy the old value (the callback expects uninitialized memory)
if (accessType == AccessType::Set)
builder.createDestroyAddr(loc, addr);
callback(addr);
}); });
} }
}; };

View File

@@ -130,7 +130,8 @@ func testGenStructRead<T>(_ s: GenStruct<T>) -> T {
// CHECK-LABEL: sil {{.*}}testGenStructWrite // CHECK-LABEL: sil {{.*}}testGenStructWrite
// CHECK: [[A:%[0-9]+]] = struct_element_addr %0 // CHECK: [[A:%[0-9]+]] = struct_element_addr %0
// CHECK: copy_addr %1 to [[A]] // CHECK: destroy_addr [[A]]
// CHECK: copy_addr {{.*}} to [initialization] [[A]]
// CHECK: return // CHECK: return
@inline(never) @inline(never)
@_semantics("optimize.sil.specialize.generic.never") @_semantics("optimize.sil.specialize.generic.never")
@@ -183,7 +184,8 @@ func testDerivedClass2Read(_ c: DerivedClass2) -> Int {
// CHECK: [[S:%[0-9]+]] = alloc_stack $T // CHECK: [[S:%[0-9]+]] = alloc_stack $T
// CHECK: [[E:%[0-9]+]] = ref_element_addr %0 // CHECK: [[E:%[0-9]+]] = ref_element_addr %0
// CHECK: [[A:%[0-9]+]] = begin_access [modify] [dynamic] [[E]] // CHECK: [[A:%[0-9]+]] = begin_access [modify] [dynamic] [[E]]
// CHECK: copy_addr [take] [[S]] to [[A]] // CHECK: destroy_addr [[A]]
// CHECK: copy_addr [take] [[S]] to [initialization] [[A]]
// CHECK: end_access [[A]] // CHECK: end_access [[A]]
// CHECK: return // CHECK: return
@inline(never) @inline(never)