mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[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:
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user