AllocStackToBox: fix a bug which results in a too early released captured variable

In case of a borrowed `alloc_box`, the optimization didn't look through the `begin_borrow` when calculating the final release of the box.
This resulted in inserting the destroy of the inserted `alloc_stack` too early.

rdar://97087762
This commit is contained in:
Erik Eckstein
2022-07-18 17:13:12 +02:00
parent 341bc34fe8
commit dc42c4c17d
2 changed files with 41 additions and 4 deletions

View File

@@ -53,9 +53,9 @@ static llvm::cl::opt<bool> AllocBoxToStackAnalyzeApply(
// SIL Utilities for alloc_box Promotion // SIL Utilities for alloc_box Promotion
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
static SILValue stripOffCopyValue(SILValue V) { static SILValue stripOffCopyAndBorrow(SILValue V) {
while (auto *CVI = dyn_cast<CopyValueInst>(V)) { while (isa<CopyValueInst>(V) || isa<BeginBorrowInst>(V)) {
V = CVI->getOperand(); V = cast<SingleValueInstruction>(V)->getOperand(0);
} }
return V; return V;
} }
@@ -127,7 +127,7 @@ static bool addLastRelease(SILValue V, SILBasicBlock *BB,
for (auto I = BB->rbegin(); I != BB->rend(); ++I) { for (auto I = BB->rbegin(); I != BB->rend(); ++I) {
if (isa<StrongReleaseInst>(*I) || isa<DeallocBoxInst>(*I) || if (isa<StrongReleaseInst>(*I) || isa<DeallocBoxInst>(*I) ||
isa<DestroyValueInst>(*I)) { isa<DestroyValueInst>(*I)) {
if (stripOffCopyValue(I->getOperand(0)) != V) if (stripOffCopyAndBorrow(I->getOperand(0)) != V)
continue; continue;
Releases.push_back(&*I); Releases.push_back(&*I);

View File

@@ -469,6 +469,43 @@ bb0(%0 : $Int):
unreachable unreachable
} }
sil [ossa] @closureWithBoxArg : $@convention(thin) (@guaranteed { var SomeClass }) -> () {
bb0(%0 : @guaranteed ${ var SomeClass }):
%r = tuple ()
return %r : $()
}
sil [ossa] @useClosure : $@convention(thin) (@guaranteed @callee_guaranteed () -> ()) -> () {
bb0(%0 : @guaranteed $@callee_guaranteed () -> ()):
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil [ossa] @finalReleaseWithBorrow
// CHECK: [[S:%[0-9]+]] = alloc_stack {{.*}}$SomeClass
// CHECK-NOT: destroy_addr
// CHECK: [[F:%[0-9]+]] = function_ref @useClosure
// CHECK: apply [[F]]
// CHECK: destroy_addr [[S]]
// CHECK: } // end sil function 'finalReleaseWithBorrow'
sil [ossa] @finalReleaseWithBorrow : $@convention(thin) (@owned SomeClass) -> () {
bb0(%0 : @owned $SomeClass):
%1 = alloc_box $ { var SomeClass }
%2 = begin_borrow %1 : ${ var SomeClass }
%1a = project_box %2 : ${ var SomeClass }, 0
store %0 to [init] %1a : $*SomeClass
%3 = function_ref @closureWithBoxArg : $@convention(thin) (@guaranteed { var SomeClass }) -> ()
%4 = copy_value %2 : ${ var SomeClass }
%5 = partial_apply [callee_guaranteed] %3(%4) : $@convention(thin) (@guaranteed { var SomeClass }) -> ()
end_borrow %2 : ${ var SomeClass }
destroy_value %1 : ${ var SomeClass }
%6 = function_ref @useClosure : $@convention(thin) (@guaranteed @callee_guaranteed () -> ()) -> ()
apply %6(%5) : $@convention(thin) (@guaranteed @callee_guaranteed () -> ()) -> ()
destroy_value %5 : $@callee_guaranteed () -> ()
%10 = tuple ()
return %10 : $()
}
// CHECK-LABEL: sil [transparent] [serialized] [ossa] @mightApply : $@convention(thin) <U where U : P> (@owned @callee_owned () -> @out U) -> () // CHECK-LABEL: sil [transparent] [serialized] [ossa] @mightApply : $@convention(thin) <U where U : P> (@owned @callee_owned () -> @out U) -> ()
sil [transparent] [serialized] [ossa] @mightApply : $@convention(thin) <U where U : P> (@owned @callee_owned () -> @out U) -> () { sil [transparent] [serialized] [ossa] @mightApply : $@convention(thin) <U where U : P> (@owned @callee_owned () -> @out U) -> () {
// CHECK: bb0 // CHECK: bb0