Skip to content

Commit 8990ed4

Browse files
committed
Fix closure inlining after witness devirtualization.
Certain patterns of directly applied partial_apply's were not being inlined. This can happen when a closure is defined in the implementation of a generic witness method. I noticed this issue while debugging SR-6254: "Fix (or explain) strange ways to make code >3 times faster/slower". After the witness method is devirtualization and specialized, the closure application looks like this: %pa = partial_apply [callee_guaranteed] %fn() : $@convention(thin) (@in Int) -> @out Int %cvt = convert_escape_to_noescape %pa apply %cvt(...) SILCombine already removes the partial_apply and convert_escape_to_noescape generating: %thick = thin_to_thick %fn apply %thick(...) However, surprisingly, neither the inliner nor SILCombine can handle this. I cleaned up the code in SILCombine's apply visitor that handles thin_to_thick and generalized it to handle this and other patterns. I also added a restriction to this code so that it doesn't break SIL ownership in the future, as I discussed with Arnold.
1 parent 805c648 commit 8990ed4

File tree

4 files changed

+89
-30
lines changed

4 files changed

+89
-30
lines changed

lib/SILOptimizer/SILCombiner/SILCombiner.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,9 @@ class SILCombiner :
283283
SILValue ConcreteTypeDef,
284284
ProtocolConformanceRef Conformance,
285285
ArchetypeType *OpenedArchetype);
286+
287+
FullApplySite rewriteApplyCallee(FullApplySite apply, SILValue callee);
288+
286289
SILInstruction *
287290
propagateConcreteTypeOfInitExistential(FullApplySite AI,
288291
ProtocolDecl *Protocol,

lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1305,6 +1305,27 @@ bool SILCombiner::optimizeIdentityCastComposition(ApplyInst *FInverse,
13051305
return true;
13061306
}
13071307

1308+
// Return a new apply with the specified callee. This creates a new apply rather
1309+
// than simply rewriting the callee operand because the apply's SubstCalleeType,
1310+
// derived from the callee and substitution list, may change.
1311+
FullApplySite SILCombiner::rewriteApplyCallee(FullApplySite apply,
1312+
SILValue callee) {
1313+
SmallVector<SILValue, 4> arguments;
1314+
for (SILValue arg : apply.getArguments())
1315+
arguments.push_back(arg);
1316+
1317+
Builder.addOpenedArchetypeOperands(apply.getInstruction());
1318+
if (auto *TAI = dyn_cast<TryApplyInst>(apply)) {
1319+
return Builder.createTryApply(TAI->getLoc(), callee,
1320+
TAI->getSubstitutions(), arguments,
1321+
TAI->getNormalBB(), TAI->getErrorBB());
1322+
} else {
1323+
return Builder.createApply(apply.getLoc(), callee, apply.getSubstitutions(),
1324+
arguments,
1325+
cast<ApplyInst>(apply)->isNonThrowing());
1326+
}
1327+
}
1328+
13081329
SILInstruction *SILCombiner::visitApplyInst(ApplyInst *AI) {
13091330
Builder.setCurrentDebugScope(AI->getDebugScope());
13101331
// apply{partial_apply(x,y)}(z) -> apply(z,x,y) is triggered
@@ -1346,20 +1367,11 @@ SILInstruction *SILCombiner::visitApplyInst(ApplyInst *AI) {
13461367

13471368
// (apply (thin_to_thick_function f)) to (apply f)
13481369
if (auto *TTTFI = dyn_cast<ThinToThickFunctionInst>(AI->getCallee())) {
1349-
// TODO: Handle substitutions and indirect results
1350-
if (AI->hasSubstitutions() || AI->hasIndirectResults())
1351-
return nullptr;
1352-
SmallVector<SILValue, 4> Arguments;
1353-
for (auto &Op : AI->getArgumentOperands()) {
1354-
Arguments.push_back(Op.get());
1355-
}
1356-
// The type of the substitution is the source type of the thin to thick
1357-
// instruction.
1358-
Builder.addOpenedArchetypeOperands(AI);
1359-
auto *NewAI = Builder.createApply(AI->getLoc(), TTTFI->getOperand(),
1360-
AI->getSubstitutions(), Arguments,
1361-
AI->isNonThrowing());
1362-
return NewAI;
1370+
// We currently don't remove any possible retain associated with the thick
1371+
// function when rewriting the callsite. This should be ok because the
1372+
// ABI normally expects a guaranteed callee.
1373+
if (!AI->getOrigCalleeType()->isCalleeConsumed())
1374+
return rewriteApplyCallee(AI, TTTFI->getOperand()).getInstruction();
13631375
}
13641376

13651377
// (apply (witness_method)) -> propagate information about
@@ -1477,21 +1489,12 @@ SILInstruction *SILCombiner::visitTryApplyInst(TryApplyInst *AI) {
14771489

14781490
// (try_apply (thin_to_thick_function f)) to (try_apply f)
14791491
if (auto *TTTFI = dyn_cast<ThinToThickFunctionInst>(AI->getCallee())) {
1480-
// TODO: Handle substitutions and indirect results
1481-
if (AI->hasSubstitutions() || AI->hasIndirectResults())
1482-
return nullptr;
1483-
SmallVector<SILValue, 4> Arguments;
1484-
for (auto &Op : AI->getArgumentOperands()) {
1485-
Arguments.push_back(Op.get());
1486-
}
1487-
// The type of the substitution is the source type of the thin to thick
1488-
// instruction.
1489-
auto *NewAI = Builder.createTryApply(AI->getLoc(), TTTFI->getOperand(),
1490-
AI->getSubstitutions(), Arguments,
1491-
AI->getNormalBB(), AI->getErrorBB());
1492-
return NewAI;
1492+
// We currently don't remove any possible retain associated with the thick
1493+
// function when rewriting the callsite. This should be ok because the
1494+
// ABI normally expects a guaranteed callee.
1495+
if (!AI->getOrigCalleeType()->isCalleeConsumed())
1496+
return rewriteApplyCallee(AI, TTTFI->getOperand()).getInstruction();
14931497
}
1494-
14951498
// (apply (witness_method)) -> propagate information about
14961499
// a concrete type from init_existential_addr or init_existential_ref.
14971500
if (auto *WMI = dyn_cast<WitnessMethodInst>(AI->getCallee())) {

test/SILOptimizer/sil_combine.sil

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1522,8 +1522,8 @@ bb0:
15221522
sil @eliminate_thin_to_thick_apply : $@convention(thin) () -> () {
15231523
bb0:
15241524
%0 = function_ref @eliminate_dead_thin_to_thick_function_fun : $@convention(thin) () -> ()
1525-
%1 = thin_to_thick_function %0 : $@convention(thin) () -> () to $@callee_owned () -> ()
1526-
%2 = apply %1() : $@callee_owned () -> ()
1525+
%1 = thin_to_thick_function %0 : $@convention(thin) () -> () to $@callee_guaranteed () -> ()
1526+
%2 = apply %1() : $@callee_guaranteed () -> ()
15271527
return %2 : $()
15281528
}
15291529

test/SILOptimizer/sil_combine_apply.sil

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,3 +517,56 @@ bb0:
517517
%return = tuple ()
518518
return %return : $()
519519
}
520+
521+
sil shared [transparent] [thunk] @genericClosure : $@convention(thin) <T> (@in T) -> @out T {
522+
bb0(%0 : $*T, %1 : $*T):
523+
%12 = tuple ()
524+
return %12 : $()
525+
}
526+
527+
// CHECK-LABEL: sil shared @genericThinToThick : $@convention(thin) () -> ()
528+
// CHECK: [[F:%.*]] = function_ref @genericClosure : $@convention(thin) <τ_0_0> (@in τ_0_0) -> @out τ_0_0
529+
// CHECK: apply [[F]]<Builtin.Int64>(%{{.*}}, %{{.*}}) : $@convention(thin) <τ_0_0> (@in τ_0_0) -> @out τ_0_0
530+
// CHECK-LABEL: } // end sil function 'genericThinToThick'
531+
sil shared @genericThinToThick : $@convention(thin) () -> () {
532+
bb0:
533+
%fn = function_ref @genericClosure : $@convention(thin) <T> (@in T) -> @out T
534+
%thick = thin_to_thick_function %fn : $@convention(thin) <T> (@in T) -> @out T to $@noescape @callee_guaranteed <T> (@in T) -> @out T
535+
%in = alloc_stack $Builtin.Int64
536+
%out = alloc_stack $Builtin.Int64
537+
%c3 = integer_literal $Builtin.Int64, 3
538+
store %c3 to %in : $*Builtin.Int64
539+
%call = apply %thick<Builtin.Int64>(%out, %in) : $@noescape @callee_guaranteed <T> (@in T) -> @out T
540+
dealloc_stack %out : $*Builtin.Int64
541+
dealloc_stack %in : $*Builtin.Int64
542+
%999 = tuple ()
543+
return %999 : $()
544+
}
545+
546+
sil shared [transparent] [thunk] @indirectClosure : $@convention(thin) (@in Builtin.Int64) -> @out Builtin.Int64 {
547+
bb0(%0 : $*Builtin.Int64, %1 : $*Builtin.Int64):
548+
%val = load %1 : $*Builtin.Int64
549+
store %val to %0 : $*Builtin.Int64
550+
%999 = tuple ()
551+
return %999 : $()
552+
}
553+
554+
// CHECK-LABEL: sil shared @appliedEscapeToNoEscape : $@convention(thin) () -> () {
555+
// CHECK: [[F:%.*]] = function_ref @indirectClosure : $@convention(thin) (@in Builtin.Int64) -> @out Builtin.Int64 // user: %5
556+
// CHECK: apply [[F]](%{{.*}}, %{{.*}}) : $@convention(thin) (@in Builtin.Int64) -> @out Builtin.Int64
557+
sil shared @appliedEscapeToNoEscape : $@convention(thin) () -> () {
558+
bb0:
559+
%fn = function_ref @indirectClosure : $@convention(thin) (@in Builtin.Int64) -> @out Builtin.Int64
560+
%pa = partial_apply [callee_guaranteed] %fn() : $@convention(thin) (@in Builtin.Int64) -> @out Builtin.Int64
561+
%cvt = convert_escape_to_noescape %pa : $@callee_guaranteed (@in Builtin.Int64) -> @out Builtin.Int64 to $@noescape @callee_guaranteed (@in Builtin.Int64) -> @out Builtin.Int64
562+
%out = alloc_stack $Builtin.Int64
563+
%in = alloc_stack $Builtin.Int64
564+
%c3 = integer_literal $Builtin.Int64, 3
565+
store %c3 to %in : $*Builtin.Int64
566+
%call = apply %cvt(%out, %in) : $@noescape @callee_guaranteed (@in Builtin.Int64) -> @out Builtin.Int64
567+
dealloc_stack %in : $*Builtin.Int64
568+
dealloc_stack %out : $*Builtin.Int64
569+
strong_release %pa : $@callee_guaranteed (@in Builtin.Int64) -> @out Builtin.Int64
570+
%999 = tuple ()
571+
return %999 : $()
572+
}

0 commit comments

Comments
 (0)