@@ -49,7 +49,7 @@ static void diagnose(ASTContext &Context, SourceLoc loc, Diag<T...> diag,
49
49
// / and that the function implementing the closure consumes its capture
50
50
// / arguments.
51
51
static void fixupReferenceCounts (
52
- SILBasicBlock::iterator I, SILValue CalleeValue,
52
+ SILInstruction * I, SILValue CalleeValue,
53
53
SmallVectorImpl<std::pair<SILValue, ParameterConvention>> &CaptureArgs,
54
54
bool isCalleeGuaranteed) {
55
55
// Add a copy of each non-address type capture argument to lifetime extend the
@@ -60,7 +60,7 @@ static void fixupReferenceCounts(
60
60
if (!CaptureArg.first ->getType ().isAddress () &&
61
61
CaptureArg.second != ParameterConvention::Direct_Guaranteed &&
62
62
CaptureArg.second != ParameterConvention::Direct_Unowned) {
63
- createIncrementBefore (CaptureArg.first , &* I);
63
+ createIncrementBefore (CaptureArg.first , I);
64
64
} else {
65
65
// FIXME: What about indirectly owned parameters? The invocation of the
66
66
// closure would perform an indirect copy which we should mimick here.
@@ -190,35 +190,31 @@ namespace {
190
190
class ClosureCleanup {
191
191
using DeadInstSet = SmallBlotSetVector<SILInstruction *, 4 >;
192
192
193
- // / A helper class to update an instruction iterator if
194
- // / removal of instructions would invalidate it.
193
+ // / A helper class to update the set of dead instructions.
195
194
// /
196
195
// / Since this is called by the SILModule callback, the instruction may longer
197
196
// / be well-formed. Do not visit its operands. However, it's position in the
198
197
// / basic block is still valid.
199
198
// /
200
- // / FIXME: Using the Module's callback mechanism is undesirable. Instead,
201
- // / cleanupCalleeValue could be easily rewritten to use its own instruction
202
- // / deletion helper and pass a callback to tryDeleteDeadClosure and
203
- // / recursivelyDeleteTriviallyDeadInstructions.
204
- class IteratorUpdateHandler : public DeleteNotificationHandler {
199
+ // / FIXME: Using the Module's callback mechanism for this is terrible.
200
+ // / Instead, cleanupCalleeValue could be easily rewritten to use its own
201
+ // / instruction deletion helper and pass a callback to tryDeleteDeadClosure
202
+ // / and recursivelyDeleteTriviallyDeadInstructions.
203
+ class DeleteUpdateHandler : public DeleteNotificationHandler {
205
204
SILModule &Module;
206
- SILBasicBlock::iterator CurrentI;
207
205
DeadInstSet &DeadInsts;
208
206
209
207
public:
210
- IteratorUpdateHandler (SILBasicBlock::iterator I , DeadInstSet &DeadInsts)
211
- : Module(I-> getModule ()), CurrentI(I ), DeadInsts(DeadInsts) {
208
+ DeleteUpdateHandler (SILModule &M , DeadInstSet &DeadInsts)
209
+ : Module(M ), DeadInsts(DeadInsts) {
212
210
Module.registerDeleteNotificationHandler (this );
213
211
}
214
212
215
- ~IteratorUpdateHandler () override {
213
+ ~DeleteUpdateHandler () override {
216
214
// Unregister the handler.
217
215
Module.removeDeleteNotificationHandler (this );
218
216
}
219
217
220
- SILBasicBlock::iterator getIterator () const { return CurrentI; }
221
-
222
218
// Handling of instruction removal notifications.
223
219
bool needsNotifications () override { return true ; }
224
220
@@ -229,9 +225,6 @@ class ClosureCleanup {
229
225
return ;
230
226
231
227
DeadInsts.erase (deletedI);
232
-
233
- if (CurrentI == SILBasicBlock::iterator (deletedI))
234
- ++CurrentI;
235
228
}
236
229
};
237
230
@@ -260,16 +253,15 @@ class ClosureCleanup {
260
253
// Note: instructions in the `deadFunctionVals` set may use each other, so the
261
254
// set needs to continue to be updated (by this handler) when deleting
262
255
// instructions. This assumes that DeadFunctionValSet::erase() is stable.
263
- SILBasicBlock::iterator cleanupDeadClosures (SILBasicBlock::iterator II ) {
264
- IteratorUpdateHandler iteratorUpdate (II , deadFunctionVals);
256
+ void cleanupDeadClosures (SILFunction *F ) {
257
+ DeleteUpdateHandler deleteUpdate (F-> getModule () , deadFunctionVals);
265
258
for (Optional<SILInstruction *> I : deadFunctionVals) {
266
259
if (!I.hasValue ())
267
260
continue ;
268
261
269
262
if (auto *SVI = dyn_cast<SingleValueInstruction>(I.getValue ()))
270
263
cleanupCalleeValue (SVI);
271
264
}
272
- return iteratorUpdate.getIterator ();
273
265
}
274
266
};
275
267
} // end of namespace
@@ -472,21 +464,21 @@ static SILFunction *getCalleeFunction(
472
464
return CalleeFunction;
473
465
}
474
466
475
- static std::tuple<FullApplySite, SILBasicBlock::iterator>
476
- tryDevirtualizeApplyHelper (FullApplySite InnerAI, SILBasicBlock::iterator I,
477
- ClassHierarchyAnalysis *CHA) {
467
+ static SILInstruction *tryDevirtualizeApplyHelper (FullApplySite InnerAI,
468
+ ClassHierarchyAnalysis *CHA) {
478
469
auto NewInst = tryDevirtualizeApply (InnerAI, CHA);
479
- if (!NewInst) {
480
- return std::make_tuple (InnerAI, I);
481
- }
470
+ if (!NewInst)
471
+ return InnerAI.getInstruction ();
482
472
483
473
deleteDevirtualizedApply (InnerAI);
484
474
475
+ // FIXME: Comments at the use of this helper indicate that devirtualization
476
+ // may return SILArgument. Yet here we assert that it must return an
477
+ // instruction.
485
478
auto newApplyAI = NewInst.getInstruction ();
486
479
assert (newApplyAI && " devirtualized but removed apply site?" );
487
480
488
- return std::make_tuple (FullApplySite::isa (newApplyAI),
489
- newApplyAI->getIterator ());
481
+ return newApplyAI;
490
482
}
491
483
492
484
// / \brief Inlines all mandatory inlined functions into the body of a function,
@@ -532,23 +524,34 @@ runOnFunctionRecursively(SILOptFunctionBuilder &FuncBuilder,
532
524
SmallVector<std::pair<SILValue, ParameterConvention>, 16 > CaptureArgs;
533
525
SmallVector<SILValue, 32 > FullArgs;
534
526
535
- for (auto BI = F->begin (), BE = F->end (); BI != BE; ++BI) {
536
- // While iterating over this block, instructions are inserted and deleted.
537
- for (auto II = BI->begin (), nextI = II; II != BI->end (); II = nextI) {
538
- nextI = std::next (II);
527
+ // Visiting blocks in reverse order avoids revisiting instructions after block
528
+ // splitting, which would be quadratic.
529
+ for (auto BI = F->rbegin (), BE = F->rend (), nextBB = BI; BI != BE;
530
+ BI = nextBB) {
531
+ // After inlining, the block iterator will be adjusted to point to the last
532
+ // block containing inlined instructions. This way, the inlined function
533
+ // body will be reprocessed within the caller's context without revisiting
534
+ // any original instructions.
535
+ nextBB = std::next (BI);
539
536
537
+ // While iterating over this block, instructions are inserted and deleted.
538
+ // To avoid quadratic block splitting, instructions must be processed in
539
+ // reverse order (block splitting reassigned the parent pointer of all
540
+ // instructions below the split point).
541
+ for (auto II = BI->rbegin (); II != BI->rend (); ++II) {
540
542
FullApplySite InnerAI = FullApplySite::isa (&*II);
541
-
542
543
if (!InnerAI)
543
544
continue ;
544
545
545
- auto *ApplyBlock = InnerAI.getParent ();
546
-
547
- // *NOTE* If devirtualization succeeds, sometimes II will not be InnerAI,
546
+ // *NOTE* If devirtualization succeeds, devirtInst may not be InnerAI,
548
547
// but a casted result of InnerAI or even a block argument due to
549
- // abstraction changes when calling the witness or class method. We still
550
- // know that InnerAI dominates II though.
551
- std::tie (InnerAI, II) = tryDevirtualizeApplyHelper (InnerAI, II, CHA);
548
+ // abstraction changes when calling the witness or class method.
549
+ auto *devirtInst = tryDevirtualizeApplyHelper (InnerAI, CHA);
550
+ // Restore II to the current apply site.
551
+ II = devirtInst->getReverseIterator ();
552
+ // If the devirtualized call result is no longer a invalid FullApplySite,
553
+ // then it has succeeded, but the result is not immediately inlinable.
554
+ InnerAI = FullApplySite::isa (devirtInst);
552
555
if (!InnerAI)
553
556
continue ;
554
557
@@ -597,13 +600,8 @@ runOnFunctionRecursively(SILOptFunctionBuilder &FuncBuilder,
597
600
598
601
SILInliner Inliner (FuncBuilder, SILInliner::InlineKind::MandatoryInline,
599
602
Subs, OpenedArchetypesTracker);
600
- if (!Inliner.canInlineApplySite (InnerAI)) {
601
- // See comment above about casting when devirtualizing and how this
602
- // sometimes causes II and InnerAI to be different and even in different
603
- // blocks.
604
- II = InnerAI.getInstruction ()->getIterator ();
603
+ if (!Inliner.canInlineApplySite (InnerAI))
605
604
continue ;
606
- }
607
605
608
606
// Inline function at I, which also changes I to refer to the first
609
607
// instruction inlined in the case that it succeeds. We purposely
@@ -620,7 +618,8 @@ runOnFunctionRecursively(SILOptFunctionBuilder &FuncBuilder,
620
618
bool IsCalleeGuaranteed =
621
619
PAI &&
622
620
PAI->getType ().castTo <SILFunctionType>()->isCalleeGuaranteed ();
623
- fixupReferenceCounts (II, CalleeValue, CaptureArgs, IsCalleeGuaranteed);
621
+ fixupReferenceCounts (InnerAI.getInstruction (), CalleeValue, CaptureArgs,
622
+ IsCalleeGuaranteed);
624
623
}
625
624
626
625
// Register a callback to record potentially unused function values after
@@ -632,20 +631,23 @@ runOnFunctionRecursively(SILOptFunctionBuilder &FuncBuilder,
632
631
633
632
// Inlining deletes the apply, and can introduce multiple new basic
634
633
// blocks. After this, CalleeValue and other instructions may be invalid.
635
- nextI = Inliner.inlineFunction (CalleeFunction, InnerAI, FullArgs);
634
+ // nextBB will point to the last inlined block
635
+ auto firstInlinedInstAndLastBB =
636
+ Inliner.inlineFunction (CalleeFunction, InnerAI, FullArgs);
637
+ nextBB = firstInlinedInstAndLastBB.second ->getReverseIterator ();
636
638
++NumMandatoryInlines;
637
639
638
640
// The IR is now valid, and trivial dead arguments are removed. However,
639
641
// we may be able to remove dead callee computations (e.g. dead
640
642
// partial_apply closures).
641
- nextI = closureCleanup.cleanupDeadClosures (nextI );
643
+ closureCleanup.cleanupDeadClosures (F );
642
644
643
- assert (nextI == ApplyBlock->end ()
644
- || nextI->getParent () == ApplyBlock
645
- && " Mismatch between the instruction and basic block" );
645
+ // Resume inlining within nextBB, which contains only the inlined
646
+ // instructions and possibly instructions in the original call block that
647
+ // have not yet been visited.
648
+ break ;
646
649
}
647
650
}
648
-
649
651
// Keep track of full inlined functions so we don't waste time recursively
650
652
// reprocessing them.
651
653
FullyInlinedSet.insert (F);
@@ -680,6 +682,9 @@ class MandatoryInlining : public SILModuleTransform {
680
682
runOnFunctionRecursively (FuncBuilder, &F,
681
683
FullApplySite (), FullyInlinedSet, SetFactory,
682
684
SetFactory.getEmptySet (), CHA);
685
+ // The inliner splits blocks at call sites. Re-merge trivial branches
686
+ // to reestablish a canonical CFG.
687
+ mergeBasicBlocks (&F);
683
688
}
684
689
685
690
if (!ShouldCleanup)
0 commit comments