@@ -45,8 +45,10 @@ STATISTIC(NumInvocationFunctionsChanged,
45
45
" Number of invocation functions rewritten" );
46
46
STATISTIC (NumUnsupportedChangesToInvocationFunctions,
47
47
" Number of invocation functions that could be rewritten, but aren't yet" );
48
- STATISTIC (NumPartialApplyCalleesWithNonPartialApplyUses,
49
- " Number of invocation functions with non-partial_apply uses" );
48
+ STATISTIC (NumPartialApplyCalleesWithNonApplyUses,
49
+ " Number of invocation functions with non-apply uses" );
50
+ STATISTIC (NumPartialApplyCalleesWithEscapingAndApplyUses,
51
+ " Number of invocation functions with both escaping and full apply uses" );
50
52
STATISTIC (NumPartialApplyCalleesPossiblyUsedExternally,
51
53
" Number of invocation functions possibly used externally" );
52
54
STATISTIC (NumPartialApplyCalleesDeclarationOnly,
@@ -69,8 +71,11 @@ struct KnownCallee {
69
71
llvm::SetVector<FunctionRefInst *> FunctionRefs;
70
72
// / The set of partial application sites.
71
73
llvm::SetVector<PartialApplyInst *> PartialApplications;
72
- // / If the callee has a non-partial-apply use, this points to an arbitrary one.
73
- SILInstruction *NonPartialApplyUse = nullptr ;
74
+ // / The set of full application sites.
75
+ llvm::SetVector<FullApplySite> FullApplications;
76
+ // / If the callee has a non-partial-apply, non-apply use, this points to an
77
+ // / arbitrary one, for logging purposes.
78
+ SILInstruction *NonApplyUse = nullptr ;
74
79
};
75
80
76
81
class PartialApplySimplificationPass : public SILModuleTransform {
@@ -115,15 +120,20 @@ class PartialApplySimplificationPass : public SILModuleTransform {
115
120
// / out of the single applied argument
116
121
// / - if the partial application is noescape:
117
122
// / - the argument is word-sized or smaller
118
- // / - the argument is either trivial, or passed with a +0 convention
119
- // / (guaranteed, unowned, in_guaranteed)
123
+ // / - the argument is either trivial, or passed with a net +0 convention
124
+ // / (guaranteed, unowned, in_guaranteed, inout )
120
125
// / - if the partial application is escapable:
121
126
// / - the argument is either a single Swift-refcounted word, or trivial and
122
127
// / sized strictly less than one word
123
128
// / - the argument ownership convention matches the callee convention of the
124
129
// / resulting function
125
130
static bool isSimplePartialApply (PartialApplyInst *i) {
126
131
auto calleeTy = i->getCallee ()->getType ().castTo <SILFunctionType>();
132
+ if (calleeTy->isPolymorphic ()) {
133
+ // TODO: Check if the "self" parameter provides the generic environment
134
+ return false ;
135
+ }
136
+
127
137
if (calleeTy->getRepresentation () != SILFunctionTypeRepresentation::Method) {
128
138
return false ;
129
139
}
@@ -136,10 +146,24 @@ static bool isSimplePartialApply(PartialApplyInst *i) {
136
146
auto argTy = i->getArguments ()[0 ]->getType ();
137
147
138
148
if (i->getFunctionType ()->isNoEscape ()) {
139
- if (argTy.isAddress ()) {
149
+ switch (calleeTy->getSelfParameter ().getConvention ()) {
150
+ case ParameterConvention::Indirect_Inout:
151
+ case ParameterConvention::Indirect_In_Constant:
152
+ case ParameterConvention::Indirect_In_Guaranteed:
153
+ case ParameterConvention::Indirect_InoutAliasable:
154
+ // Indirect arguments are trivially word sized.
140
155
return true ;
156
+
157
+ case ParameterConvention::Direct_Guaranteed:
158
+ case ParameterConvention::Direct_Unowned:
159
+ // TODO: Handle word-sized direct arguments.
160
+ return false ;
161
+
162
+ // +1 arguments need a thunk to stage a copy for the callee to consume.
163
+ case ParameterConvention::Direct_Owned:
164
+ case ParameterConvention::Indirect_In:
165
+ return false ;
141
166
}
142
- return false ;
143
167
} else {
144
168
if (!argTy.isObject ()) {
145
169
return false ;
@@ -175,8 +199,14 @@ void PartialApplySimplificationPass::scanFunction(SILFunction *f,
175
199
continue ;
176
200
}
177
201
202
+ // Collect full apply sites for potential transformation as well.
203
+ if (auto fa = FullApplySite::isa (frUse->getUser ())) {
204
+ knownCallee.FullApplications .insert (fa);
205
+ continue ;
206
+ }
207
+
178
208
// Record if the function has uses that aren't partial applies.
179
- knownCallee.NonPartialApplyUse = frUse->getUser ();
209
+ knownCallee.NonApplyUse = frUse->getUser ();
180
210
}
181
211
}
182
212
@@ -207,10 +237,10 @@ void PartialApplySimplificationPass::processKnownCallee(SILFunction *callee,
207
237
208
238
// If the subject of the partial application has other uses that aren't
209
239
// partial applications, then thunk it.
210
- if (pa.NonPartialApplyUse ) {
211
- LLVM_DEBUG (llvm::dbgs () << " Callee has non-partial_apply uses; thunking\n " ;
212
- pa.NonPartialApplyUse ->print (llvm::dbgs ()));
213
- ++NumPartialApplyCalleesWithNonPartialApplyUses ;
240
+ if (pa.NonApplyUse ) {
241
+ LLVM_DEBUG (llvm::dbgs () << " Callee has non-apply uses; thunking\n " ;
242
+ pa.NonApplyUse ->print (llvm::dbgs ()));
243
+ ++NumPartialApplyCalleesWithNonApplyUses ;
214
244
goto create_forwarding_thunks;
215
245
}
216
246
@@ -265,6 +295,22 @@ void PartialApplySimplificationPass::processKnownCallee(SILFunction *callee,
265
295
LLVM_DEBUG (llvm::dbgs () << " And they're already simple, don't need to do anything!\n " );
266
296
return ;
267
297
}
298
+
299
+ // TODO: Check if the partial_apply would become simple if we only change
300
+ // the callee type to convention(method).
301
+
302
+ // If the partial applications form escaping closures, and there are also
303
+ // full application sites, then we don't want to burden those full
304
+ // application sites with having to allocate a box for the captured arguments.
305
+ // Emit a thunk for the partial application sites.
306
+ //
307
+ // TODO: Evaluate if stack-allocating the escapable box is acceptable.
308
+ if (!examplePA->isOnStack () && !pa.FullApplications .empty ()) {
309
+ LLVM_DEBUG (llvm::dbgs () << " Callee has mix of escaping partial_apply and full application sites; thunking:\n " ;
310
+ pa.FullApplications .front ().getInstruction ()->print (llvm::dbgs ()));
311
+ ++NumPartialApplyCalleesWithEscapingAndApplyUses;
312
+ goto create_forwarding_thunks;
313
+ }
268
314
269
315
// Rewrite the function type to take the captures in box form.
270
316
auto origTy = callee->getLoweredFunctionType ();
@@ -529,28 +575,44 @@ void PartialApplySimplificationPass::processKnownCallee(SILFunction *callee,
529
575
}
530
576
531
577
// Rewrite partial applications to partially apply the new clone.
532
- for ( auto pa : pa. PartialApplications ) {
533
- auto caller = pa ->getFunction ();
578
+ auto rewriteApplySite = [&](ApplySite site ) {
579
+ auto caller = site ->getFunction ();
534
580
SILBuilder B (*caller);
535
- auto loc = pa ->getLoc ();
536
- B.setInsertionPoint (pa );
581
+ auto loc = site ->getLoc ();
582
+ B.setInsertionPoint (site. getInstruction () );
537
583
538
584
auto newFunctionRef = B.createFunctionRef (loc, callee);
539
585
SILValue contextBuffer, contextProj;
540
586
auto contextStorageTy = SILType::getPrimitiveAddressType (contextTy)
541
- .subst (getModule ()->Types , pa-> getSubstitutionMap ());
587
+ .subst (getModule ()->Types , site. getSubstitutionMap ());
542
588
if (isNoEscape) {
543
589
auto contextAlloc = B.createAllocStack (loc, contextStorageTy);
544
590
contextBuffer = contextProj = contextAlloc;
545
591
546
- // We'll need to deallocate the context buffer after the end of the
547
- // original partial_apply's lifetime.
548
- auto deallocStackUses = pa->getUsersOfType <DeallocStackInst>();
549
- assert (deallocStackUses.begin () != deallocStackUses.end ());
550
- for (auto use : deallocStackUses) {
551
- B.setInsertionPoint (use->getNextInstruction ());
592
+ // We'll need to deallocate the context buffer after we don't need it.
593
+ // For a partial_apply, that's after the partial_apply itself is
594
+ // deallocated.
595
+ if (auto ppa = dyn_cast<PartialApplyInst>(site.getInstruction ())) {
596
+ auto deallocStackUses = ppa->getUsersOfType <DeallocStackInst>();
597
+ assert (deallocStackUses.begin () != deallocStackUses.end ());
598
+ for (auto use : deallocStackUses) {
599
+ B.setInsertionPoint (use->getNextInstruction ());
600
+ B.createDeallocStack (loc, contextBuffer);
601
+ }
602
+ // For a full application, we're done immediately after the call.
603
+ // If the apply site is a terminator, dealloc in all the successor
604
+ // blocks.
605
+ } else if (auto term = dyn_cast<TermInst>(site.getInstruction ())) {
606
+ for (auto successor : term->getSuccessorBlocks ()) {
607
+ B.setInsertionPoint (successor->begin ());
608
+ B.createDeallocStack (loc, contextBuffer);
609
+ }
610
+ // If the apply site is a normal instruction, dealloc after it.
611
+ } else {
612
+ B.setInsertionPoint (site.getInstruction ()->getNextInstruction ());
552
613
B.createDeallocStack (loc, contextBuffer);
553
614
}
615
+ // Continue emitting code to populate the context.
554
616
B.setInsertionPoint (contextAlloc->getNextInstruction ());
555
617
} else {
556
618
contextBuffer = B.createAllocBox (loc,
@@ -562,9 +624,15 @@ void PartialApplySimplificationPass::processKnownCallee(SILFunction *callee,
562
624
}
563
625
564
626
// Transfer the formerly partially-applied arguments into the box.
565
- auto appliedArgs = pa->getArguments ();
566
- for (unsigned i = 0 ; i < appliedArgs.size (); ++i) {
567
- auto arg = appliedArgs[i];
627
+ SmallVector<SILValue, 4 > newArgs;
628
+ // Carry over non-partial-applied arguments, if any.
629
+ auto appliedArgs = site.getArguments ();
630
+ auto paArgsOffset = appliedArgs.size () - boxFields.size ();
631
+ for (unsigned i = 0 ; i < paArgsOffset; ++i) {
632
+ newArgs.push_back (appliedArgs[i]);
633
+ }
634
+ for (unsigned i = 0 ; i < boxFields.size (); ++i) {
635
+ auto arg = appliedArgs[i + paArgsOffset];
568
636
SILValue proj = contextProj;
569
637
if (boxFields.size () > 1 ) {
570
638
proj = B.createTupleElementAddr (loc, proj, i);
@@ -604,23 +672,58 @@ void PartialApplySimplificationPass::processKnownCallee(SILFunction *callee,
604
672
}
605
673
}
606
674
607
- // Partially apply the new box to create the closure.
608
- auto paConvention = isNoEscape ? ParameterConvention::Direct_Guaranteed
609
- : contextParam.getConvention ();
610
- auto paOnStack = isNoEscape ? PartialApplyInst::OnStack
611
- : PartialApplyInst::NotOnStack;
612
- auto newPA = B.createPartialApply (loc, newFunctionRef,
613
- pa->getSubstitutionMap (),
614
- contextBuffer,
615
- paConvention,
616
- paOnStack);
617
- assert (isSimplePartialApply (newPA)
618
- && " partial apply wasn't simple after transformation?" );
619
- pa->replaceAllUsesWith (newPA);
620
- pa->eraseFromParent ();
675
+ // Transform the application to use the context instead of the original
676
+ // arguments.
677
+ newArgs.push_back (contextBuffer);
678
+ SILInstruction *newInst;
679
+ switch (site.getKind ()) {
680
+ case ApplySiteKind::PartialApplyInst: {
681
+ auto paConvention = isNoEscape ? ParameterConvention::Direct_Guaranteed
682
+ : contextParam.getConvention ();
683
+ auto paOnStack = isNoEscape ? PartialApplyInst::OnStack
684
+ : PartialApplyInst::NotOnStack;
685
+ auto newPA = B.createPartialApply (loc, newFunctionRef,
686
+ site.getSubstitutionMap (),
687
+ newArgs,
688
+ paConvention,
689
+ paOnStack);
690
+ assert (isSimplePartialApply (newPA)
691
+ && " partial apply wasn't simple after transformation?" );
692
+ newInst = newPA;
693
+ break ;
694
+ }
695
+ case ApplySiteKind::ApplyInst:
696
+ newInst = B.createApply (loc, newFunctionRef,
697
+ site.getSubstitutionMap (), newArgs);
698
+ break ;
699
+ case ApplySiteKind::BeginApplyInst:
700
+ newInst = B.createBeginApply (loc, newFunctionRef,
701
+ site.getSubstitutionMap (), newArgs);
702
+ break ;
703
+ case ApplySiteKind::TryApplyInst: {
704
+ auto tai = cast<TryApplyInst>(site.getInstruction ());
705
+ newInst = B.createTryApply (loc, newFunctionRef,
706
+ site.getSubstitutionMap (), newArgs,
707
+ tai->getNormalBB (),
708
+ tai->getErrorBB ());
709
+ break ;
710
+ }
711
+ }
712
+ site.getInstruction ()->replaceAllUsesPairwiseWith (newInst);
713
+ site.getInstruction ()->eraseFromParent ();
714
+ };
715
+
716
+ for (auto paSite : pa.PartialApplications ) {
717
+ rewriteApplySite (paSite);
718
+ }
719
+
720
+ // Rewrite full application sites to package up the partially applied
721
+ // arguments as well.
722
+ for (auto fa : pa.FullApplications ) {
723
+ rewriteApplySite (fa);
621
724
}
622
725
623
- // Once all the partial applications have been rewritten, then the original
726
+ // Once all the applications have been rewritten, then the original
624
727
// function refs with the old function type should all be unused. Delete
625
728
// them, since they are no longer valid.
626
729
for (auto fr : pa.FunctionRefs ) {
0 commit comments