21
21
#include " swift/SILOptimizer/PassManager/Transforms.h"
22
22
#include " swift/SILOptimizer/Utils/CFG.h"
23
23
#include " swift/SILOptimizer/Utils/Local.h"
24
+ #include " swift/SILOptimizer/Utils/SILSSAUpdater.h"
24
25
#include " swift/SILOptimizer/Utils/StackNesting.h"
25
26
26
27
#include " llvm/Support/CommandLine.h"
@@ -88,8 +89,33 @@ static SILInstruction *getDeinitSafeClosureDestructionPoint(TermInst *Term) {
88
89
return Term;
89
90
}
90
91
92
+ static void findReachableExitBlocks (SILInstruction *i,
93
+ SmallVectorImpl<SILBasicBlock *> &result) {
94
+ SmallVector<SILBasicBlock *, 32 > worklist;
95
+ SmallPtrSet<SILBasicBlock *, 8 > visitedBlocks;
96
+
97
+ visitedBlocks.insert (i->getParent ());
98
+ worklist.push_back (i->getParent ());
99
+
100
+ while (!worklist.empty ()) {
101
+ auto *bb = worklist.pop_back_val ();
102
+ if (bb->getTerminator ()->isFunctionExiting ()) {
103
+ result.push_back (bb);
104
+ continue ;
105
+ }
106
+ copy_if (bb->getSuccessorBlocks (), std::back_inserter (worklist),
107
+ [&](SILBasicBlock *bb) { return visitedBlocks.insert (bb).second ; });
108
+ }
109
+ }
110
+
91
111
// / Extend the lifetime of the convert_escape_to_noescape's operand to the end
92
112
// / of the function.
113
+ // /
114
+ // / NOTE: Since we are lifetime extending a copy that we have introduced, we do
115
+ // / not need to consider destroy_value emitted by SILGen unlike
116
+ // / copy_block_without_escaping which consumes its sentinel parameter. Unlike
117
+ // / that case where we have to consider that destroy_value, we have a simpler
118
+ // / time here.
93
119
static void extendLifetimeToEndOfFunction (SILFunction &Fn,
94
120
ConvertEscapeToNoEscapeInst *Cvt) {
95
121
auto EscapingClosure = Cvt->getOperand ();
@@ -104,40 +130,78 @@ static void extendLifetimeToEndOfFunction(SILFunction &Fn,
104
130
Cvt->eraseFromParent ();
105
131
Cvt = NewCvt;
106
132
107
- // Create an alloc_stack Optional<() -> ()> at the beginning of the function.
108
- AllocStackInst *Slot;
109
- auto &Context = Cvt->getModule ().getASTContext ();
110
- {
111
- SILBuilderWithScope B (Fn.getEntryBlock ()->begin ());
112
- Slot = B.createAllocStack (loc, OptionalEscapingClosureTy);
113
- auto *NoneDecl = Context.getOptionalNoneDecl ();
114
- // Store None to it.
115
- B.createStore (
116
- loc, B.createEnum (loc, SILValue (), NoneDecl, OptionalEscapingClosureTy),
117
- Slot, StoreOwnershipQualifier::Init);
118
- }
119
- // Insert a copy before the convert_escape_to_noescape and store it to the
120
- // alloc_stack location.
121
- {
133
+ // If our Cvt is in the initial block, we do not need to use the SSA updater
134
+ // since we know Cvt can not be in a loop and must dominate all exits
135
+ // (*). Just insert a copy of the escaping closure at the Cvt and destroys at
136
+ // the exit blocks of the function.
137
+ //
138
+ // (*) In fact we can't use the SILSSAUpdater::GetValueInMiddleOfBlock.
139
+ if (Cvt->getParent () == Cvt->getFunction ()->getEntryBlock ()) {
122
140
SILBuilderWithScope B (Cvt);
123
- auto *SomeDecl = Context.getOptionalSomeDecl ();
124
- B.createDestroyAddr (loc, Slot);
125
- auto ClosureCopy = B.createCopyValue (loc, EscapingClosure);
126
- B.createStore (
127
- loc,
128
- B.createEnum (loc, ClosureCopy, SomeDecl, OptionalEscapingClosureTy),
129
- Slot, StoreOwnershipQualifier::Init);
141
+ CopyValueInst *InnerCVI = B.createCopyValue (loc, EscapingClosure);
142
+ SmallVector<SILBasicBlock *, 4 > ExitingBlocks;
143
+ Fn.findExitingBlocks (ExitingBlocks);
144
+
145
+ for (auto *Exit : ExitingBlocks) {
146
+ auto *Term = Exit->getTerminator ();
147
+ auto *SafeClosureDestructionPt =
148
+ getDeinitSafeClosureDestructionPoint (Term);
149
+ SILBuilderWithScope B (SafeClosureDestructionPt);
150
+ B.createDestroyValue (loc, InnerCVI);
151
+ }
152
+ return ;
130
153
}
131
- // Insert destroys at the function exits.
154
+
155
+ // Ok. At this point we know that Cvt is not in the entry block... so we can
156
+ // use SILSSAUpdater::GetValueInMiddleOfBlock() to extend the object's
157
+ // lifetime respecting loops.
158
+ SILSSAUpdater Updater (Cvt->getModule ());
159
+ Updater.Initialize (OptionalEscapingClosureTy);
160
+
161
+ // Create an Optional<() -> ()>.none in the entry block of the function and
162
+ // add it as an available value to the SSAUpdater.
163
+ //
164
+ // Since we know that Cvt is not in the entry block and this must be, we know
165
+ // that it is safe to use the SSAUpdater's getValueInMiddleOfBlock with this
166
+ // value.
167
+ Updater.AddAvailableValue (Fn.getEntryBlock (), [&]() -> SILValue {
168
+ SILBuilderWithScope B (Fn.getEntryBlock ()->begin ());
169
+ return B.createOptionalNone (loc, OptionalEscapingClosureTy);
170
+ }());
171
+
172
+ // Create a copy of the convert_escape_to_no_escape and add it as an available
173
+ // value to the SSA updater.
174
+ //
175
+ // NOTE: The SSAUpdater does not support providing multiple values in the same
176
+ // block without extra work. So the fact that Cvt is not in the entry block
177
+ // means that we don't have to worry about overwriting the .none value.
178
+ auto *CVI = [&]() -> CopyValueInst * {
179
+ SILBuilderWithScope B (Cvt);
180
+ CopyValueInst *InnerCVI = B.createCopyValue (loc, EscapingClosure);
181
+ Updater.AddAvailableValue (
182
+ Cvt->getParent (),
183
+ B.createOptionalSome (loc, InnerCVI, OptionalEscapingClosureTy));
184
+ return InnerCVI;
185
+ }();
186
+
187
+ // Then we use the SSA updater to insert a destroy_value before the cvt and at
188
+ // the reachable exit blocks.
132
189
SmallVector<SILBasicBlock *, 4 > ExitingBlocks;
133
- Fn.findExitingBlocks (ExitingBlocks);
190
+ findReachableExitBlocks (Cvt, ExitingBlocks);
191
+
192
+ {
193
+ // Before the copy value, insert an extra destroy_value to handle
194
+ // loops. Since we used our enum value this is safe.
195
+ SILBuilderWithScope B (CVI);
196
+ B.createDestroyValue (loc,
197
+ Updater.GetValueInMiddleOfBlock (CVI->getParent ()));
198
+ }
199
+
134
200
for (auto *Exit : ExitingBlocks) {
135
201
auto *Term = Exit->getTerminator ();
136
202
auto *SafeClosureDestructionPt = getDeinitSafeClosureDestructionPoint (Term);
137
203
SILBuilderWithScope B (SafeClosureDestructionPt);
138
- B.createDestroyAddr (loc, Slot);
139
- SILBuilderWithScope B2 (Term);
140
- B2.createDeallocStack (loc, Slot);
204
+ B.createDestroyValue (loc, Updater.GetValueAtEndOfBlock (Exit));
141
205
}
142
206
}
143
207
@@ -538,88 +602,171 @@ static SILInstruction *getOnlyDestroy(CopyBlockWithoutEscapingInst *CB) {
538
602
// / %e = is_escaping %closure
539
603
// / cond_fail %e
540
604
// / destroy_value %closure
541
- static bool fixupCopyBlockWithoutEscaping (CopyBlockWithoutEscapingInst *CB) {
542
- SmallVector<SILInstruction *, 4 > LifetimeEndPoints;
543
-
605
+ static bool fixupCopyBlockWithoutEscaping (CopyBlockWithoutEscapingInst *CB,
606
+ bool &modifiedCFG) {
544
607
// Find the end of the lifetime of the copy_block_without_escaping
545
608
// instruction.
546
609
auto &Fn = *CB->getFunction ();
610
+
611
+ // If we find a single destroy, this destroy is going to be a destroy that may
612
+ // be in the same block as CB. It is important that we make sure that the
613
+ // destroy is in a different block than CB or any terminating blocks to ensure
614
+ // that we can use the SSAUpdater if needed.
547
615
auto *SingleDestroy = getOnlyDestroy (CB);
548
- SmallVector<SILBasicBlock *, 4 > ExitingBlocks;
549
- Fn.findExitingBlocks (ExitingBlocks);
550
- if (SingleDestroy) {
551
- LifetimeEndPoints.push_back (
552
- &*std::next (SILBasicBlock::iterator (SingleDestroy)));
553
- } else {
554
- // Otherwise, conservatively insert verification at the end of the function.
555
- for (auto *Exit : ExitingBlocks)
556
- LifetimeEndPoints.push_back (Exit->getTerminator ());
616
+ if (SingleDestroy && SingleDestroy->getParent () == CB->getParent ()) {
617
+ modifiedCFG = true ;
618
+ {
619
+ SILBuilderWithScope B (SingleDestroy);
620
+ splitBasicBlockAndBranch (B, SingleDestroy, nullptr , nullptr );
621
+ }
622
+
623
+ {
624
+ SILBuilderWithScope B (SingleDestroy);
625
+ auto *Term = SingleDestroy->getParent ()->getTerminator ();
626
+ if (Term->isFunctionExiting ()) {
627
+ splitBasicBlockAndBranch (B, &*std::next (SingleDestroy->getIterator ()),
628
+ nullptr , nullptr );
629
+ }
630
+ }
557
631
}
558
632
559
633
auto SentinelClosure = CB->getClosure ();
560
634
auto Loc = CB->getLoc ();
561
635
636
+ // At this point, we transform our copy_block_without_escaping into a
637
+ // copy_block. This has a few important implications:
638
+ //
639
+ // 1. copy_block_without_escaping takes the sentinel value at +1. We will need
640
+ // to balance that +1.
641
+ // 2. The destroy_value associated with the copy_block_without_escaping will
642
+ // be on the copy_block value.
562
643
SILBuilderWithScope B (CB);
563
644
auto *NewCB = B.createCopyBlock (Loc, CB->getBlock ());
564
645
CB->replaceAllUsesWith (NewCB);
565
646
CB->eraseFromParent ();
566
647
567
- // Create an stack slot for the closure sentinel and store the sentinel (or
568
- // none on other paths.
569
648
auto generatedLoc = RegularLocation::getAutoGeneratedLocation ();
570
- AllocStackInst *Slot;
571
- auto &Context = NewCB->getModule ().getASTContext ();
649
+
650
+ // If CB is in the entry block, we know that our definition of SentinelClosure
651
+ // must be as well. Thus we know that we do not need to worry about loops or
652
+ // dominance issues and can just insert destroy_values for the sentinel at the
653
+ // lifetime end points.
654
+ if (NewCB->getParent () == NewCB->getFunction ()->getEntryBlock ()) {
655
+ // Our single destroy must not be in the entry block since if so, we would
656
+ // have inserted an edge to appease the SSA updater.
657
+ if (SingleDestroy) {
658
+ SILBuilderWithScope B (std::next (SingleDestroy->getIterator ()));
659
+ SILValue V = SentinelClosure;
660
+ SILValue isEscaping = B.createIsEscapingClosure (
661
+ Loc, V, IsEscapingClosureInst::ObjCEscaping);
662
+ B.createCondFail (Loc, isEscaping);
663
+ B.createDestroyValue (Loc, V);
664
+ return true ;
665
+ }
666
+
667
+ // If we couldn't find a specific destroy_value, lifetime extend to the end
668
+ // of the function.
669
+ SmallVector<SILBasicBlock *, 4 > ExitingBlocks;
670
+ Fn.findExitingBlocks (ExitingBlocks);
671
+ for (auto *Block : ExitingBlocks) {
672
+ SILBuilderWithScope B (Block->getTerminator ());
673
+ SILValue V = SentinelClosure;
674
+ SILValue isEscaping = B.createIsEscapingClosure (
675
+ Loc, V, IsEscapingClosureInst::ObjCEscaping);
676
+ B.createCondFail (Loc, isEscaping);
677
+ B.createDestroyValue (Loc, V);
678
+ }
679
+
680
+ return true ;
681
+ }
682
+
683
+ // Otherwise, we need to be more careful since we can have loops and may not
684
+ // transitively dominate all uses of the closure. So we:
685
+ //
686
+ // 1. Create an Optional<T>.none at the entry.
687
+ // 2. Create a destroy_value(val), val = Optional<T>.some(sentinel) in the cvt
688
+ // block.
689
+ // 3. Create a destroy_value at all exits of the value.
690
+ //
691
+ // and then use the SSAUpdater to ensure that we handle loops correctly.
572
692
auto OptionalEscapingClosureTy =
573
693
SILType::getOptionalType (SentinelClosure->getType ());
574
- auto *NoneDecl = Context.getOptionalNoneDecl ();
694
+
695
+ SILSSAUpdater Updater (Fn.getModule ());
696
+ Updater.Initialize (OptionalEscapingClosureTy);
697
+
698
+ // Create the Optional.none as the beginning available value.
575
699
{
576
700
SILBuilderWithScope B (Fn.getEntryBlock ()->begin ());
577
- Slot = B.createAllocStack (generatedLoc, OptionalEscapingClosureTy);
578
- // Store None to it.
579
- B.createStore (generatedLoc,
580
- B.createEnum (generatedLoc, SILValue (), NoneDecl,
581
- OptionalEscapingClosureTy),
582
- Slot, StoreOwnershipQualifier::Init);
701
+ Updater.AddAvailableValue (
702
+ Fn.getEntryBlock (),
703
+ B.createOptionalNone (generatedLoc, OptionalEscapingClosureTy));
583
704
}
584
- {
705
+
706
+ // Then create the Optional.some(closure sentinel).
707
+ //
708
+ // NOTE: We return the appropriate insertion point to insert the destroy_value
709
+ // before the value (to ensure we handle loops). We need to get all available
710
+ // values first though.
711
+ auto *InitialValue = [&]() -> EnumInst * {
585
712
SILBuilderWithScope B (NewCB);
586
- // Store the closure sentinel (the copy_block_without_escaping closure
713
+ // Create the closure sentinel (the copy_block_without_escaping closure
587
714
// operand consumed at +1, so we don't need a copy) to it.
588
- B.createDestroyAddr (generatedLoc, Slot); // We could be in a loop.
589
- auto *SomeDecl = Context.getOptionalSomeDecl ();
590
- B.createStore (generatedLoc,
591
- B.createEnum (generatedLoc, SentinelClosure, SomeDecl,
592
- OptionalEscapingClosureTy),
593
- Slot, StoreOwnershipQualifier::Init);
715
+ auto *Result = B.createOptionalSome (generatedLoc, SentinelClosure,
716
+ OptionalEscapingClosureTy);
717
+ Updater.AddAvailableValue (Result->getParent (), Result);
718
+ return Result;
719
+ }();
720
+
721
+ // If we had a single destroy, creating a .none after it and add that as a
722
+ // value to the SSA updater.
723
+ if (SingleDestroy) {
724
+ SILBuilderWithScope B (std::next (SingleDestroy->getIterator ()));
725
+ auto *Result =
726
+ B.createOptionalNone (generatedLoc, OptionalEscapingClosureTy);
727
+ Updater.AddAvailableValue (Result->getParent (), Result);
594
728
}
595
729
596
- for (auto LifetimeEndPoint : LifetimeEndPoints) {
597
- SILBuilderWithScope B (LifetimeEndPoint);
598
- SILValue isEscaping;
599
- B.emitScopedBorrowOperation (generatedLoc, Slot, [&](SILValue value) {
600
- isEscaping = B.createIsEscapingClosure (
601
- Loc, value, IsEscapingClosureInst::ObjCEscaping);
602
- });
730
+ // Now that we have all of our available values, insert a destroy_value before
731
+ // the initial Optional.some value using the SSA updater to ensure that we
732
+ // handle loops correctly.
733
+ {
734
+ SILBuilderWithScope B (InitialValue);
735
+ B.createDestroyValue (generatedLoc, Updater.GetValueInMiddleOfBlock (
736
+ InitialValue->getParent ()));
737
+ }
738
+
739
+ // And insert an is_escaping_closure, cond_fail, destroy_value at each of the
740
+ // lifetime end points. This ensures we do not expand our lifetime too much.
741
+ if (SingleDestroy) {
742
+ SILBuilderWithScope B (std::next (SingleDestroy->getIterator ()));
743
+ SILValue V = Updater.GetValueInMiddleOfBlock (SingleDestroy->getParent ());
744
+ SILValue isEscaping =
745
+ B.createIsEscapingClosure (Loc, V, IsEscapingClosureInst::ObjCEscaping);
603
746
B.createCondFail (Loc, isEscaping);
604
- B.createDestroyAddr (generatedLoc, Slot);
605
- // Store None to it.
606
- B.createStore (generatedLoc,
607
- B.createEnum (generatedLoc, SILValue (), NoneDecl,
608
- OptionalEscapingClosureTy),
609
- Slot, StoreOwnershipQualifier::Init);
747
+ B.createDestroyValue (Loc, V);
610
748
}
611
749
612
- // Insert the dealloc_stack in all exiting blocks.
613
- for (auto *ExitBlock: ExitingBlocks) {
614
- auto *Terminator = ExitBlock->getTerminator ();
615
- SILBuilderWithScope B (Terminator);
616
- B.createDeallocStack (generatedLoc, Slot);
750
+ // Then to be careful with regards to loops, insert at each of the destroy
751
+ // blocks destroy_value to ensure that we obey ownership invariants.
752
+ {
753
+ SmallVector<SILBasicBlock *, 4 > ExitingBlocks;
754
+ findReachableExitBlocks (NewCB, ExitingBlocks);
755
+
756
+ for (auto *Exit : ExitingBlocks) {
757
+ auto *Term = Exit->getTerminator ();
758
+ auto *SafeClosureDestructionPt =
759
+ getDeinitSafeClosureDestructionPoint (Term);
760
+ SILBuilderWithScope B (SafeClosureDestructionPt);
761
+ B.createDestroyValue (generatedLoc, Updater.GetValueInMiddleOfBlock (Exit));
762
+ }
617
763
}
618
764
619
765
return true ;
620
766
}
621
767
622
- static bool fixupClosureLifetimes (SILFunction &Fn, bool &checkStackNesting) {
768
+ static bool fixupClosureLifetimes (SILFunction &Fn, bool &checkStackNesting,
769
+ bool &modifiedCFG) {
623
770
bool Changed = false ;
624
771
625
772
// tryExtendLifetimeToLastUse uses a cache of recursive instruction use
@@ -634,7 +781,7 @@ static bool fixupClosureLifetimes(SILFunction &Fn, bool &checkStackNesting) {
634
781
635
782
// Handle, copy_block_without_escaping instructions.
636
783
if (auto *CB = dyn_cast<CopyBlockWithoutEscapingInst>(Inst)) {
637
- Changed |= fixupCopyBlockWithoutEscaping (CB);
784
+ Changed |= fixupCopyBlockWithoutEscaping (CB, modifiedCFG );
638
785
continue ;
639
786
}
640
787
@@ -688,7 +835,7 @@ class ClosureLifetimeFixup : public SILFunctionTransform {
688
835
bool checkStackNesting = false ;
689
836
bool modifiedCFG = false ;
690
837
691
- if (fixupClosureLifetimes (*getFunction (), checkStackNesting)) {
838
+ if (fixupClosureLifetimes (*getFunction (), checkStackNesting, modifiedCFG )) {
692
839
if (checkStackNesting){
693
840
StackNesting SN;
694
841
modifiedCFG =
0 commit comments