15
15
#include " swift/Basic/STLExtras.h"
16
16
#include " swift/SIL/BasicBlockUtils.h"
17
17
#include " swift/SIL/BranchPropagatedUser.h"
18
+ #include " swift/SIL/DebugUtils.h"
18
19
#include " swift/SIL/MemAccessUtils.h"
19
20
#include " swift/SIL/OwnershipUtils.h"
20
21
#include " swift/SIL/SILArgument.h"
@@ -148,9 +149,12 @@ struct SemanticARCOptVisitor
148
149
// / Our main worklist. We use this after an initial run through.
149
150
SmallBlotSetVector<SILValue, 32 > worklist;
150
151
151
- // / A secondary work list that we use to store dead trivial instructions to
152
- // / delete after we are done processing the worklist.
153
- SmallBlotSetVector<SILInstruction *, 32 > deadTrivialInsts;
152
+ // / A set of values that we have visited since the last mutation. We use this
153
+ // / to ensure that we do not visit values twice without mutating.
154
+ // /
155
+ // / This is specifically to ensure that we do not go into an infinite loop
156
+ // / when visiting phi nodes.
157
+ SmallBlotSetVector<SILValue, 16 > visitedSinceLastMutation;
154
158
155
159
SILFunction &F;
156
160
Optional<DeadEndBlocks> TheDeadEndBlocks;
@@ -167,8 +171,13 @@ struct SemanticARCOptVisitor
167
171
// / the worklist, and then call eraseInstruction on i.
168
172
void eraseAndRAUWSingleValueInstruction (SingleValueInstruction *i, SILValue newValue) {
169
173
worklist.insert (newValue);
174
+ for (auto *use : i->getUses ()) {
175
+ for (SILValue result : use->getUser ()->getResults ()) {
176
+ worklist.insert (result);
177
+ }
178
+ }
170
179
i->replaceAllUsesWith (newValue);
171
- eraseInstruction (i);
180
+ eraseInstructionAndAddOperandsToWorklist (i);
172
181
}
173
182
174
183
// / Add all operands of i to the worklist and then call eraseInstruction on
@@ -189,16 +198,86 @@ struct SemanticARCOptVisitor
189
198
// the instruction.
190
199
for (SILValue result : i->getResults ()) {
191
200
worklist.erase (result);
201
+ visitedSinceLastMutation.erase (result);
192
202
}
193
- deadTrivialInsts.erase (i);
194
203
i->eraseFromParent ();
204
+
205
+ // Add everything else from visitedSinceLastMutation to the worklist.
206
+ for (auto opt : visitedSinceLastMutation) {
207
+ if (!opt.hasValue ()) {
208
+ continue ;
209
+ }
210
+ worklist.insert (*opt);
211
+ }
212
+ visitedSinceLastMutation.clear ();
195
213
}
196
214
197
215
// / The default visitor.
198
- bool visitSILInstruction (SILInstruction *i) { return false ; }
216
+ bool visitSILInstruction (SILInstruction *i) {
217
+ assert (!isGuaranteedForwardingInst (i) &&
218
+ " Should have forwarding visitor for all ownership forwarding "
219
+ " instructions" );
220
+ return false ;
221
+ }
222
+
199
223
bool visitCopyValueInst (CopyValueInst *cvi);
200
224
bool visitBeginBorrowInst (BeginBorrowInst *bbi);
201
225
bool visitLoadInst (LoadInst *li);
226
+ static bool shouldVisitInst (SILInstruction *i) {
227
+ switch (i->getKind ()) {
228
+ default :
229
+ return false ;
230
+ case SILInstructionKind::CopyValueInst:
231
+ case SILInstructionKind::BeginBorrowInst:
232
+ case SILInstructionKind::LoadInst:
233
+ return true ;
234
+ }
235
+ }
236
+
237
+ #define FORWARDING_INST (NAME ) \
238
+ bool visit##NAME##Inst(NAME##Inst *cls) { \
239
+ for (SILValue v : cls->getResults ()) { \
240
+ worklist.insert (v); \
241
+ } \
242
+ return false ; \
243
+ }
244
+ FORWARDING_INST (Tuple)
245
+ FORWARDING_INST (Struct)
246
+ FORWARDING_INST (Enum)
247
+ FORWARDING_INST (OpenExistentialRef)
248
+ FORWARDING_INST (Upcast)
249
+ FORWARDING_INST (UncheckedRefCast)
250
+ FORWARDING_INST (ConvertFunction)
251
+ FORWARDING_INST (RefToBridgeObject)
252
+ FORWARDING_INST (BridgeObjectToRef)
253
+ FORWARDING_INST (UnconditionalCheckedCast)
254
+ FORWARDING_INST (UncheckedEnumData)
255
+ FORWARDING_INST (MarkUninitialized)
256
+ FORWARDING_INST (SelectEnum)
257
+ FORWARDING_INST (DestructureStruct)
258
+ FORWARDING_INST (DestructureTuple)
259
+ FORWARDING_INST (TupleExtract)
260
+ FORWARDING_INST (StructExtract)
261
+ FORWARDING_INST (OpenExistentialValue)
262
+ FORWARDING_INST (OpenExistentialBoxValue)
263
+ FORWARDING_INST (MarkDependence)
264
+ #undef FORWARDING_INST
265
+
266
+ #define FORWARDING_TERM (NAME ) \
267
+ bool visit##NAME##Inst(NAME##Inst *cls) { \
268
+ for (auto succValues : cls->getSuccessorBlockArguments ()) { \
269
+ for (SILValue v : succValues) { \
270
+ worklist.insert (v); \
271
+ } \
272
+ } \
273
+ return false ; \
274
+ }
275
+
276
+ FORWARDING_TERM (SwitchEnum)
277
+ FORWARDING_TERM (CheckedCastBranch)
278
+ FORWARDING_TERM (Branch)
279
+ FORWARDING_TERM (CondBranch)
280
+ #undef FORWARDING_TERM
202
281
203
282
bool isWrittenTo (LoadInst *li);
204
283
@@ -225,6 +304,14 @@ bool SemanticARCOptVisitor::processWorklist() {
225
304
if (!next)
226
305
continue ;
227
306
307
+ // First check if this is a value that we have visited since the last time
308
+ // we erased an instruction. If we have visited it, skip it. Every time we
309
+ // modify something, we should be deleting an instruction, so we have not
310
+ // found any further information.
311
+ if (!visitedSinceLastMutation.insert (next).second ) {
312
+ continue ;
313
+ }
314
+
228
315
// First check if this is an instruction that is trivially dead. This can
229
316
// occur if we eliminate rr traffic resulting in dead projections and the
230
317
// like.
@@ -234,7 +321,8 @@ bool SemanticARCOptVisitor::processWorklist() {
234
321
// the instruction).
235
322
if (auto *defInst = next->getDefiningInstruction ()) {
236
323
if (isInstructionTriviallyDead (defInst)) {
237
- deadTrivialInsts.insert (defInst);
324
+ deleteAllDebugUses (defInst);
325
+ eraseInstruction (defInst);
238
326
continue ;
239
327
}
240
328
}
@@ -247,23 +335,6 @@ bool SemanticARCOptVisitor::processWorklist() {
247
335
}
248
336
}
249
337
250
- // Then eliminate the rest of the dead trivial insts.
251
- //
252
- // NOTE: We do not need to touch the worklist here since it is guaranteed to
253
- // be empty due to the loop above. We enforce this programatically with the
254
- // assert.
255
- assert (worklist.empty () && " Expected drained worklist so we don't have to "
256
- " remove dead insts form it" );
257
- while (!deadTrivialInsts.empty ()) {
258
- auto val = deadTrivialInsts.pop_back_val ();
259
- if (!val)
260
- continue ;
261
- recursivelyDeleteTriviallyDeadInstructions (
262
- *val, true /* force*/ ,
263
- [&](SILInstruction *i) { deadTrivialInsts.erase (i); });
264
- madeChange = true ;
265
- }
266
-
267
338
return madeChange;
268
339
}
269
340
@@ -795,59 +866,24 @@ struct SemanticARCOpts : SILFunctionTransform {
795
866
" Can not perform semantic arc optimization unless ownership "
796
867
" verification is enabled" );
797
868
798
- // Iterate over all of the arguments, performing small peephole ARC
799
- // optimizations. We assume that the visitor will add any instructions we
800
- // need to recursively to the visitor's worklist. Also, note that we assume
801
- // that we do not look through /any/ sil block arguments here since our
802
- // iteration here is only valid up to SSA.
803
- bool madeChange = false ;
804
-
805
869
SemanticARCOptVisitor visitor (f);
806
-
807
- for (auto &bb : f) {
808
- auto ii = bb.rend ();
809
- auto start = bb.rbegin ();
810
-
811
- // If the bb is empty, continue.
812
- if (start == ii)
813
- continue ;
814
870
815
- // Go to the first instruction to process.
816
- --ii;
817
-
818
- // Then until we process the first instruction of the block...
819
- while (ii != start) {
820
- // Move the iterator before ii.
821
- auto tmp = std::next (ii);
822
-
823
- // Then try to optimize. If we succeeded, then we deleted
824
- // ii. Move ii from the next value back onto the instruction
825
- // after ii's old value in the block instruction list and then
826
- // process that.
827
- if (visitor.visit (&*ii)) {
828
- madeChange = true ;
829
- ii = std::prev (tmp);
830
- continue ;
871
+ // Add all the results of all instructions that we want to visit to the
872
+ // worklist.
873
+ for (auto &block : f) {
874
+ for (auto &inst : block) {
875
+ if (SemanticARCOptVisitor::shouldVisitInst (&inst)) {
876
+ for (SILValue v : inst.getResults ()) {
877
+ visitor.worklist .insert (v);
878
+ }
831
879
}
832
-
833
- // Otherwise, we didn't delete ii. Just visit the next instruction.
834
- --ii;
835
880
}
836
-
837
- // Finally visit the first instruction of the block.
838
- madeChange |= visitor.visit (&*ii);
839
881
}
840
882
841
- // Finally drain the worklist on the visitor and process until we reach the
842
- // fixpoint and thus do not have any further work to do.
843
- //
844
- // NOTE: At this point madeChange has already been set to true if we have
845
- // anything in the worklist, so technically we do not need to do this. But I
846
- // would rather represent this state to future proof the pass to be
847
- // "visually" correct.
848
- madeChange |= visitor.processWorklist ();
849
-
850
- if (madeChange) {
883
+ // Then process the worklist. We only destroy instructions, so invalidate
884
+ // that. Once we modify the ownership of block arguments, we will need to
885
+ // perhaps invalidate branches as well.
886
+ if (visitor.processWorklist ()) {
851
887
invalidateAnalysis (SILAnalysis::InvalidationKind::Instructions);
852
888
}
853
889
}
0 commit comments