Skip to content

Commit 43ce060

Browse files
Merge pull request #74309 from nate-chandler/rdar129622373
[ClosureSpecializer] Bail on noncopyable argument.
2 parents bcdcdf1 + d895c34 commit 43ce060

File tree

4 files changed

+98
-8
lines changed

4 files changed

+98
-8
lines changed

lib/IRGen/IRGenSIL.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5109,6 +5109,7 @@ void IRGenSILFunction::visitCondBranchInst(swift::CondBranchInst *i) {
51095109
}
51105110

51115111
void IRGenSILFunction::visitRetainValueInst(swift::RetainValueInst *i) {
5112+
assert(!i->getOperand()->getType().isMoveOnly());
51125113
Explosion in = getLoweredExplosion(i->getOperand());
51135114
Explosion out;
51145115
cast<LoadableTypeInfo>(getTypeInfo(i->getOperand()->getType()))
@@ -5119,6 +5120,7 @@ void IRGenSILFunction::visitRetainValueInst(swift::RetainValueInst *i) {
51195120

51205121
void IRGenSILFunction::visitRetainValueAddrInst(swift::RetainValueAddrInst *i) {
51215122
SILValue operandValue = i->getOperand();
5123+
assert(!operandValue->getType().isMoveOnly());
51225124
Address addr = getLoweredAddress(operandValue);
51235125
SILType addrTy = operandValue->getType();
51245126
SILType objectT = addrTy.getObjectType();

lib/SILOptimizer/IPO/ClosureSpecializer.cpp

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -675,22 +675,30 @@ static bool isSupportedClosure(const SILInstruction *Closure) {
675675
return false;
676676

677677
if (auto *PAI = dyn_cast<PartialApplyInst>(Closure)) {
678-
// Bail if any of the arguments are passed by address and
679-
// are not @inout.
680-
// This is a temporary limitation.
678+
// Check whether each argument is supported.
681679
auto ClosureCallee = FRI->getReferencedFunction();
682680
auto ClosureCalleeConv = ClosureCallee->getConventions();
683-
unsigned ClosureArgIdx =
681+
unsigned ClosureArgIdxBase =
684682
ClosureCalleeConv.getNumSILArguments() - PAI->getNumArguments();
685-
for (auto Arg : PAI->getArguments()) {
683+
for (auto pair : llvm::enumerate(PAI->getArguments())) {
684+
auto Arg = pair.value();
685+
auto ClosureArgIdx = pair.index() + ClosureArgIdxBase;
686+
auto ArgConvention =
687+
ClosureCalleeConv.getSILArgumentConvention(ClosureArgIdx);
688+
686689
SILType ArgTy = Arg->getType();
690+
// Specializing (currently) always produces a retain in the caller.
691+
// That's not allowed for values of move-only type.
692+
if (ArgTy.isMoveOnly()) {
693+
return false;
694+
}
695+
696+
// Only @inout/@inout_aliasable addresses are (currently) supported.
687697
// If our argument is an object, continue...
688698
if (ArgTy.isObject()) {
689699
++ClosureArgIdx;
690700
continue;
691701
}
692-
auto ArgConvention =
693-
ClosureCalleeConv.getSILArgumentConvention(ClosureArgIdx);
694702
if (ArgConvention != SILArgumentConvention::Indirect_Inout &&
695703
ArgConvention != SILArgumentConvention::Indirect_InoutAliasable)
696704
return false;
@@ -1394,7 +1402,7 @@ bool SILClosureSpecializerTransform::gatherCallSites(
13941402
// foo({ c() })
13951403
// }
13961404
//
1397-
// A limit of 2 is good enough and will not be exceed in "regular"
1405+
// A limit of 2 is good enough and will not be exceeded in "regular"
13981406
// optimization scenarios.
13991407
if (getSpecializationLevel(getClosureCallee(ClosureInst))
14001408
> SpecializationLevelLimit) {

test/SILOptimizer/closure_specialize.sil

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -938,3 +938,35 @@ bb0(%0 : $Int):
938938
%empty = tuple ()
939939
return %empty : $()
940940
}
941+
942+
struct NC : ~Copyable {
943+
deinit {}
944+
}
945+
946+
sil hidden [noinline] @noncopyable_arg_closure : $@convention(thin) (@guaranteed NC) -> () {
947+
bb0(%0 : $NC):
948+
%retval = tuple ()
949+
return %retval : $()
950+
}
951+
952+
sil hidden [noinline] @use_noncopyable_arg_closure : $@convention(thin) (@noescape @callee_guaranteed () -> ()) -> () {
953+
bb0(%0 : $@noescape @callee_guaranteed () -> ()):
954+
%2 = apply %0() : $@noescape @callee_guaranteed () -> ()
955+
%3 = tuple ()
956+
return %3 : $()
957+
}
958+
959+
// Ensure that a retain_value of a noncopyable value isn't created.
960+
// CHECK-LABEL: sil @dont_specialize_noncopyable_arg_closure : {{.*}} {
961+
// CHECK-NOT: retain_value {{%.*}} : $NC
962+
// CHECK-LABEL: } // end sil function 'dont_specialize_noncopyable_arg_closure'
963+
sil @dont_specialize_noncopyable_arg_closure : $@convention(thin) (@guaranteed NC) -> () {
964+
bb0(%nc : $NC):
965+
%closure_fn = function_ref @noncopyable_arg_closure : $@convention(thin) (@guaranteed NC) -> ()
966+
%closure = partial_apply [callee_guaranteed] [on_stack] %closure_fn(%nc) : $@convention(thin) (@guaranteed NC) -> ()
967+
%use = function_ref @use_noncopyable_arg_closure : $@convention(thin) (@noescape @callee_guaranteed () -> ()) -> ()
968+
apply %use(%closure) : $@convention(thin) (@noescape @callee_guaranteed () -> ()) -> ()
969+
dealloc_stack %closure : $@noescape @callee_guaranteed () -> ()
970+
%11 = tuple ()
971+
return %11 : $()
972+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// RUN: %target-run-simple-swift(-Xfrontend -sil-verify-all)
2+
// RUN: %target-run-simple-swift(-O -Xfrontend -sil-verify-all)
3+
4+
// REQUIRES: executable_test
5+
6+
/// A unique value represented by a heap memory location.
7+
struct Handle: ~Copyable {
8+
var address: UnsafeMutableRawPointer
9+
10+
init() {
11+
self.address = .allocate(byteCount: 2, alignment: 2)
12+
}
13+
14+
consuming func done() {
15+
let address = self.address
16+
discard self
17+
address.deallocate()
18+
print("deallocated handle via done()")
19+
}
20+
21+
deinit {
22+
address.deallocate()
23+
print("deallocated handle via deinit")
24+
}
25+
}
26+
27+
func description(of pointer: UnsafeRawPointer) -> String {
28+
let address = UInt(bitPattern: pointer)
29+
return "0x" + String(address, radix: 16)
30+
}
31+
32+
func borrowHandleAndCrashNoMore(_ handle: borrowing Handle) -> String {
33+
var string = ""
34+
return string.withUTF8 { _ in
35+
description(of: handle.address)
36+
}
37+
}
38+
39+
let handleDescription: String
40+
41+
do {
42+
let handle = Handle()
43+
handleDescription = borrowHandleAndCrashNoMore(handle)
44+
handle.done()
45+
}
46+
47+
// CHECK: result: 0x
48+
print("result: \(handleDescription)")

0 commit comments

Comments
 (0)