Skip to content

Commit c129356

Browse files
Merge pull request swiftlang#74316 from nate-chandler/cherrypick/release/6.0/rdar129622373
6.0: [ClosureSpecializer] Bail on noncopyable argument.
2 parents e471e2a + 0065403 commit c129356

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
@@ -5028,6 +5028,7 @@ void IRGenSILFunction::visitCondBranchInst(swift::CondBranchInst *i) {
50285028
}
50295029

50305030
void IRGenSILFunction::visitRetainValueInst(swift::RetainValueInst *i) {
5031+
assert(!i->getOperand()->getType().isMoveOnly());
50315032
Explosion in = getLoweredExplosion(i->getOperand());
50325033
Explosion out;
50335034
cast<LoadableTypeInfo>(getTypeInfo(i->getOperand()->getType()))
@@ -5038,6 +5039,7 @@ void IRGenSILFunction::visitRetainValueInst(swift::RetainValueInst *i) {
50385039

50395040
void IRGenSILFunction::visitRetainValueAddrInst(swift::RetainValueAddrInst *i) {
50405041
SILValue operandValue = i->getOperand();
5042+
assert(!operandValue->getType().isMoveOnly());
50415043
Address addr = getLoweredAddress(operandValue);
50425044
SILType addrTy = operandValue->getType();
50435045
SILType objectT = addrTy.getObjectType();

lib/SILOptimizer/IPO/ClosureSpecializer.cpp

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

578578
if (auto *PAI = dyn_cast<PartialApplyInst>(Closure)) {
579-
// Bail if any of the arguments are passed by address and
580-
// are not @inout.
581-
// This is a temporary limitation.
579+
// Check whether each argument is supported.
582580
auto ClosureCallee = FRI->getReferencedFunction();
583581
auto ClosureCalleeConv = ClosureCallee->getConventions();
584-
unsigned ClosureArgIdx =
582+
unsigned ClosureArgIdxBase =
585583
ClosureCalleeConv.getNumSILArguments() - PAI->getNumArguments();
586-
for (auto Arg : PAI->getArguments()) {
584+
for (auto pair : llvm::enumerate(PAI->getArguments())) {
585+
auto Arg = pair.value();
586+
auto ClosureArgIdx = pair.index() + ClosureArgIdxBase;
587+
auto ArgConvention =
588+
ClosureCalleeConv.getSILArgumentConvention(ClosureArgIdx);
589+
587590
SILType ArgTy = Arg->getType();
591+
// Specializing (currently) always produces a retain in the caller.
592+
// That's not allowed for values of move-only type.
593+
if (ArgTy.isMoveOnly()) {
594+
return false;
595+
}
596+
597+
// Only @inout/@inout_aliasable addresses are (currently) supported.
588598
// If our argument is an object, continue...
589599
if (ArgTy.isObject()) {
590600
++ClosureArgIdx;
591601
continue;
592602
}
593-
auto ArgConvention =
594-
ClosureCalleeConv.getSILArgumentConvention(ClosureArgIdx);
595603
if (ArgConvention != SILArgumentConvention::Indirect_Inout &&
596604
ArgConvention != SILArgumentConvention::Indirect_InoutAliasable)
597605
return false;
@@ -1372,7 +1380,7 @@ bool SILClosureSpecializerTransform::gatherCallSites(
13721380
// foo({ c() })
13731381
// }
13741382
//
1375-
// A limit of 2 is good enough and will not be exceed in "regular"
1383+
// A limit of 2 is good enough and will not be exceeded in "regular"
13761384
// optimization scenarios.
13771385
if (getSpecializationLevel(getClosureCallee(ClosureInst))
13781386
> 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)