Skip to content

Commit d61b923

Browse files
authored
Merge pull request #18077 from eeckstein/closure-specializer
ClosureSpecializer: extend the benefit-analysis so that specialization is also done in case a closure is passed through multiple call-levels.
2 parents ac1738f + 7d88353 commit d61b923

File tree

3 files changed

+73
-34
lines changed

3 files changed

+73
-34
lines changed

include/swift/SIL/SILInstruction.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7683,11 +7683,17 @@ class ApplySite {
76837683
}
76847684
}
76857685

7686+
/// Returns true if \p oper is an argument operand and not the callee
7687+
/// operand.
7688+
bool isArgumentOperand(const Operand &oper) {
7689+
return oper.getOperandNumber() >= getOperandIndexOfFirstArgument();
7690+
}
7691+
76867692
// Translate the index of the argument to the full apply or partial_apply into
76877693
// to the corresponding index into the arguments of the called function.
76887694
unsigned getCalleeArgIndex(const Operand &oper) {
76897695
assert(oper.getUser() == Inst);
7690-
assert(oper.getOperandNumber() >= getOperandIndexOfFirstArgument());
7696+
assert(isArgumentOperand(oper));
76917697

76927698
unsigned appliedArgIdx =
76937699
oper.getOperandNumber() - getOperandIndexOfFirstArgument();

lib/SILOptimizer/IPO/ClosureSpecializer.cpp

Lines changed: 43 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -908,6 +908,37 @@ static void markReabstractionPartialApplyAsUsed(
908908
llvm_unreachable("Unexpect instruction");
909909
}
910910

911+
/// Returns true if the \p closureArgIdx argument of \p callee is called in
912+
/// \p callee or any function called by callee.
913+
static bool isClosureAppliedIn(SILFunction *Callee, unsigned closureArgIdx,
914+
SmallPtrSetImpl<SILFunction *> &HandledFuncs) {
915+
// Limit the number of recursive calls to not go into exponential behavior in
916+
// corner cases.
917+
const int RecursionBudget = 8;
918+
919+
SILValue Arg = Callee->getArgument(closureArgIdx);
920+
for (Operand *ArgUse : Arg->getUses()) {
921+
if (auto UserAI = FullApplySite::isa(ArgUse->getUser())) {
922+
if (UserAI.getCallee() == Arg)
923+
return true;
924+
925+
assert(UserAI.isArgumentOperand(*ArgUse) &&
926+
"any other non-argument operands than the callee?");
927+
928+
SILFunction *ApplyCallee = UserAI.getReferencedFunction();
929+
if (ApplyCallee && !ApplyCallee->isExternalDeclaration() &&
930+
HandledFuncs.count(ApplyCallee) == 0 &&
931+
HandledFuncs.size() < RecursionBudget) {
932+
HandledFuncs.insert(ApplyCallee);
933+
if (isClosureAppliedIn(UserAI.getReferencedFunction(),
934+
UserAI.getCalleeArgIndex(*ArgUse), HandledFuncs))
935+
return true;
936+
}
937+
}
938+
}
939+
return false;
940+
}
941+
911942
bool SILClosureSpecializerTransform::gatherCallSites(
912943
SILFunction *Caller,
913944
llvm::SmallVectorImpl<ClosureInfo*> &ClosureCandidates,
@@ -1018,43 +1049,23 @@ bool SILClosureSpecializerTransform::gatherCallSites(
10181049
if (mayBindDynamicSelf(ApplyCallee))
10191050
return CFGChanged;
10201051

1021-
// Ok, we know that we can perform the optimization but not whether or
1022-
// not the optimization is profitable. Find the index of the argument
1023-
// corresponding to our partial apply.
1024-
Optional<unsigned> ClosureIndex;
1025-
for (unsigned i = 0, e = AI.getNumArguments(); i != e; ++i) {
1026-
if (AI.getArgument(i) != Use->get())
1027-
continue;
1028-
ClosureIndex = i;
1029-
DEBUG(llvm::dbgs() << " Found callsite with closure argument at "
1030-
<< i << ": " << *AI.getInstruction());
1031-
break;
1032-
}
1033-
1034-
// If we did not find an index, there is nothing further to do,
1035-
// continue.
1036-
if (!ClosureIndex.hasValue())
1052+
// Check if the closure is passed as an argument (and not called).
1053+
if (!AI.isArgumentOperand(*Use))
10371054
continue;
10381055

1039-
// Make sure that the Closure is invoked in the Apply's callee. We only
1040-
// want to perform closure specialization if we know that we will be
1041-
// able to change a partial_apply into an apply.
1042-
//
1043-
// TODO: Maybe just call the function directly instead of moving the
1044-
// partial apply?
1045-
SILValue Arg = ApplyCallee->getArgument(ClosureIndex.getValue());
1046-
if (std::none_of(Arg->use_begin(), Arg->use_end(),
1047-
[&Arg](Operand *Op) -> bool {
1048-
auto UserAI = FullApplySite::isa(Op->getUser());
1049-
return UserAI && UserAI.getCallee() == Arg;
1050-
})) {
1056+
unsigned ClosureIndex = AI.getCalleeArgIndex(*Use);
1057+
1058+
// Ok, we know that we can perform the optimization but not whether or
1059+
// not the optimization is profitable. Check if the closure is actually
1060+
// called in the callee (or in a function called by the callee).
1061+
SmallPtrSet<SILFunction *, 8> HandledFuncs;
1062+
if (!isClosureAppliedIn(ApplyCallee, ClosureIndex, HandledFuncs))
10511063
continue;
1052-
}
10531064

10541065
unsigned firstParamArgIdx =
10551066
AI.getSubstCalleeConv().getSILArgIndexOfFirstParam();
1056-
assert(ClosureIndex.getValue() >= firstParamArgIdx);
1057-
auto ClosureParamIndex = ClosureIndex.getValue() - firstParamArgIdx;
1067+
assert(ClosureIndex >= firstParamArgIdx);
1068+
auto ClosureParamIndex = ClosureIndex - firstParamArgIdx;
10581069

10591070
auto ParamInfo = AI.getSubstCalleeType()->getParameters();
10601071
SILParameterInfo ClosureParamInfo = ParamInfo[ClosureParamIndex];
@@ -1097,7 +1108,7 @@ bool SILClosureSpecializerTransform::gatherCallSites(
10971108
// Now we know that CSDesc is profitable to specialize. Add it to our
10981109
// call site list.
10991110
CInfo->CallSites.push_back(
1100-
CallSiteDescriptor(CInfo, AI, ClosureIndex.getValue(),
1111+
CallSiteDescriptor(CInfo, AI, ClosureIndex,
11011112
ClosureParamInfo, std::move(NonFailureExitBBs)));
11021113
}
11031114
if (CInfo) {

test/SILOptimizer/closure_specialize_simple.sil

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,23 @@ bb2:
2626
return %2 : $Builtin.Int1
2727
}
2828

29+
// CHECK-LABEL: sil shared @$S37simple_partial_apply_2nd_level_caller0a1_b1_C4_funBi1_Tf1c_n : $@convention(thin) (Builtin.Int1) -> Builtin.Int1 {
30+
// CHECK: bb0([[CAPTURED_ARG:%.*]] : $Builtin.Int1):
31+
// CHECK: [[SPECIALIZED_CALLEE:%.*]] = function_ref @$S27simple_partial_apply_caller0a1_b1_C4_funBi1_Tf1c_n :
32+
// CHECK: [[RET:%.*]]= apply [[SPECIALIZED_CALLEE]]([[CAPTURED_ARG]])
33+
// CHECK: return [[RET]]
34+
sil @simple_partial_apply_2nd_level_caller : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1 {
35+
bb0(%0 : $@callee_owned (Builtin.Int1) -> Builtin.Int1):
36+
br bb1
37+
38+
bb1:
39+
%1 = function_ref @simple_partial_apply_caller : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1
40+
%2 = apply %1(%0) : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1
41+
cond_br undef, bb1, bb2
42+
43+
bb2:
44+
return %2 : $Builtin.Int1
45+
}
2946
sil @simple_partial_apply_caller_decl : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1
3047

3148
sil @simple_multiple_partial_apply_caller : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1, @owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1 {
@@ -177,8 +194,10 @@ bb2:
177194
}
178195

179196
// CHECK-LABEL: sil @loop_driver : $@convention(thin) (Builtin.Int1, Builtin.Int1) -> () {
180-
// CHECK: [[SPECIALIZED_FUN:%.*]] = function_ref @$S27simple_partial_apply_caller0a1_b1_C4_funBi1_Tf1c_n : $@convention(thin) (Builtin.Int1) -> Builtin.Int1
197+
// CHECK-DAG: [[SPECIALIZED_FUN:%.*]] = function_ref @$S27simple_partial_apply_caller0a1_b1_C4_funBi1_Tf1c_n : $@convention(thin) (Builtin.Int1) -> Builtin.Int1
198+
// CHECK-DAG: [[SPECIALIZED_FUN2:%.*]] = function_ref @$S37simple_partial_apply_2nd_level_caller0a1_b1_C4_funBi1_Tf1c_n : $@convention(thin) (Builtin.Int1) -> Builtin.Int1
181199
// CHECK: apply [[SPECIALIZED_FUN]]
200+
// CHECK: apply [[SPECIALIZED_FUN2]]
182201

183202
// We can't call this one b/c it is just a declaration.
184203
// CHECK: [[UNSPECIALIZED_FUN_DECL:%.*]] = function_ref @simple_partial_apply_caller_decl : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1
@@ -218,6 +237,9 @@ bb0(%0 : $Builtin.Int1, %1 : $Builtin.Int1):
218237
%4 = function_ref @simple_partial_apply_caller : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1
219238
%5 = apply %4(%3) : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1
220239

240+
%51 = function_ref @simple_partial_apply_2nd_level_caller : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1
241+
%52 = apply %51(%3) : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1
242+
221243
%6 = partial_apply %2(%0) : $@convention(thin) (Builtin.Int1, Builtin.Int1) -> Builtin.Int1
222244
%7 = function_ref @simple_partial_apply_caller_decl : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1
223245
%8 = apply %7(%6) : $@convention(thin) (@owned @callee_owned (Builtin.Int1) -> Builtin.Int1) -> Builtin.Int1

0 commit comments

Comments
 (0)