@@ -118,6 +118,89 @@ static void findReachableExitBlocks(SILInstruction *i,
118
118
}
119
119
}
120
120
121
+ // / We use this to ensure that we properly handle recursive cases by revisiting
122
+ // / phi nodes that we are tracking. This just makes it easier to reproduce in a
123
+ // / test case.
124
+ static llvm::cl::opt<bool > ReverseInitialWorklist (
125
+ " sil-closure-lifetime-fixup-reverse-phi-order" , llvm::cl::init(false ),
126
+ llvm::cl::desc(
127
+ " Reverse the order in which we visit phis for testing purposes" ),
128
+ llvm::cl::Hidden);
129
+
130
+ // Finally, we need to prune phis inserted by the SSA updater that
131
+ // only take the .none from the entry block. This means that they are
132
+ // not actually reachable from the .some() so we know that we do not
133
+ // need to lifetime extend there at all. As an additional benefit, we
134
+ // eliminate the need to balance these arguments to satisfy the
135
+ // ownership verifier. This occurs since arguments are a place in SIL
136
+ // where the trivialness of an enums case is erased.
137
+ static void
138
+ cleanupDeadTrivialPhiArgs (SILValue initialValue,
139
+ SmallVectorImpl<SILPhiArgument *> &insertedPhis) {
140
+ // Just for testing purposes.
141
+ if (ReverseInitialWorklist) {
142
+ std::reverse (insertedPhis.begin (), insertedPhis.end ());
143
+ }
144
+ SmallVector<SILPhiArgument *, 8 > worklist (insertedPhis.begin (),
145
+ insertedPhis.end ());
146
+ sortUnique (insertedPhis);
147
+ SmallVector<SILValue, 8 > incomingValues;
148
+
149
+ while (!worklist.empty ()) {
150
+ // Clear the incoming values array after each iteration.
151
+ SWIFT_DEFER { incomingValues.clear (); };
152
+
153
+ auto *phi = worklist.pop_back_val ();
154
+ {
155
+ auto it = lower_bound (insertedPhis, phi);
156
+ if (it == insertedPhis.end () || *it != phi)
157
+ continue ;
158
+ }
159
+
160
+ // TODO: When we split true phi arguments from transformational terminators,
161
+ // this will always succeed and the assert can go away.
162
+ bool foundPhiValues = phi->getIncomingPhiValues (incomingValues);
163
+ (void )foundPhiValues;
164
+ assert (foundPhiValues && " Should always have 'true' phi arguments since "
165
+ " these were inserted by the SSA updater." );
166
+ if (llvm::any_of (incomingValues,
167
+ [&](SILValue v) { return v != initialValue; }))
168
+ continue ;
169
+
170
+ // Remove it from our insertedPhis list to prevent us from re-visiting this.
171
+ {
172
+ auto it = lower_bound (insertedPhis, phi);
173
+ assert ((it != insertedPhis.end () && *it == phi) &&
174
+ " Should have found the phi" );
175
+ insertedPhis.erase (it);
176
+ }
177
+
178
+ // See if any of our users are branch or cond_br. If so, we may have
179
+ // exposed additional unneeded phis. Add it back to the worklist in such a
180
+ // case.
181
+ for (auto *op : phi->getUses ()) {
182
+ auto *user = op->getUser ();
183
+
184
+ if (!isa<BranchInst>(user) && !isa<CondBranchInst>(user))
185
+ continue ;
186
+
187
+ auto *termInst = cast<TermInst>(user);
188
+ for (auto succBlockArgList : termInst->getSuccessorBlockArguments ()) {
189
+ copy_if (succBlockArgList, std::back_inserter (worklist),
190
+ [&](SILPhiArgument *succArg) -> bool {
191
+ auto it = lower_bound (insertedPhis, succArg);
192
+ return it != insertedPhis.end () && *it == succArg;
193
+ });
194
+ }
195
+ }
196
+
197
+ // Then RAUW the phi with the entryBlockOptionalNone and erase the
198
+ // argument.
199
+ phi->replaceAllUsesWith (initialValue);
200
+ erasePhiArgument (phi->getParent (), phi->getIndex ());
201
+ }
202
+ }
203
+
121
204
// / Extend the lifetime of the convert_escape_to_noescape's operand to the end
122
205
// / of the function.
123
206
// /
@@ -157,7 +240,8 @@ static void extendLifetimeToEndOfFunction(SILFunction &fn,
157
240
// Ok. At this point we know that Cvt is not in the entry block... so we can
158
241
// use SILSSAUpdater::GetValueInMiddleOfBlock() to extend the object's
159
242
// lifetime respecting loops.
160
- SILSSAUpdater updater;
243
+ SmallVector<SILPhiArgument *, 8 > insertedPhis;
244
+ SILSSAUpdater updater (&insertedPhis);
161
245
updater.Initialize (optionalEscapingClosureTy);
162
246
163
247
// Create an Optional<() -> ()>.none in the entry block of the function and
@@ -166,10 +250,11 @@ static void extendLifetimeToEndOfFunction(SILFunction &fn,
166
250
// Since we know that Cvt is not in the entry block and this must be, we know
167
251
// that it is safe to use the SSAUpdater's getValueInMiddleOfBlock with this
168
252
// value.
169
- updater. AddAvailableValue (fn. getEntryBlock (), [&]() -> SILValue {
253
+ SILValue entryBlockOptionalNone = [&]() -> SILValue {
170
254
SILBuilderWithScope b (fn.getEntryBlock ()->begin ());
171
255
return b.createOptionalNone (loc, optionalEscapingClosureTy);
172
- }());
256
+ }();
257
+ updater.AddAvailableValue (fn.getEntryBlock (), entryBlockOptionalNone);
173
258
174
259
// Create a copy of the convert_escape_to_no_escape and add it as an available
175
260
// value to the SSA updater.
@@ -206,6 +291,14 @@ static void extendLifetimeToEndOfFunction(SILFunction &fn,
206
291
SILValue v = updater.GetValueAtEndOfBlock (block);
207
292
SILBuilderWithScope (safeDestructionPt).createDestroyValue (loc, v);
208
293
}
294
+
295
+ // Finally, we need to prune phis inserted by the SSA updater that only take
296
+ // the .none from the entry block.
297
+ //
298
+ // TODO: Should we sort inserted phis before or after we initialize
299
+ // the worklist or maybe backwards? We should investigate how the
300
+ // SSA updater adds phi nodes to this list to resolve this question.
301
+ cleanupDeadTrivialPhiArgs (entryBlockOptionalNone, insertedPhis);
209
302
}
210
303
211
304
static SILInstruction *lookThroughRebastractionUsers (
@@ -724,16 +817,19 @@ static bool fixupCopyBlockWithoutEscaping(CopyBlockWithoutEscapingInst *cb,
724
817
auto optionalEscapingClosureTy =
725
818
SILType::getOptionalType (sentinelClosure->getType ());
726
819
727
- SILSSAUpdater updater;
820
+ SmallVector<SILPhiArgument *, 8 > insertedPhis;
821
+ SILSSAUpdater updater (&insertedPhis);
728
822
updater.Initialize (optionalEscapingClosureTy);
729
823
730
824
// Create the Optional.none as the beginning available value.
825
+ SILValue entryBlockOptionalNone;
731
826
{
732
827
SILBuilderWithScope b (fn.getEntryBlock ()->begin ());
733
- updater. AddAvailableValue (
734
- fn. getEntryBlock (),
735
- b. createOptionalNone (autoGenLoc, optionalEscapingClosureTy) );
828
+ entryBlockOptionalNone =
829
+ b. createOptionalNone (autoGenLoc, optionalEscapingClosureTy);
830
+ updater. AddAvailableValue (fn. getEntryBlock (), entryBlockOptionalNone );
736
831
}
832
+ assert (entryBlockOptionalNone);
737
833
738
834
// Then create the Optional.some(closure sentinel).
739
835
//
@@ -790,6 +886,14 @@ static bool fixupCopyBlockWithoutEscaping(CopyBlockWithoutEscapingInst *cb,
790
886
}
791
887
}
792
888
889
+ // Finally, we need to prune phis inserted by the SSA updater that only take
890
+ // the .none from the entry block.
891
+ //
892
+ // TODO: Should we sort inserted phis before or after we initialize
893
+ // the worklist or maybe backwards? We should investigate how the
894
+ // SSA updater adds phi nodes to this list to resolve this question.
895
+ cleanupDeadTrivialPhiArgs (entryBlockOptionalNone, insertedPhis);
896
+
793
897
return true ;
794
898
}
795
899
0 commit comments