Skip to content

Commit 2492e45

Browse files
authored
Merge pull request #31719 from jckarter/simple-partial-apply
IRGen: Restore handling of "simple" partial_apply instructions.
2 parents 7ce30d7 + 1df1f66 commit 2492e45

File tree

2 files changed

+155
-0
lines changed

2 files changed

+155
-0
lines changed

lib/IRGen/IRGenSIL.cpp

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2589,9 +2589,107 @@ getPartialApplicationFunction(IRGenSILFunction &IGF, SILValue v,
25892589
llvm_unreachable("bad kind");
25902590
}
25912591

2592+
// A "simple" partial_apply is one where the argument can be directly
2593+
// adopted as the context of the result closure.
2594+
static bool isSimplePartialApply(IRGenFunction &IGF, PartialApplyInst *i) {
2595+
// The callee type must use the `method` convention.
2596+
auto calleeTy = i->getCallee()->getType().castTo<SILFunctionType>();
2597+
auto resultTy = i->getFunctionType();
2598+
2599+
if (calleeTy->getRepresentation() != SILFunctionTypeRepresentation::Method)
2600+
return false;
2601+
2602+
// There should be one applied argument.
2603+
// (This is a bit stricter than necessary, because empty arguments could be
2604+
// ignored, and for noescape closures, any amount of data less than a pointer
2605+
// in size can be blobbed into a single context word, but those will be
2606+
// handled by a simplification pass in SIL.)
2607+
if (i->getNumArguments() != 1)
2608+
return false;
2609+
2610+
auto appliedParam = calleeTy->getParameters().back();
2611+
if (resultTy->isNoEscape()) {
2612+
// A trivial closure accepts an unowned or guaranteed argument, possibly
2613+
// direct or indirect.
2614+
switch (appliedParam.getConvention()) {
2615+
case ParameterConvention::Indirect_Inout:
2616+
case ParameterConvention::Indirect_In_Constant:
2617+
case ParameterConvention::Indirect_In_Guaranteed:
2618+
case ParameterConvention::Indirect_InoutAliasable:
2619+
// Indirect arguments are trivially word sized.
2620+
return true;
2621+
2622+
case ParameterConvention::Direct_Guaranteed:
2623+
case ParameterConvention::Direct_Unowned: {
2624+
// Is the direct argument a single word-sized value?
2625+
auto argSchema = IGF.IGM.getTypeInfo(i->getArgument(0)->getType())
2626+
.getSchema();
2627+
if (argSchema.size() != 1)
2628+
return false;
2629+
2630+
if (argSchema[0].getScalarType()->getPrimitiveSizeInBits()
2631+
!= IGF.IGM.getPointerSize().getValueInBits())
2632+
return false;
2633+
2634+
return true;
2635+
}
2636+
default:
2637+
return false;
2638+
}
2639+
} else {
2640+
// An escaping closure argument's convention should match the callee
2641+
// convention of the result.
2642+
if (resultTy->getCalleeConvention() != appliedParam.getConvention()) {
2643+
return false;
2644+
}
2645+
assert(!isIndirectFormalParameter(resultTy->getCalleeConvention()));
2646+
2647+
auto &argInfo = IGF.IGM.getTypeInfo(i->getArgument(0)->getType());
2648+
2649+
if (!argInfo.isSingleSwiftRetainablePointer(ResilienceExpansion::Maximal))
2650+
return false;
2651+
2652+
return true;
2653+
}
2654+
}
2655+
25922656
void IRGenSILFunction::visitPartialApplyInst(swift::PartialApplyInst *i) {
25932657
SILValue v(i);
25942658

2659+
if (isSimplePartialApply(*this, i)) {
2660+
Explosion function;
2661+
2662+
auto schema = IGM.getTypeInfo(v->getType()).getSchema();
2663+
assert(schema.size() == 2);
2664+
auto calleeTy = schema[0].getScalarType();
2665+
auto contextTy = schema[1].getScalarType();
2666+
2667+
auto callee = getLoweredExplosion(i->getCallee());
2668+
auto calleeValue = callee.claimNext();
2669+
assert(callee.empty());
2670+
calleeValue = Builder.CreateBitOrPointerCast(calleeValue, calleeTy);
2671+
function.add(calleeValue);
2672+
2673+
Explosion context;
2674+
for (auto arg : i->getArguments()) {
2675+
auto &value = getLoweredValue(arg);
2676+
2677+
if (value.isAddress()) {
2678+
context.add(value.getAnyAddress().getAddress());
2679+
} else {
2680+
getLoweredExplosion(arg, context);
2681+
}
2682+
}
2683+
auto contextValue = context.claimNext();
2684+
assert(context.empty());
2685+
contextValue = Builder.CreateBitOrPointerCast(contextValue, contextTy);
2686+
function.add(contextValue);
2687+
2688+
setLoweredExplosion(v, function);
2689+
return;
2690+
}
2691+
2692+
25952693
// NB: We collect the arguments under the substituted type.
25962694
auto args = i->getArguments();
25972695
auto calleeTy = i->getSubstCalleeType();

test/IRGen/simple_partial_apply.sil

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// RUN: %target-swift-frontend -emit-ir %s | %FileCheck %s
2+
3+
sil_stage canonical
4+
5+
import Swift
6+
7+
class C {}
8+
9+
sil_vtable C {}
10+
11+
struct SingleRefcounted {
12+
var c: C
13+
}
14+
15+
// CHECK-LABEL: define {{.*}} @escape_partial_apply_swift_class
16+
// CHECK: [[FPTR:%.*]] = insertvalue { i8*, %swift.refcounted* } undef, i8* %0, 0
17+
// CHECK-NEXT: [[FCTX:%.*]] = insertvalue { i8*, %swift.refcounted* } [[FPTR]], %swift.refcounted* {{.*}}, 1
18+
// CHECK-NEXT: ret { i8*, %swift.refcounted* } [[FCTX]]
19+
sil @escape_partial_apply_swift_class : $@convention(thin) (@convention(method) (Int, @guaranteed C) -> Int, @guaranteed C) -> @callee_guaranteed (Int) -> Int {
20+
entry(%body : $@convention(method) (Int, @guaranteed C) -> Int, %context : $C):
21+
%closure = partial_apply [callee_guaranteed] %body(%context) : $@convention(method) (Int, @guaranteed C) -> Int
22+
return %closure : $@callee_guaranteed (Int) -> Int
23+
}
24+
25+
// CHECK-LABEL: define {{.*}} @escape_partial_apply_swift_single_refcount_struct
26+
// CHECK: [[FPTR:%.*]] = insertvalue { i8*, %swift.refcounted* } undef, i8* %0, 0
27+
// CHECK-NEXT: [[FCTX:%.*]] = insertvalue { i8*, %swift.refcounted* } [[FPTR]], %swift.refcounted* {{.*}}, 1
28+
// CHECK-NEXT: ret { i8*, %swift.refcounted* } [[FCTX]]
29+
sil @escape_partial_apply_swift_single_refcount_struct : $@convention(thin) (@convention(method) (Int, @guaranteed SingleRefcounted) -> Int, @guaranteed SingleRefcounted) -> @callee_guaranteed (Int) -> Int {
30+
entry(%body : $@convention(method) (Int, @guaranteed SingleRefcounted) -> Int, %context : $SingleRefcounted):
31+
%closure = partial_apply [callee_guaranteed] %body(%context) : $@convention(method) (Int, @guaranteed SingleRefcounted) -> Int
32+
return %closure : $@callee_guaranteed (Int) -> Int
33+
}
34+
35+
// CHECK-LABEL: define {{.*}} @noescape_partial_apply_swift_indirect
36+
// CHECK: [[CTX:%.*]] = bitcast {{.*}}** %1 to %swift.opaque*
37+
// CHECK-NEXT: [[CONT:%.*]] = bitcast i8* %2
38+
// CHECK-NEXT: call {{.*}}void [[CONT]](i8* %0, %swift.opaque* [[CTX]], %swift.refcounted* {{.*}}%3)
39+
sil @noescape_partial_apply_swift_indirect : $@convention(thin) (@convention(method) (Int, @in_guaranteed C) -> Int, @in_guaranteed C, @guaranteed @callee_guaranteed (@noescape @callee_guaranteed (Int) -> Int) -> ()) -> () {
40+
entry(%body : $@convention(method) (Int, @in_guaranteed C) -> Int, %context : $*C, %cont : $@callee_guaranteed (@noescape @callee_guaranteed (Int) -> Int) -> ()):
41+
%closure = partial_apply [callee_guaranteed] [on_stack] %body(%context) : $@convention(method) (Int, @in_guaranteed C) -> Int
42+
%x = apply %cont(%closure) : $@callee_guaranteed (@noescape @callee_guaranteed (Int) -> Int) -> ()
43+
dealloc_stack %closure : $@noescape @callee_guaranteed (Int) -> Int
44+
return undef : $()
45+
}
46+
47+
// CHECK-LABEL: define {{.*}} @noescape_partial_apply_swift_direct_word
48+
// CHECK: [[CTX:%.*]] = inttoptr i{{.*}} %1 to %swift.opaque*
49+
// CHECK-NEXT: [[CONT:%.*]] = bitcast i8* %2
50+
// CHECK-NEXT: call {{.*}}void [[CONT]](i8* %0, %swift.opaque* [[CTX]], %swift.refcounted* {{.*}}%3)
51+
sil @noescape_partial_apply_swift_direct_word : $@convention(thin) (@convention(method) (Int, Int) -> Int, Int, @guaranteed @callee_guaranteed (@noescape @callee_guaranteed (Int) -> Int) -> ()) -> () {
52+
entry(%body : $@convention(method) (Int, Int) -> Int, %context : $Int, %cont : $@callee_guaranteed (@noescape @callee_guaranteed (Int) -> Int) -> ()):
53+
%closure = partial_apply [callee_guaranteed] [on_stack] %body(%context) : $@convention(method) (Int, Int) -> Int
54+
%x = apply %cont(%closure) : $@callee_guaranteed (@noescape @callee_guaranteed (Int) -> Int) -> ()
55+
dealloc_stack %closure : $@noescape @callee_guaranteed (Int) -> Int
56+
return undef : $()
57+
}

0 commit comments

Comments
 (0)